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:
authorFabian Schempp <fabianschempp@googlemail.com>2022-04-18 23:28:08 +0300
committerFabian Schempp <fabianschempp@googlemail.com>2022-04-18 23:28:08 +0300
commit01616f9ed2da14b5cad40efac03d1a22dbb759f3 (patch)
tree4955ad12f260e557aa1c0f724a320e565f8bce5c
parentf06d361da1249c93568153bae88bcdf43b4774a1 (diff)
parent314b27850ccb6d103cf5c73855187cfc11ec48d8 (diff)
Merge branch 'master' into soc-2021-porting-modifiers-to-nodes-remesh-voxel
-rw-r--r--CMakeLists.txt2
-rwxr-xr-xbuild_files/build_environment/install_deps.sh19
-rw-r--r--build_files/cmake/platform/platform_apple.cmake2
-rw-r--r--extern/fast_float/LICENSE-MIT27
-rw-r--r--extern/fast_float/README.blender7
-rw-r--r--extern/fast_float/README.md218
-rw-r--r--extern/fast_float/fast_float.h2979
-rw-r--r--intern/cycles/blender/addon/properties.py2
-rw-r--r--intern/cycles/device/hip/util.h2
-rw-r--r--intern/cycles/device/metal/kernel.mm2
-rw-r--r--intern/cycles/hydra/camera.cpp57
-rw-r--r--intern/cycles/hydra/camera.h8
-rw-r--r--intern/cycles/hydra/geometry.inl6
-rw-r--r--intern/cycles/hydra/light.cpp11
-rw-r--r--intern/cycles/hydra/render_delegate.cpp26
-rw-r--r--intern/cycles/hydra/render_pass.cpp5
-rw-r--r--intern/cycles/hydra/session.h11
-rw-r--r--intern/cycles/integrator/pass_accessor_cpu.cpp4
-rw-r--r--intern/cycles/integrator/path_trace.cpp38
-rw-r--r--intern/cycles/integrator/path_trace_work_cpu.cpp8
-rw-r--r--intern/cycles/integrator/shader_eval.cpp2
-rw-r--r--intern/cycles/kernel/svm/blackbody.h2
-rw-r--r--intern/cycles/kernel/svm/closure.h3
-rw-r--r--intern/cycles/kernel/svm/math_util.h4
-rw-r--r--intern/cycles/kernel/types.h7
-rw-r--r--intern/cycles/kernel/util/color.h9
-rw-r--r--intern/cycles/scene/mesh.cpp12
-rw-r--r--intern/cycles/scene/shader.cpp39
-rw-r--r--intern/cycles/scene/shader.h5
-rw-r--r--intern/cycles/scene/shader_nodes.cpp4
-rw-r--r--intern/cycles/util/simd.h23
-rw-r--r--intern/cycles/util/tbb.h11
-rw-r--r--intern/cycles/util/transform.h15
-rw-r--r--intern/ghost/GHOST_C-api.h4
-rw-r--r--intern/ghost/GHOST_ISystem.h3
-rw-r--r--intern/ghost/GHOST_Types.h10
-rw-r--r--intern/ghost/intern/GHOST_C-api.cpp4
-rw-r--r--intern/ghost/intern/GHOST_System.cpp4
-rw-r--r--intern/ghost/intern/GHOST_System.h3
-rw-r--r--intern/ghost/intern/GHOST_SystemWin32.cpp71
-rw-r--r--intern/ghost/intern/GHOST_SystemWin32.h10
-rw-r--r--intern/ghost/intern/GHOST_WindowWin32.cpp2
-rw-r--r--intern/ghost/intern/GHOST_Wintab.cpp160
-rw-r--r--intern/ghost/intern/GHOST_Wintab.h28
-rw-r--r--intern/opencolorio/fallback_impl.cc9
-rw-r--r--intern/opencolorio/ocio_capi.cc6
-rw-r--r--intern/opencolorio/ocio_capi.h3
-rw-r--r--intern/opencolorio/ocio_impl.cc14
-rw-r--r--intern/opencolorio/ocio_impl.h9
-rw-r--r--intern/opencolorio/ocio_impl_glsl.cc2
-rw-r--r--intern/utfconv/utfconv.c2
-rw-r--r--release/datafiles/colormanagement/config.ocio2
-rw-r--r--release/datafiles/icons/brush.sculpt.displacement_smear.datbin4436 -> 3968 bytes
-rw-r--r--release/datafiles/icons/brush.sculpt.draw_sharp.datbin2492 -> 2492 bytes
-rw-r--r--release/datafiles/icons/brush.sculpt.multiplane_scrape.datbin1916 -> 2060 bytes
-rw-r--r--release/datafiles/icons/ops.curves.sculpt_grow_shrink.datbin1736 -> 1736 bytes
-rw-r--r--release/datafiles/icons/ops.curves.sculpt_snake_hook.datbin0 -> 1916 bytes
-rw-r--r--release/datafiles/icons/ops.sculpt.color_filter.datbin1700 -> 1700 bytes
-rw-r--r--release/datafiles/icons/ops.sculpt.mask_by_color.datbin3716 -> 3716 bytes
m---------release/datafiles/locale0
m---------release/scripts/addons0
-rw-r--r--release/scripts/modules/bl_i18n_utils/bl_extract_messages.py7
-rw-r--r--release/scripts/modules/rna_manual_reference.py362
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/blender_default.py14
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py6
-rw-r--r--release/scripts/startup/bl_operators/geometry_nodes.py30
-rw-r--r--release/scripts/startup/bl_operators/node.py6
-rw-r--r--release/scripts/startup/bl_ui/properties_data_mesh.py19
-rw-r--r--release/scripts/startup/bl_ui/space_nla.py24
-rw-r--r--release/scripts/startup/bl_ui/space_node.py3
-rw-r--r--release/scripts/startup/bl_ui/space_time.py8
-rw-r--r--release/scripts/startup/bl_ui/space_topbar.py59
-rw-r--r--release/scripts/startup/bl_ui/space_userpref.py6
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py9
-rw-r--r--source/blender/blenfont/BLF_api.h14
-rw-r--r--source/blender/blenfont/intern/blf.c36
-rw-r--r--source/blender/blenfont/intern/blf_font.c363
-rw-r--r--source/blender/blenfont/intern/blf_glyph.c84
-rw-r--r--source/blender/blenfont/intern/blf_internal.h16
-rw-r--r--source/blender/blenfont/intern/blf_internal_types.h92
-rw-r--r--source/blender/blenfont/intern/blf_thumbs.c4
-rw-r--r--source/blender/blenkernel/BKE_animsys.h13
-rw-r--r--source/blender/blenkernel/BKE_attribute.h2
-rw-r--r--source/blender/blenkernel/BKE_curve_to_mesh.hh8
-rw-r--r--source/blender/blenkernel/BKE_curves.hh45
-rw-r--r--source/blender/blenkernel/BKE_global.h5
-rw-r--r--source/blender/blenkernel/BKE_image.h16
-rw-r--r--source/blender/blenkernel/BKE_layer.h4
-rw-r--r--source/blender/blenkernel/BKE_mesh_tangent.h2
-rw-r--r--source/blender/blenkernel/BKE_object.h11
-rw-r--r--source/blender/blenkernel/BKE_paint.h22
-rw-r--r--source/blender/blenkernel/BKE_pbvh.h21
-rw-r--r--source/blender/blenkernel/BKE_pbvh_pixels.hh184
-rw-r--r--source/blender/blenkernel/CMakeLists.txt8
-rw-r--r--source/blender/blenkernel/intern/anim_path.c8
-rw-r--r--source/blender/blenkernel/intern/anim_sys.c924
-rw-r--r--source/blender/blenkernel/intern/armature_update.c10
-rw-r--r--source/blender/blenkernel/intern/attribute.c12
-rw-r--r--source/blender/blenkernel/intern/attribute_access.cc5
-rw-r--r--source/blender/blenkernel/intern/blendfile_link_append.c5
-rw-r--r--source/blender/blenkernel/intern/camera.c12
-rw-r--r--source/blender/blenkernel/intern/collection.c2
-rw-r--r--source/blender/blenkernel/intern/constraint.c8
-rw-r--r--source/blender/blenkernel/intern/curve.cc4
-rw-r--r--source/blender/blenkernel/intern/curve_bezier.cc5
-rw-r--r--source/blender/blenkernel/intern/curve_eval.cc7
-rw-r--r--source/blender/blenkernel/intern/curve_poly.cc20
-rw-r--r--source/blender/blenkernel/intern/curve_to_mesh_convert.cc1092
-rw-r--r--source/blender/blenkernel/intern/curves.cc47
-rw-r--r--source/blender/blenkernel/intern/curves_geometry.cc101
-rw-r--r--source/blender/blenkernel/intern/editmesh.c16
-rw-r--r--source/blender/blenkernel/intern/editmesh_cache.cc (renamed from source/blender/blenkernel/intern/editmesh_cache.c)38
-rw-r--r--source/blender/blenkernel/intern/geometry_component_curves.cc9
-rw-r--r--source/blender/blenkernel/intern/geometry_set_instances.cc7
-rw-r--r--source/blender/blenkernel/intern/gpencil_geom.cc69
-rw-r--r--source/blender/blenkernel/intern/image.cc25
-rw-r--r--source/blender/blenkernel/intern/lattice_deform_test.cc12
-rw-r--r--source/blender/blenkernel/intern/layer.c5
-rw-r--r--source/blender/blenkernel/intern/lib_remap.c19
-rw-r--r--source/blender/blenkernel/intern/material.c8
-rw-r--r--source/blender/blenkernel/intern/mesh.cc4
-rw-r--r--source/blender/blenkernel/intern/mesh_convert.cc9
-rw-r--r--source/blender/blenkernel/intern/mesh_remap.c2
-rw-r--r--source/blender/blenkernel/intern/mesh_remesh_voxel.cc145
-rw-r--r--source/blender/blenkernel/intern/mesh_runtime.cc (renamed from source/blender/blenkernel/intern/mesh_runtime.c)104
-rw-r--r--source/blender/blenkernel/intern/mesh_wrapper.cc (renamed from source/blender/blenkernel/intern/mesh_wrapper.c)136
-rw-r--r--source/blender/blenkernel/intern/nla.c7
-rw-r--r--source/blender/blenkernel/intern/node.cc2
-rw-r--r--source/blender/blenkernel/intern/object.cc55
-rw-r--r--source/blender/blenkernel/intern/object_dupli.cc2
-rw-r--r--source/blender/blenkernel/intern/paint.c24
-rw-r--r--source/blender/blenkernel/intern/paint_canvas.cc131
-rw-r--r--source/blender/blenkernel/intern/particle.c25
-rw-r--r--source/blender/blenkernel/intern/particle_child.c2
-rw-r--r--source/blender/blenkernel/intern/pbvh.c14
-rw-r--r--source/blender/blenkernel/intern/pbvh_intern.h37
-rw-r--r--source/blender/blenkernel/intern/pbvh_pixels.cc393
-rw-r--r--source/blender/blenkernel/intern/pointcache.c23
-rw-r--r--source/blender/blenkernel/intern/subsurf_ccg.c2
-rw-r--r--source/blender/blenkernel/intern/vfont.c8
-rw-r--r--source/blender/blenkernel/nla_private.h51
-rw-r--r--source/blender/blenlib/BLI_math_rotation.hh18
-rw-r--r--source/blender/blenlib/BLI_vector.hh10
-rw-r--r--source/blender/blenlib/CMakeLists.txt2
-rw-r--r--source/blender/blenlib/intern/math_base_inline.c9
-rw-r--r--source/blender/blenlib/intern/math_rotation.cc26
-rw-r--r--source/blender/blenlib/tests/BLI_math_rotation_test.cc22
-rw-r--r--source/blender/blenloader/intern/readfile.c2
-rw-r--r--source/blender/blenloader/intern/versioning_300.c12
-rw-r--r--source/blender/blenloader/intern/versioning_defaults.c4
-rw-r--r--source/blender/blenloader/intern/writefile.c11
-rw-r--r--source/blender/bmesh/operators/bmo_smooth_laplacian.c2
-rw-r--r--source/blender/compositor/intern/COM_NodeOperationBuilder.cc2
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.cc38
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.h3
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc2
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc2
-rw-r--r--source/blender/draw/CMakeLists.txt9
-rw-r--r--source/blender/draw/engines/eevee/eevee_data.c14
-rw-r--r--source/blender/draw/engines/eevee/eevee_engine.c8
-rw-r--r--source/blender/draw/engines/eevee/eevee_lightprobes.c5
-rw-r--r--source/blender/draw/engines/eevee/eevee_materials.c94
-rw-r--r--source/blender/draw/engines/eevee/eevee_motion_blur.c51
-rw-r--r--source/blender/draw/engines/eevee/eevee_private.h35
-rw-r--r--source/blender/draw/engines/eevee/eevee_render.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_renderpasses.c4
-rw-r--r--source/blender/draw/engines/eevee/eevee_shaders.c84
-rw-r--r--source/blender/draw/engines/eevee/eevee_shaders_extra.cc173
-rw-r--r--source/blender/draw/engines/eevee/eevee_volumes.c1
-rw-r--r--source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl44
-rw-r--r--source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl13
-rw-r--r--source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl12
-rw-r--r--source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl36
-rw-r--r--source/blender/draw/engines/eevee/shaders/closure_eval_refraction_lib.glsl14
-rw-r--r--source/blender/draw/engines/eevee/shaders/closure_eval_surface_lib.glsl325
-rw-r--r--source/blender/draw/engines/eevee/shaders/closure_eval_translucent_lib.glsl15
-rw-r--r--source/blender/draw/engines/eevee/shaders/closure_eval_volume_lib.glsl113
-rw-r--r--source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl242
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_empty.glsl7
-rw-r--r--source/blender/draw/engines/eevee/shaders/eevee_empty_volume.glsl8
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl10
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl3
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl3
-rw-r--r--source/blender/draw/engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl1
-rw-r--r--source/blender/draw/engines/eevee/shaders/prepass_frag.glsl10
-rw-r--r--source/blender/draw/engines/eevee/shaders/prepass_vert.glsl35
-rw-r--r--source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl21
-rw-r--r--source/blender/draw/engines/eevee/shaders/shadow_vert.glsl92
-rw-r--r--source/blender/draw/engines/eevee/shaders/surface_frag.glsl141
-rw-r--r--source/blender/draw/engines/eevee/shaders/surface_lib.glsl166
-rw-r--r--source/blender/draw/engines/eevee/shaders/surface_vert.glsl108
-rw-r--r--source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl116
-rw-r--r--source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl6
-rw-r--r--source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl12
-rw-r--r--source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl28
-rw-r--r--source/blender/draw/engines/eevee/shaders/world_vert.glsl24
-rw-r--r--source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl3
-rw-r--r--source/blender/draw/engines/overlay/overlay_extra.c6
-rw-r--r--source/blender/draw/engines/overlay/shaders/background_frag.glsl8
-rw-r--r--source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl6
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_prepass_hair_vert.glsl6
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl9
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_world_light_lib.glsl25
-rw-r--r--source/blender/draw/engines/workbench/workbench_engine.c3
-rw-r--r--source/blender/draw/intern/DRW_render.h54
-rw-r--r--source/blender/draw/intern/draw_cache_extract_mesh.cc1
-rw-r--r--source/blender/draw/intern/draw_cache_impl_mesh.c1
-rw-r--r--source/blender/draw/intern/draw_cache_impl_subdivision.cc8
-rw-r--r--source/blender/draw/intern/draw_common.h5
-rw-r--r--source/blender/draw/intern/draw_hair.c161
-rw-r--r--source/blender/draw/intern/draw_manager.c4
-rw-r--r--source/blender/draw/intern/draw_manager_shader.c190
-rw-r--r--source/blender/draw/intern/draw_shader_shared.h21
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc67
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc4
-rw-r--r--source/blender/draw/intern/shaders/common_attribute_lib.glsl21
-rw-r--r--source/blender/draw/intern/shaders/common_hair_lib.glsl5
-rw-r--r--source/blender/draw/intern/shaders/common_hair_refine_vert.glsl7
-rw-r--r--source/blender/draw/intern/shaders/common_math_lib.glsl6
-rw-r--r--source/blender/draw/intern/shaders/common_smaa_lib.glsl28
-rw-r--r--source/blender/draw/intern/shaders/common_view_lib.glsl54
-rw-r--r--source/blender/editors/animation/anim_channels_defines.c6
-rw-r--r--source/blender/editors/animation/anim_markers.c78
-rw-r--r--source/blender/editors/animation/keyframing.c305
-rw-r--r--source/blender/editors/curves/intern/curves_ops.cc270
-rw-r--r--source/blender/editors/datafiles/CMakeLists.txt5
-rw-r--r--source/blender/editors/geometry/geometry_attributes.cc2
-rw-r--r--source/blender/editors/gizmo_library/gizmo_draw_utils.c34
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c11
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c109
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c26
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c15
-rw-r--r--source/blender/editors/gpencil/gpencil_select.c9
-rw-r--r--source/blender/editors/include/ED_armature.h2
-rw-r--r--source/blender/editors/include/ED_paint.h5
-rw-r--r--source/blender/editors/include/ED_sculpt.h3
-rw-r--r--source/blender/editors/include/UI_interface.h19
-rw-r--r--source/blender/editors/interface/interface_handlers.c4
-rw-r--r--source/blender/editors/interface/interface_icons.c4
-rw-r--r--source/blender/editors/interface/interface_layout.c34
-rw-r--r--source/blender/editors/interface/interface_query.cc2
-rw-r--r--source/blender/editors/interface/interface_region_tooltip.c6
-rw-r--r--source/blender/editors/interface/interface_style.cc8
-rw-r--r--source/blender/editors/interface/interface_templates.c2
-rw-r--r--source/blender/editors/interface/interface_widgets.c6
-rw-r--r--source/blender/editors/io/io_obj.c13
-rw-r--r--source/blender/editors/io/io_usd.c36
-rw-r--r--source/blender/editors/mask/mask_ops.c37
-rw-r--r--source/blender/editors/mask/mask_select.c93
-rw-r--r--source/blender/editors/object/CMakeLists.txt2
-rw-r--r--source/blender/editors/object/object_edit.c7
-rw-r--r--source/blender/editors/object/object_intern.h2
-rw-r--r--source/blender/editors/object/object_modifier.cc (renamed from source/blender/editors/object/object_modifier.c)307
-rw-r--r--source/blender/editors/object/object_ops.c2
-rw-r--r--source/blender/editors/object/object_relations.c36
-rw-r--r--source/blender/editors/object/object_select.c2
-rw-r--r--source/blender/editors/object/object_transform.cc34
-rw-r--r--source/blender/editors/physics/particle_edit.c11
-rw-r--r--source/blender/editors/render/render_shading.cc6
-rw-r--r--source/blender/editors/screen/area.c16
-rw-r--r--source/blender/editors/sculpt_paint/CMakeLists.txt1
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_ops.cc427
-rw-r--r--source/blender/editors/sculpt_paint/paint_canvas.cc71
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c127
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_color.c89
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_mesh.c1
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h35
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_ops.c15
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_paint_color.c8
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_paint_image.cc430
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_undo.c2
-rw-r--r--source/blender/editors/space_action/CMakeLists.txt1
-rw-r--r--source/blender/editors/space_action/action_data.c9
-rw-r--r--source/blender/editors/space_buttons/space_buttons.c12
-rw-r--r--source/blender/editors/space_file/file_draw.c4
-rw-r--r--source/blender/editors/space_file/filelist.c2
-rw-r--r--source/blender/editors/space_nla/nla_edit.c15
-rw-r--r--source/blender/editors/space_node/node_draw.cc270
-rw-r--r--source/blender/editors/space_node/node_intern.hh2
-rw-r--r--source/blender/editors/space_node/node_templates.cc16
-rw-r--r--source/blender/editors/space_outliner/outliner_tools.cc15
-rw-r--r--source/blender/editors/space_spreadsheet/space_spreadsheet.cc8
-rw-r--r--source/blender/editors/space_view3d/view3d_select.c43
-rw-r--r--source/blender/editors/space_view3d/view3d_view.c1
-rw-r--r--source/blender/editors/transform/transform_convert_sequencer.c6
-rw-r--r--source/blender/editors/util/ed_draw.c2
-rw-r--r--source/blender/editors/util/ed_transverts.c2
-rw-r--r--source/blender/functions/FN_multi_function_builder.hh33
-rw-r--r--source/blender/functions/tests/FN_multi_function_test.cc19
-rw-r--r--source/blender/geometry/intern/mesh_merge_by_distance.cc235
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilenvelope.c25
-rw-r--r--source/blender/gpu/CMakeLists.txt4
-rw-r--r--source/blender/gpu/GPU_immediate_util.h11
-rw-r--r--source/blender/gpu/GPU_material.h126
-rw-r--r--source/blender/gpu/GPU_shader.h1
-rw-r--r--source/blender/gpu/GPU_uniform_buffer.h4
-rw-r--r--source/blender/gpu/intern/gpu_codegen.c1123
-rw-r--r--source/blender/gpu/intern/gpu_codegen.cc825
-rw-r--r--source/blender/gpu/intern/gpu_codegen.h28
-rw-r--r--source/blender/gpu/intern/gpu_immediate_util.c143
-rw-r--r--source/blender/gpu/intern/gpu_init_exit.c2
-rw-r--r--source/blender/gpu/intern/gpu_material.c339
-rw-r--r--source/blender/gpu/intern/gpu_material_library.c904
-rw-r--r--source/blender/gpu/intern/gpu_material_library.h27
-rw-r--r--source/blender/gpu/intern/gpu_node_graph.c117
-rw-r--r--source/blender/gpu/intern/gpu_node_graph.h54
-rw-r--r--source/blender/gpu/intern/gpu_shader.cc44
-rw-r--r--source/blender/gpu/intern/gpu_shader_builder_stubs.cc5
-rw-r--r--source/blender/gpu/intern/gpu_shader_create_info.cc28
-rw-r--r--source/blender/gpu/intern/gpu_shader_create_info.hh11
-rw-r--r--source/blender/gpu/intern/gpu_shader_dependency.cc321
-rw-r--r--source/blender/gpu/opengl/gl_shader.cc17
-rw-r--r--source/blender/gpu/opengl/gl_texture.cc2
-rw-r--r--source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl173
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_ambient_occlusion.glsl18
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_anisotropic.glsl26
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_background.glsl16
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_bump.glsl38
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_camera.glsl8
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_combine_hsv.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl31
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_displacement.glsl8
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl101
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_emission.glsl15
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl3
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_fresnel.glsl7
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_gamma.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl53
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_glass.glsl64
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_glossy.glsl39
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_hair.glsl46
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl26
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl10
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_holdout.glsl14
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_hue_sat_val.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_layer_weight.glsl10
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_light_path.glsl16
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_mapping.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_math.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl69
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_mix_rgb.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_normal_map.glsl14
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_object_info.glsl16
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_output_aov.glsl12
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_output_material.glsl36
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_output_world.glsl18
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_particle_info.glsl23
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_point_info.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl248
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl37
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_separate_hsv.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_shader_to_rgba.glsl27
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl30
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_tangent.glsl6
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_tex_brick.glsl3
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl16
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl3
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl4
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl3
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl4
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_texture_coordinates.glsl84
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_toon.glsl20
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_translucent.glsl23
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_transparent.glsl17
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_vector_displacement.glsl32
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_vector_rotate.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_velvet.glsl19
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_volume_absorption.glsl13
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_volume_info.glsl24
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_volume_principled.glsl22
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_volume_scatter.glsl15
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_wireframe.glsl23
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_world_normals.glsl24
-rw-r--r--source/blender/imbuf/IMB_colormanagement.h4
-rw-r--r--source/blender/imbuf/intern/colormanagement.c33
-rw-r--r--source/blender/io/alembic/ABC_alembic.h14
-rw-r--r--source/blender/io/alembic/intern/alembic_capi.cc17
-rw-r--r--source/blender/io/common/CMakeLists.txt5
-rw-r--r--source/blender/io/common/IO_string_utils.hh69
-rw-r--r--source/blender/io/common/intern/string_utils.cc99
-rw-r--r--source/blender/io/common/intern/string_utils_test.cc118
-rw-r--r--source/blender/io/usd/CMakeLists.txt28
-rw-r--r--source/blender/io/usd/tests/usd_imaging_test.cc71
-rw-r--r--source/blender/io/usd/tests/usd_stage_creation_test.cc19
-rw-r--r--source/blender/io/usd/tests/usd_tests_common.cc45
-rw-r--r--source/blender/io/usd/tests/usd_tests_common.h20
-rw-r--r--source/blender/io/wavefront_obj/CMakeLists.txt5
-rw-r--r--source/blender/io/wavefront_obj/IO_wavefront_obj.h1
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc11
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc58
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh13
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_nurbs.cc6
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_exporter.cc39
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc792
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_import_file_reader.hh114
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_import_mesh.cc95
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_import_mesh.hh7
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_import_mtl.cc10
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_import_mtl.hh25
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_import_objects.hh25
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_importer.cc37
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_importer.hh3
-rw-r--r--source/blender/io/wavefront_obj/importer/parser_string_utils.cc174
-rw-r--r--source/blender/io/wavefront_obj/importer/parser_string_utils.hh54
-rw-r--r--source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc95
-rw-r--r--source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh110
-rw-r--r--source/blender/io/wavefront_obj/tests/obj_importer_tests.cc4
-rw-r--r--source/blender/io/wavefront_obj/tests/obj_mtl_parser_tests.cc172
-rw-r--r--source/blender/makesdna/DNA_anim_types.h2
-rw-r--r--source/blender/makesdna/DNA_brush_enums.h1
-rw-r--r--source/blender/makesdna/DNA_curve_types.h6
-rw-r--r--source/blender/makesdna/DNA_lattice_types.h4
-rw-r--r--source/blender/makesdna/DNA_light_types.h2
-rw-r--r--source/blender/makesdna/DNA_linestyle_types.h82
-rw-r--r--source/blender/makesdna/DNA_material_types.h8
-rw-r--r--source/blender/makesdna/DNA_mesh_defaults.h2
-rw-r--r--source/blender/makesdna/DNA_mesh_types.h8
-rw-r--r--source/blender/makesdna/DNA_node_types.h15
-rw-r--r--source/blender/makesdna/DNA_particle_types.h7
-rw-r--r--source/blender/makesdna/DNA_scene_types.h1
-rw-r--r--source/blender/makesdna/DNA_screen_types.h2
-rw-r--r--source/blender/makesdna/DNA_simulation_types.h2
-rw-r--r--source/blender/makesdna/DNA_space_types.h3
-rw-r--r--source/blender/makesdna/DNA_texture_types.h5
-rw-r--r--source/blender/makesdna/DNA_userdef_types.h2
-rw-r--r--source/blender/makesdna/DNA_world_types.h2
-rw-r--r--source/blender/makesrna/intern/rna_ID.c2
-rw-r--r--source/blender/makesrna/intern/rna_armature.c2
-rw-r--r--source/blender/makesrna/intern/rna_brush.c3
-rw-r--r--source/blender/makesrna/intern/rna_camera.c3
-rw-r--r--source/blender/makesrna/intern/rna_mesh.c3
-rw-r--r--source/blender/makesrna/intern/rna_modifier.c2
-rw-r--r--source/blender/makesrna/intern/rna_object.c6
-rw-r--r--source/blender/makesrna/intern/rna_space.c7
-rw-r--r--source/blender/makesrna/intern/rna_texture.c2
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c6
-rw-r--r--source/blender/modifiers/intern/MOD_laplaciansmooth.c2
-rw-r--r--source/blender/modifiers/intern/MOD_meshsequencecache.cc15
-rw-r--r--source/blender/modifiers/intern/MOD_screw.c4
-rw-r--r--source/blender/nodes/NOD_geometry_exec.hh3
-rw-r--r--source/blender/nodes/NOD_geometry_nodes_eval_log.hh26
-rw-r--r--source/blender/nodes/NOD_node_tree_ref.hh16
-rw-r--r--source/blender/nodes/NOD_shader.h5
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc13
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc1
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_material.cc13
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc4
-rw-r--r--source/blender/nodes/intern/geometry_nodes_eval_log.cc14
-rw-r--r--source/blender/nodes/intern/node_geometry_exec.cc10
-rw-r--r--source/blender/nodes/intern/node_tree_ref.cc17
-rw-r--r--source/blender/nodes/shader/node_shader_tree.cc736
-rw-r--r--source/blender/nodes/shader/node_shader_util.cc15
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_ambient_occlusion.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_background.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bevel.cc6
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bsdf_anisotropic.cc10
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bsdf_diffuse.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bsdf_glass.cc11
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bsdf_glossy.cc9
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bsdf_hair.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.cc11
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc61
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bsdf_refraction.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bsdf_toon.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bsdf_translucent.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bsdf_transparent.cc4
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bsdf_velvet.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bump.cc17
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_camera.cc6
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_displacement.cc9
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_eevee_specular.cc9
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_emission.cc4
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_fresnel.cc8
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_geometry.cc20
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_holdout.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_layer_weight.cc8
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_normal_map.cc9
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_object_info.cc11
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_output_aov.cc14
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_output_material.cc44
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_output_world.cc18
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_particle_info.cc13
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_shader_to_rgb.cc4
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.cc20
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tangent.cc9
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_coord.cc24
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_environment.cc12
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_image.cc8
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_pointdensity.cc6
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vector_displacement.cc29
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc34
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vector_transform.cc39
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_volume_absorption.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_volume_principled.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_volume_scatter.cc1
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_wireframe.cc14
-rw-r--r--source/blender/python/bmesh/bmesh_py_types.c11
-rw-r--r--source/blender/python/bmesh/bmesh_py_types_customdata.c7
-rw-r--r--source/blender/python/generic/py_capi_utils.h8
-rw-r--r--source/blender/python/gpu/CMakeLists.txt1
-rw-r--r--source/blender/python/gpu/gpu_py_buffer.c38
-rw-r--r--source/blender/python/gpu/gpu_py_element.c2
-rw-r--r--source/blender/python/gpu/gpu_py_shader.c45
-rw-r--r--source/blender/python/gpu/gpu_py_shader.h47
-rw-r--r--source/blender/python/gpu/gpu_py_shader_create_info.cc1130
-rw-r--r--source/blender/python/gpu/gpu_py_types.c8
-rw-r--r--source/blender/python/gpu/gpu_py_uniformbuffer.c36
-rw-r--r--source/blender/sequencer/intern/effects.c6
-rw-r--r--source/blender/windowmanager/intern/wm_draw.c51
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c124
-rw-r--r--source/blender/windowmanager/intern/wm_toolsystem.c2
-rw-r--r--source/blender/windowmanager/intern/wm_window.c10
-rw-r--r--source/creator/creator_args.c14
m---------source/tools0
-rw-r--r--tests/python/CMakeLists.txt8
523 files changed, 18119 insertions, 8713 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ca457ab6b37..cc39429742e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -446,7 +446,7 @@ if(NOT APPLE)
endif()
option(WITH_CYCLES_HIP_BINARIES "Build Cycles AMD HIP binaries" OFF)
- set(CYCLES_HIP_BINARIES_ARCH gfx900 gfx906 gfx1010 gfx1011 gfx1012 gfx1030 gfx1031 gfx1032 gfx1034 CACHE STRING "AMD HIP architectures to build binaries for")
+ set(CYCLES_HIP_BINARIES_ARCH gfx1010 gfx1011 gfx1012 gfx1030 gfx1031 gfx1032 gfx1034 CACHE STRING "AMD HIP architectures to build binaries for")
mark_as_advanced(WITH_CYCLES_DEVICE_HIP)
mark_as_advanced(CYCLES_HIP_BINARIES_ARCH)
endif()
diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh
index 8018ac8ff1a..ecf5d215333 100755
--- a/build_files/build_environment/install_deps.sh
+++ b/build_files/build_environment/install_deps.sh
@@ -4004,6 +4004,7 @@ install_DEB() {
WEBP_DEV="libwebp-dev"
check_package_DEB $WEBP_DEV
if [ $? -eq 0 ]; then
+ install_packages_DEB $WEBP_DEV
WEBP_USE=true
fi
@@ -4435,9 +4436,6 @@ install_DEB() {
if [ "$VPX_USE" = true ]; then
_packages="$_packages $VPX_DEV"
fi
- if [ "$WEBP_USE" = true ]; then
- _packages="$_packages $WEBP_DEV"
- fi
if [ "$OPUS_USE" = true ]; then
_packages="$_packages $OPUS_DEV"
fi
@@ -4722,6 +4720,7 @@ install_RPM() {
WEBP_DEV="libwebp-devel"
check_package_RPM $WEBP_DEV
if [ $? -eq 0 ]; then
+ install_packages_RPM $WEBP_DEV
WEBP_USE=true
fi
@@ -5124,9 +5123,6 @@ install_RPM() {
if [ "$VPX_USE" = true ]; then
_packages="$_packages $VPX_DEV"
fi
- if [ "$WEBP_USE" = true ]; then
- _packages="$_packages $WEBP_DEV"
- fi
if [ "$OPUS_USE" = true ]; then
_packages="$_packages $OPUS_DEV"
fi
@@ -5300,6 +5296,7 @@ install_ARCH() {
WEBP_DEV="libwebp"
check_package_ARCH $WEBP_DEV
if [ $? -eq 0 ]; then
+ install_packages_ARCH $WEBP_DEV
WEBP_USE=true
fi
@@ -5704,16 +5701,12 @@ install_ARCH() {
if [ "$VPX_USE" = true ]; then
_packages="$_packages $VPX_DEV"
fi
- if [ "$WEBP_USE" = true ]; then
- _packages="$_packages $WEBP_DEV"
- fi
if [ "$OPUS_USE" = true ]; then
_packages="$_packages $OPUS_DEV"
fi
if [ "$MP3LAME_USE" = true ]; then
_packages="$_packages $MP3LAME_DEV"
fi
- install_packages_ARCH $_packages
compile_FFmpeg
fi
@@ -5961,6 +5954,12 @@ print_info() {
fi
fi
+ if [ "$WEBP_USE" = true ]; then
+ _1="-D WITH_IMAGE_WEBP=ON"
+ PRINT " $_1"
+ _buildargs="$_buildargs $_1"
+ fi
+
if [ -d $INST/openexr ]; then
_1="-D OPENEXR_ROOT_DIR=$INST/openexr"
PRINT " $_1"
diff --git a/build_files/cmake/platform/platform_apple.cmake b/build_files/cmake/platform/platform_apple.cmake
index 43ce23081af..cdc9aa91a53 100644
--- a/build_files/cmake/platform/platform_apple.cmake
+++ b/build_files/cmake/platform/platform_apple.cmake
@@ -214,7 +214,7 @@ if(WITH_SDL)
find_package(SDL2)
set(SDL_INCLUDE_DIR ${SDL2_INCLUDE_DIRS})
set(SDL_LIBRARY ${SDL2_LIBRARIES})
- string(APPEND PLATFORM_LINKFLAGS " -framework ForceFeedback")
+ string(APPEND PLATFORM_LINKFLAGS " -framework ForceFeedback -framework GameController -framework CoreHaptics")
endif()
set(PNG_ROOT ${LIBDIR}/png)
diff --git a/extern/fast_float/LICENSE-MIT b/extern/fast_float/LICENSE-MIT
new file mode 100644
index 00000000000..2fb2a37ad7f
--- /dev/null
+++ b/extern/fast_float/LICENSE-MIT
@@ -0,0 +1,27 @@
+MIT License
+
+Copyright (c) 2021 The fast_float authors
+
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/extern/fast_float/README.blender b/extern/fast_float/README.blender
new file mode 100644
index 00000000000..a584a0511ee
--- /dev/null
+++ b/extern/fast_float/README.blender
@@ -0,0 +1,7 @@
+Project: fast_float
+URL: https://github.com/fastfloat/fast_float
+License: MIT
+Upstream version: 3.4.0 (b7f9d6c)
+Local modifications:
+
+- Took only the fast_float.h header and the license/readme files
diff --git a/extern/fast_float/README.md b/extern/fast_float/README.md
new file mode 100644
index 00000000000..1e1c06d0a3e
--- /dev/null
+++ b/extern/fast_float/README.md
@@ -0,0 +1,218 @@
+## fast_float number parsing library: 4x faster than strtod
+
+![Ubuntu 20.04 CI (GCC 9)](https://github.com/lemire/fast_float/workflows/Ubuntu%2020.04%20CI%20(GCC%209)/badge.svg)
+![Ubuntu 18.04 CI (GCC 7)](https://github.com/lemire/fast_float/workflows/Ubuntu%2018.04%20CI%20(GCC%207)/badge.svg)
+![Alpine Linux](https://github.com/lemire/fast_float/workflows/Alpine%20Linux/badge.svg)
+![MSYS2-CI](https://github.com/lemire/fast_float/workflows/MSYS2-CI/badge.svg)
+![VS16-CLANG-CI](https://github.com/lemire/fast_float/workflows/VS16-CLANG-CI/badge.svg)
+[![VS16-CI](https://github.com/fastfloat/fast_float/actions/workflows/vs16-ci.yml/badge.svg)](https://github.com/fastfloat/fast_float/actions/workflows/vs16-ci.yml)
+
+The fast_float library provides fast header-only implementations for the C++ from_chars
+functions for `float` and `double` types. These functions convert ASCII strings representing
+decimal values (e.g., `1.3e10`) into binary types. We provide exact rounding (including
+round to even). In our experience, these `fast_float` functions many times faster than comparable number-parsing functions from existing C++ standard libraries.
+
+Specifically, `fast_float` provides the following two functions with a C++17-like syntax (the library itself only requires C++11):
+
+```C++
+from_chars_result from_chars(const char* first, const char* last, float& value, ...);
+from_chars_result from_chars(const char* first, const char* last, double& value, ...);
+```
+
+The return type (`from_chars_result`) is defined as the struct:
+```C++
+struct from_chars_result {
+ const char* ptr;
+ std::errc ec;
+};
+```
+
+It parses the character sequence [first,last) for a number. It parses floating-point numbers expecting
+a locale-independent format equivalent to the C++17 from_chars function.
+The resulting floating-point value is the closest floating-point values (using either float or double),
+using the "round to even" convention for values that would otherwise fall right in-between two values.
+That is, we provide exact parsing according to the IEEE standard.
+
+
+Given a successful parse, the pointer (`ptr`) in the returned value is set to point right after the
+parsed number, and the `value` referenced is set to the parsed value. In case of error, the returned
+`ec` contains a representative error, otherwise the default (`std::errc()`) value is stored.
+
+The implementation does not throw and does not allocate memory (e.g., with `new` or `malloc`).
+
+It will parse infinity and nan values.
+
+Example:
+
+``` C++
+#include "fast_float/fast_float.h"
+#include <iostream>
+
+int main() {
+ const std::string input = "3.1416 xyz ";
+ double result;
+ auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
+ if(answer.ec != std::errc()) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
+ std::cout << "parsed the number " << result << std::endl;
+ return EXIT_SUCCESS;
+}
+```
+
+
+Like the C++17 standard, the `fast_float::from_chars` functions take an optional last argument of
+the type `fast_float::chars_format`. It is a bitset value: we check whether
+`fmt & fast_float::chars_format::fixed` and `fmt & fast_float::chars_format::scientific` are set
+to determine whether we allow the fixed point and scientific notation respectively.
+The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`.
+
+The library seeks to follow the C++17 (see [20.19.3](http://eel.is/c++draft/charconv.from.chars).(7.1)) specification.
+* The `from_chars` function does not skip leading white-space characters.
+* [A leading `+` sign](https://en.cppreference.com/w/cpp/utility/from_chars) is forbidden.
+* It is generally impossible to represent a decimal value exactly as binary floating-point number (`float` and `double` types). We seek the nearest value. We round to an even mantissa when we are in-between two binary floating-point numbers.
+
+Furthermore, we have the following restrictions:
+* We only support `float` and `double` types at this time.
+* We only support the decimal format: we do not support hexadecimal strings.
+* For values that are either very large or very small (e.g., `1e9999`), we represent it using the infinity or negative infinity value.
+
+We support Visual Studio, macOS, Linux, freeBSD. We support big and little endian. We support 32-bit and 64-bit systems.
+
+
+
+## Using commas as decimal separator
+
+
+The C++ standard stipulate that `from_chars` has to be locale-independent. In
+particular, the decimal separator has to be the period (`.`). However,
+some users still want to use the `fast_float` library with in a locale-dependent
+manner. Using a separate function called `from_chars_advanced`, we allow the users
+to pass a `parse_options` instance which contains a custom decimal separator (e.g.,
+the comma). You may use it as follows.
+
+```C++
+#include "fast_float/fast_float.h"
+#include <iostream>
+
+int main() {
+ const std::string input = "3,1416 xyz ";
+ double result;
+ fast_float::parse_options options{fast_float::chars_format::general, ','};
+ auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options);
+ if((answer.ec != std::errc()) || ((result != 3.1416))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
+ std::cout << "parsed the number " << result << std::endl;
+ return EXIT_SUCCESS;
+}
+```
+
+
+## Reference
+
+- Daniel Lemire, [Number Parsing at a Gigabyte per Second](https://arxiv.org/abs/2101.11408), Software: Pratice and Experience 51 (8), 2021.
+
+## Other programming languages
+
+- [There is an R binding](https://github.com/eddelbuettel/rcppfastfloat) called `rcppfastfloat`.
+- [There is a Rust port of the fast_float library](https://github.com/aldanor/fast-float-rust/) called `fast-float-rust`.
+- [There is a Java port of the fast_float library](https://github.com/wrandelshofer/FastDoubleParser) called `FastDoubleParser`.
+- [There is a C# port of the fast_float library](https://github.com/CarlVerret/csFastFloat) called `csFastFloat`.
+
+
+## Relation With Other Work
+
+The fastfloat algorithm is part of the [LLVM standard libraries](https://github.com/llvm/llvm-project/commit/87c016078ad72c46505461e4ff8bfa04819fe7ba).
+
+The fast_float library provides a performance similar to that of the [fast_double_parser](https://github.com/lemire/fast_double_parser) library but using an updated algorithm reworked from the ground up, and while offering an API more in line with the expectations of C++ programmers. The fast_double_parser library is part of the [Microsoft LightGBM machine-learning framework](https://github.com/microsoft/LightGBM).
+
+## Users
+
+The fast_float library is used by [Apache Arrow](https://github.com/apache/arrow/pull/8494) where it multiplied the number parsing speed by two or three times. It is also used by [Yandex ClickHouse](https://github.com/ClickHouse/ClickHouse) and by [Google Jsonnet](https://github.com/google/jsonnet).
+
+
+## How fast is it?
+
+It can parse random floating-point numbers at a speed of 1 GB/s on some systems. We find that it is often twice as fast as the best available competitor, and many times faster than many standard-library implementations.
+
+<img src="http://lemire.me/blog/wp-content/uploads/2020/11/fastfloat_speed.png" width="400">
+
+```
+$ ./build/benchmarks/benchmark
+# parsing random integers in the range [0,1)
+volume = 2.09808 MB
+netlib : 271.18 MB/s (+/- 1.2 %) 12.93 Mfloat/s
+doubleconversion : 225.35 MB/s (+/- 1.2 %) 10.74 Mfloat/s
+strtod : 190.94 MB/s (+/- 1.6 %) 9.10 Mfloat/s
+abseil : 430.45 MB/s (+/- 2.2 %) 20.52 Mfloat/s
+fastfloat : 1042.38 MB/s (+/- 9.9 %) 49.68 Mfloat/s
+```
+
+See https://github.com/lemire/simple_fastfloat_benchmark for our benchmarking code.
+
+
+## Video
+
+[![Go Systems 2020](http://img.youtube.com/vi/AVXgvlMeIm4/0.jpg)](http://www.youtube.com/watch?v=AVXgvlMeIm4)<br />
+
+## Using as a CMake dependency
+
+This library is header-only by design. The CMake file provides the `fast_float` target
+which is merely a pointer to the `include` directory.
+
+If you drop the `fast_float` repository in your CMake project, you should be able to use
+it in this manner:
+
+```cmake
+add_subdirectory(fast_float)
+target_link_libraries(myprogram PUBLIC fast_float)
+```
+
+Or you may want to retrieve the dependency automatically if you have a sufficiently recent version of CMake (3.11 or better at least):
+
+```cmake
+FetchContent_Declare(
+ fast_float
+ GIT_REPOSITORY https://github.com/lemire/fast_float.git
+ GIT_TAG tags/v1.1.2
+ GIT_SHALLOW TRUE)
+
+FetchContent_MakeAvailable(fast_float)
+target_link_libraries(myprogram PUBLIC fast_float)
+
+```
+
+You should change the `GIT_TAG` line so that you recover the version you wish to use.
+
+## Using as single header
+
+The script `script/amalgamate.py` may be used to generate a single header
+version of the library if so desired.
+Just run the script from the root directory of this repository.
+You can customize the license type and output file if desired as described in
+the command line help.
+
+You may directly download automatically generated single-header files:
+
+https://github.com/fastfloat/fast_float/releases/download/v1.1.2/fast_float.h
+
+## Credit
+
+Though this work is inspired by many different people, this work benefited especially from exchanges with
+Michael Eisel, who motivated the original research with his key insights, and with Nigel Tao who provided
+invaluable feedback. Rémy Oudompheng first implemented a fast path we use in the case of long digits.
+
+The library includes code adapted from Google Wuffs (written by Nigel Tao) which was originally published
+under the Apache 2.0 license.
+
+## License
+
+<sup>
+Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
+2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option.
+</sup>
+
+<br>
+
+<sub>
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in this repository by you, as defined in the Apache-2.0 license,
+shall be dual licensed as above, without any additional terms or conditions.
+</sub>
diff --git a/extern/fast_float/fast_float.h b/extern/fast_float/fast_float.h
new file mode 100644
index 00000000000..2482dfdbd6c
--- /dev/null
+++ b/extern/fast_float/fast_float.h
@@ -0,0 +1,2979 @@
+// fast_float by Daniel Lemire
+// fast_float by João Paulo Magalhaes
+//
+// with contributions from Eugene Golushkov
+// with contributions from Maksim Kita
+// with contributions from Marcin Wojdyr
+// with contributions from Neal Richardson
+// with contributions from Tim Paine
+// with contributions from Fabio Pellacini
+//
+// Licensed under the Apache License, Version 2.0, or the
+// MIT License at your option. This file may not be copied,
+// modified, or distributed except according to those terms.
+//
+// MIT License Notice
+//
+// MIT License
+//
+// Copyright (c) 2021 The fast_float authors
+//
+// Permission is hereby granted, free of charge, to any
+// person obtaining a copy of this software and associated
+// documentation files (the "Software"), to deal in the
+// Software without restriction, including without
+// limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software
+// is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice
+// shall be included in all copies or substantial portions
+// of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+//
+// Apache License (Version 2.0) Notice
+//
+// Copyright 2021 The fast_float authors
+// 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
+//
+
+#ifndef FASTFLOAT_FAST_FLOAT_H
+#define FASTFLOAT_FAST_FLOAT_H
+
+#include <system_error>
+
+namespace fast_float {
+enum chars_format {
+ scientific = 1<<0,
+ fixed = 1<<2,
+ hex = 1<<3,
+ general = fixed | scientific
+};
+
+
+struct from_chars_result {
+ const char *ptr;
+ std::errc ec;
+};
+
+struct parse_options {
+ constexpr explicit parse_options(chars_format fmt = chars_format::general,
+ char dot = '.')
+ : format(fmt), decimal_point(dot) {}
+
+ /** Which number formats are accepted */
+ chars_format format;
+ /** The character used as decimal point */
+ char decimal_point;
+};
+
+/**
+ * This function parses the character sequence [first,last) for a number. It parses floating-point numbers expecting
+ * a locale-indepent format equivalent to what is used by std::strtod in the default ("C") locale.
+ * The resulting floating-point value is the closest floating-point values (using either float or double),
+ * using the "round to even" convention for values that would otherwise fall right in-between two values.
+ * That is, we provide exact parsing according to the IEEE standard.
+ *
+ * Given a successful parse, the pointer (`ptr`) in the returned value is set to point right after the
+ * parsed number, and the `value` referenced is set to the parsed value. In case of error, the returned
+ * `ec` contains a representative error, otherwise the default (`std::errc()`) value is stored.
+ *
+ * The implementation does not throw and does not allocate memory (e.g., with `new` or `malloc`).
+ *
+ * Like the C++17 standard, the `fast_float::from_chars` functions take an optional last argument of
+ * the type `fast_float::chars_format`. It is a bitset value: we check whether
+ * `fmt & fast_float::chars_format::fixed` and `fmt & fast_float::chars_format::scientific` are set
+ * to determine whether we allowe the fixed point and scientific notation respectively.
+ * The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`.
+ */
+template<typename T>
+from_chars_result from_chars(const char *first, const char *last,
+ T &value, chars_format fmt = chars_format::general) noexcept;
+
+/**
+ * Like from_chars, but accepts an `options` argument to govern number parsing.
+ */
+template<typename T>
+from_chars_result from_chars_advanced(const char *first, const char *last,
+ T &value, parse_options options) noexcept;
+
+}
+#endif // FASTFLOAT_FAST_FLOAT_H
+
+#ifndef FASTFLOAT_FLOAT_COMMON_H
+#define FASTFLOAT_FLOAT_COMMON_H
+
+#include <cfloat>
+#include <cstdint>
+#include <cassert>
+#include <cstring>
+#include <type_traits>
+
+#if (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) \
+ || defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) \
+ || defined(__MINGW64__) \
+ || defined(__s390x__) \
+ || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) \
+ || defined(__EMSCRIPTEN__))
+#define FASTFLOAT_64BIT
+#elif (defined(__i386) || defined(__i386__) || defined(_M_IX86) \
+ || defined(__arm__) || defined(_M_ARM) \
+ || defined(__MINGW32__))
+#define FASTFLOAT_32BIT
+#else
+ // Need to check incrementally, since SIZE_MAX is a size_t, avoid overflow.
+ // We can never tell the register width, but the SIZE_MAX is a good approximation.
+ // UINTPTR_MAX and INTPTR_MAX are optional, so avoid them for max portability.
+ #if SIZE_MAX == 0xffff
+ #error Unknown platform (16-bit, unsupported)
+ #elif SIZE_MAX == 0xffffffff
+ #define FASTFLOAT_32BIT
+ #elif SIZE_MAX == 0xffffffffffffffff
+ #define FASTFLOAT_64BIT
+ #else
+ #error Unknown platform (not 32-bit, not 64-bit?)
+ #endif
+#endif
+
+#if ((defined(_WIN32) || defined(_WIN64)) && !defined(__clang__))
+#include <intrin.h>
+#endif
+
+#if defined(_MSC_VER) && !defined(__clang__)
+#define FASTFLOAT_VISUAL_STUDIO 1
+#endif
+
+#ifdef _WIN32
+#define FASTFLOAT_IS_BIG_ENDIAN 0
+#else
+#if defined(__APPLE__) || defined(__FreeBSD__)
+#include <machine/endian.h>
+#elif defined(sun) || defined(__sun)
+#include <sys/byteorder.h>
+#else
+#include <endian.h>
+#endif
+#
+#ifndef __BYTE_ORDER__
+// safe choice
+#define FASTFLOAT_IS_BIG_ENDIAN 0
+#endif
+#
+#ifndef __ORDER_LITTLE_ENDIAN__
+// safe choice
+#define FASTFLOAT_IS_BIG_ENDIAN 0
+#endif
+#
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#define FASTFLOAT_IS_BIG_ENDIAN 0
+#else
+#define FASTFLOAT_IS_BIG_ENDIAN 1
+#endif
+#endif
+
+#ifdef FASTFLOAT_VISUAL_STUDIO
+#define fastfloat_really_inline __forceinline
+#else
+#define fastfloat_really_inline inline __attribute__((always_inline))
+#endif
+
+#ifndef FASTFLOAT_ASSERT
+#define FASTFLOAT_ASSERT(x) { if (!(x)) abort(); }
+#endif
+
+#ifndef FASTFLOAT_DEBUG_ASSERT
+#include <cassert>
+#define FASTFLOAT_DEBUG_ASSERT(x) assert(x)
+#endif
+
+// rust style `try!()` macro, or `?` operator
+#define FASTFLOAT_TRY(x) { if (!(x)) return false; }
+
+namespace fast_float {
+
+// Compares two ASCII strings in a case insensitive manner.
+inline bool fastfloat_strncasecmp(const char *input1, const char *input2,
+ size_t length) {
+ char running_diff{0};
+ for (size_t i = 0; i < length; i++) {
+ running_diff |= (input1[i] ^ input2[i]);
+ }
+ return (running_diff == 0) || (running_diff == 32);
+}
+
+#ifndef FLT_EVAL_METHOD
+#error "FLT_EVAL_METHOD should be defined, please include cfloat."
+#endif
+
+// a pointer and a length to a contiguous block of memory
+template <typename T>
+struct span {
+ const T* ptr;
+ size_t length;
+ span(const T* _ptr, size_t _length) : ptr(_ptr), length(_length) {}
+ span() : ptr(nullptr), length(0) {}
+
+ constexpr size_t len() const noexcept {
+ return length;
+ }
+
+ const T& operator[](size_t index) const noexcept {
+ FASTFLOAT_DEBUG_ASSERT(index < length);
+ return ptr[index];
+ }
+};
+
+struct value128 {
+ uint64_t low;
+ uint64_t high;
+ value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {}
+ value128() : low(0), high(0) {}
+};
+
+/* result might be undefined when input_num is zero */
+fastfloat_really_inline int leading_zeroes(uint64_t input_num) {
+ assert(input_num > 0);
+#ifdef FASTFLOAT_VISUAL_STUDIO
+ #if defined(_M_X64) || defined(_M_ARM64)
+ unsigned long leading_zero = 0;
+ // Search the mask data from most significant bit (MSB)
+ // to least significant bit (LSB) for a set bit (1).
+ _BitScanReverse64(&leading_zero, input_num);
+ return (int)(63 - leading_zero);
+ #else
+ int last_bit = 0;
+ if(input_num & uint64_t(0xffffffff00000000)) input_num >>= 32, last_bit |= 32;
+ if(input_num & uint64_t( 0xffff0000)) input_num >>= 16, last_bit |= 16;
+ if(input_num & uint64_t( 0xff00)) input_num >>= 8, last_bit |= 8;
+ if(input_num & uint64_t( 0xf0)) input_num >>= 4, last_bit |= 4;
+ if(input_num & uint64_t( 0xc)) input_num >>= 2, last_bit |= 2;
+ if(input_num & uint64_t( 0x2)) input_num >>= 1, last_bit |= 1;
+ return 63 - last_bit;
+ #endif
+#else
+ return __builtin_clzll(input_num);
+#endif
+}
+
+#ifdef FASTFLOAT_32BIT
+
+// slow emulation routine for 32-bit
+fastfloat_really_inline uint64_t emulu(uint32_t x, uint32_t y) {
+ return x * (uint64_t)y;
+}
+
+// slow emulation routine for 32-bit
+#if !defined(__MINGW64__)
+fastfloat_really_inline uint64_t _umul128(uint64_t ab, uint64_t cd,
+ uint64_t *hi) {
+ uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd);
+ uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd);
+ uint64_t adbc = ad + emulu((uint32_t)ab, (uint32_t)(cd >> 32));
+ uint64_t adbc_carry = !!(adbc < ad);
+ uint64_t lo = bd + (adbc << 32);
+ *hi = emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) +
+ (adbc_carry << 32) + !!(lo < bd);
+ return lo;
+}
+#endif // !__MINGW64__
+
+#endif // FASTFLOAT_32BIT
+
+
+// compute 64-bit a*b
+fastfloat_really_inline value128 full_multiplication(uint64_t a,
+ uint64_t b) {
+ value128 answer;
+#ifdef _M_ARM64
+ // ARM64 has native support for 64-bit multiplications, no need to emulate
+ answer.high = __umulh(a, b);
+ answer.low = a * b;
+#elif defined(FASTFLOAT_32BIT) || (defined(_WIN64) && !defined(__clang__))
+ answer.low = _umul128(a, b, &answer.high); // _umul128 not available on ARM64
+#elif defined(FASTFLOAT_64BIT)
+ __uint128_t r = ((__uint128_t)a) * b;
+ answer.low = uint64_t(r);
+ answer.high = uint64_t(r >> 64);
+#else
+ #error Not implemented
+#endif
+ return answer;
+}
+
+struct adjusted_mantissa {
+ uint64_t mantissa{0};
+ int32_t power2{0}; // a negative value indicates an invalid result
+ adjusted_mantissa() = default;
+ bool operator==(const adjusted_mantissa &o) const {
+ return mantissa == o.mantissa && power2 == o.power2;
+ }
+ bool operator!=(const adjusted_mantissa &o) const {
+ return mantissa != o.mantissa || power2 != o.power2;
+ }
+};
+
+// Bias so we can get the real exponent with an invalid adjusted_mantissa.
+constexpr static int32_t invalid_am_bias = -0x8000;
+
+constexpr static double powers_of_ten_double[] = {
+ 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11,
+ 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22};
+constexpr static float powers_of_ten_float[] = {1e0, 1e1, 1e2, 1e3, 1e4, 1e5,
+ 1e6, 1e7, 1e8, 1e9, 1e10};
+
+template <typename T> struct binary_format {
+ using equiv_uint = typename std::conditional<sizeof(T) == 4, uint32_t, uint64_t>::type;
+
+ static inline constexpr int mantissa_explicit_bits();
+ static inline constexpr int minimum_exponent();
+ static inline constexpr int infinite_power();
+ static inline constexpr int sign_index();
+ static inline constexpr int min_exponent_fast_path();
+ static inline constexpr int max_exponent_fast_path();
+ static inline constexpr int max_exponent_round_to_even();
+ static inline constexpr int min_exponent_round_to_even();
+ static inline constexpr uint64_t max_mantissa_fast_path();
+ static inline constexpr int largest_power_of_ten();
+ static inline constexpr int smallest_power_of_ten();
+ static inline constexpr T exact_power_of_ten(int64_t power);
+ static inline constexpr size_t max_digits();
+ static inline constexpr equiv_uint exponent_mask();
+ static inline constexpr equiv_uint mantissa_mask();
+ static inline constexpr equiv_uint hidden_bit_mask();
+};
+
+template <> inline constexpr int binary_format<double>::mantissa_explicit_bits() {
+ return 52;
+}
+template <> inline constexpr int binary_format<float>::mantissa_explicit_bits() {
+ return 23;
+}
+
+template <> inline constexpr int binary_format<double>::max_exponent_round_to_even() {
+ return 23;
+}
+
+template <> inline constexpr int binary_format<float>::max_exponent_round_to_even() {
+ return 10;
+}
+
+template <> inline constexpr int binary_format<double>::min_exponent_round_to_even() {
+ return -4;
+}
+
+template <> inline constexpr int binary_format<float>::min_exponent_round_to_even() {
+ return -17;
+}
+
+template <> inline constexpr int binary_format<double>::minimum_exponent() {
+ return -1023;
+}
+template <> inline constexpr int binary_format<float>::minimum_exponent() {
+ return -127;
+}
+
+template <> inline constexpr int binary_format<double>::infinite_power() {
+ return 0x7FF;
+}
+template <> inline constexpr int binary_format<float>::infinite_power() {
+ return 0xFF;
+}
+
+template <> inline constexpr int binary_format<double>::sign_index() { return 63; }
+template <> inline constexpr int binary_format<float>::sign_index() { return 31; }
+
+template <> inline constexpr int binary_format<double>::min_exponent_fast_path() {
+#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
+ return 0;
+#else
+ return -22;
+#endif
+}
+template <> inline constexpr int binary_format<float>::min_exponent_fast_path() {
+#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
+ return 0;
+#else
+ return -10;
+#endif
+}
+
+template <> inline constexpr int binary_format<double>::max_exponent_fast_path() {
+ return 22;
+}
+template <> inline constexpr int binary_format<float>::max_exponent_fast_path() {
+ return 10;
+}
+
+template <> inline constexpr uint64_t binary_format<double>::max_mantissa_fast_path() {
+ return uint64_t(2) << mantissa_explicit_bits();
+}
+template <> inline constexpr uint64_t binary_format<float>::max_mantissa_fast_path() {
+ return uint64_t(2) << mantissa_explicit_bits();
+}
+
+template <>
+inline constexpr double binary_format<double>::exact_power_of_ten(int64_t power) {
+ return powers_of_ten_double[power];
+}
+template <>
+inline constexpr float binary_format<float>::exact_power_of_ten(int64_t power) {
+
+ return powers_of_ten_float[power];
+}
+
+
+template <>
+inline constexpr int binary_format<double>::largest_power_of_ten() {
+ return 308;
+}
+template <>
+inline constexpr int binary_format<float>::largest_power_of_ten() {
+ return 38;
+}
+
+template <>
+inline constexpr int binary_format<double>::smallest_power_of_ten() {
+ return -342;
+}
+template <>
+inline constexpr int binary_format<float>::smallest_power_of_ten() {
+ return -65;
+}
+
+template <> inline constexpr size_t binary_format<double>::max_digits() {
+ return 769;
+}
+template <> inline constexpr size_t binary_format<float>::max_digits() {
+ return 114;
+}
+
+template <> inline constexpr binary_format<float>::equiv_uint
+ binary_format<float>::exponent_mask() {
+ return 0x7F800000;
+}
+template <> inline constexpr binary_format<double>::equiv_uint
+ binary_format<double>::exponent_mask() {
+ return 0x7FF0000000000000;
+}
+
+template <> inline constexpr binary_format<float>::equiv_uint
+ binary_format<float>::mantissa_mask() {
+ return 0x007FFFFF;
+}
+template <> inline constexpr binary_format<double>::equiv_uint
+ binary_format<double>::mantissa_mask() {
+ return 0x000FFFFFFFFFFFFF;
+}
+
+template <> inline constexpr binary_format<float>::equiv_uint
+ binary_format<float>::hidden_bit_mask() {
+ return 0x00800000;
+}
+template <> inline constexpr binary_format<double>::equiv_uint
+ binary_format<double>::hidden_bit_mask() {
+ return 0x0010000000000000;
+}
+
+template<typename T>
+fastfloat_really_inline void to_float(bool negative, adjusted_mantissa am, T &value) {
+ uint64_t word = am.mantissa;
+ word |= uint64_t(am.power2) << binary_format<T>::mantissa_explicit_bits();
+ word = negative
+ ? word | (uint64_t(1) << binary_format<T>::sign_index()) : word;
+#if FASTFLOAT_IS_BIG_ENDIAN == 1
+ if (std::is_same<T, float>::value) {
+ ::memcpy(&value, (char *)&word + 4, sizeof(T)); // extract value at offset 4-7 if float on big-endian
+ } else {
+ ::memcpy(&value, &word, sizeof(T));
+ }
+#else
+ // For little-endian systems:
+ ::memcpy(&value, &word, sizeof(T));
+#endif
+}
+
+} // namespace fast_float
+
+#endif
+
+#ifndef FASTFLOAT_ASCII_NUMBER_H
+#define FASTFLOAT_ASCII_NUMBER_H
+
+#include <cctype>
+#include <cstdint>
+#include <cstring>
+#include <iterator>
+
+
+namespace fast_float {
+
+// Next function can be micro-optimized, but compilers are entirely
+// able to optimize it well.
+fastfloat_really_inline bool is_integer(char c) noexcept { return c >= '0' && c <= '9'; }
+
+fastfloat_really_inline uint64_t byteswap(uint64_t val) {
+ return (val & 0xFF00000000000000) >> 56
+ | (val & 0x00FF000000000000) >> 40
+ | (val & 0x0000FF0000000000) >> 24
+ | (val & 0x000000FF00000000) >> 8
+ | (val & 0x00000000FF000000) << 8
+ | (val & 0x0000000000FF0000) << 24
+ | (val & 0x000000000000FF00) << 40
+ | (val & 0x00000000000000FF) << 56;
+}
+
+fastfloat_really_inline uint64_t read_u64(const char *chars) {
+ uint64_t val;
+ ::memcpy(&val, chars, sizeof(uint64_t));
+#if FASTFLOAT_IS_BIG_ENDIAN == 1
+ // Need to read as-if the number was in little-endian order.
+ val = byteswap(val);
+#endif
+ return val;
+}
+
+fastfloat_really_inline void write_u64(uint8_t *chars, uint64_t val) {
+#if FASTFLOAT_IS_BIG_ENDIAN == 1
+ // Need to read as-if the number was in little-endian order.
+ val = byteswap(val);
+#endif
+ ::memcpy(chars, &val, sizeof(uint64_t));
+}
+
+// credit @aqrit
+fastfloat_really_inline uint32_t parse_eight_digits_unrolled(uint64_t val) {
+ const uint64_t mask = 0x000000FF000000FF;
+ const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
+ const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
+ val -= 0x3030303030303030;
+ val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8;
+ val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32;
+ return uint32_t(val);
+}
+
+fastfloat_really_inline uint32_t parse_eight_digits_unrolled(const char *chars) noexcept {
+ return parse_eight_digits_unrolled(read_u64(chars));
+}
+
+// credit @aqrit
+fastfloat_really_inline bool is_made_of_eight_digits_fast(uint64_t val) noexcept {
+ return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) &
+ 0x8080808080808080));
+}
+
+fastfloat_really_inline bool is_made_of_eight_digits_fast(const char *chars) noexcept {
+ return is_made_of_eight_digits_fast(read_u64(chars));
+}
+
+typedef span<const char> byte_span;
+
+struct parsed_number_string {
+ int64_t exponent{0};
+ uint64_t mantissa{0};
+ const char *lastmatch{nullptr};
+ bool negative{false};
+ bool valid{false};
+ bool too_many_digits{false};
+ // contains the range of the significant digits
+ byte_span integer{}; // non-nullable
+ byte_span fraction{}; // nullable
+};
+
+// Assuming that you use no more than 19 digits, this will
+// parse an ASCII string.
+fastfloat_really_inline
+parsed_number_string parse_number_string(const char *p, const char *pend, parse_options options) noexcept {
+ const chars_format fmt = options.format;
+ const char decimal_point = options.decimal_point;
+
+ parsed_number_string answer;
+ answer.valid = false;
+ answer.too_many_digits = false;
+ answer.negative = (*p == '-');
+ if (*p == '-') { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
+ ++p;
+ if (p == pend) {
+ return answer;
+ }
+ if (!is_integer(*p) && (*p != decimal_point)) { // a sign must be followed by an integer or the dot
+ return answer;
+ }
+ }
+ const char *const start_digits = p;
+
+ uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad)
+
+ while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) {
+ i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok
+ p += 8;
+ }
+ while ((p != pend) && is_integer(*p)) {
+ // a multiplication by 10 is cheaper than an arbitrary integer
+ // multiplication
+ i = 10 * i +
+ uint64_t(*p - '0'); // might overflow, we will handle the overflow later
+ ++p;
+ }
+ const char *const end_of_integer_part = p;
+ int64_t digit_count = int64_t(end_of_integer_part - start_digits);
+ answer.integer = byte_span(start_digits, size_t(digit_count));
+ int64_t exponent = 0;
+ if ((p != pend) && (*p == decimal_point)) {
+ ++p;
+ const char* before = p;
+ // can occur at most twice without overflowing, but let it occur more, since
+ // for integers with many digits, digit parsing is the primary bottleneck.
+ while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) {
+ i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok
+ p += 8;
+ }
+ while ((p != pend) && is_integer(*p)) {
+ uint8_t digit = uint8_t(*p - '0');
+ ++p;
+ i = i * 10 + digit; // in rare cases, this will overflow, but that's ok
+ }
+ exponent = before - p;
+ answer.fraction = byte_span(before, size_t(p - before));
+ digit_count -= exponent;
+ }
+ // we must have encountered at least one integer!
+ if (digit_count == 0) {
+ return answer;
+ }
+ int64_t exp_number = 0; // explicit exponential part
+ if ((fmt & chars_format::scientific) && (p != pend) && (('e' == *p) || ('E' == *p))) {
+ const char * location_of_e = p;
+ ++p;
+ bool neg_exp = false;
+ if ((p != pend) && ('-' == *p)) {
+ neg_exp = true;
+ ++p;
+ } else if ((p != pend) && ('+' == *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1)
+ ++p;
+ }
+ if ((p == pend) || !is_integer(*p)) {
+ if(!(fmt & chars_format::fixed)) {
+ // We are in error.
+ return answer;
+ }
+ // Otherwise, we will be ignoring the 'e'.
+ p = location_of_e;
+ } else {
+ while ((p != pend) && is_integer(*p)) {
+ uint8_t digit = uint8_t(*p - '0');
+ if (exp_number < 0x10000000) {
+ exp_number = 10 * exp_number + digit;
+ }
+ ++p;
+ }
+ if(neg_exp) { exp_number = - exp_number; }
+ exponent += exp_number;
+ }
+ } else {
+ // If it scientific and not fixed, we have to bail out.
+ if((fmt & chars_format::scientific) && !(fmt & chars_format::fixed)) { return answer; }
+ }
+ answer.lastmatch = p;
+ answer.valid = true;
+
+ // If we frequently had to deal with long strings of digits,
+ // we could extend our code by using a 128-bit integer instead
+ // of a 64-bit integer. However, this is uncommon.
+ //
+ // We can deal with up to 19 digits.
+ if (digit_count > 19) { // this is uncommon
+ // It is possible that the integer had an overflow.
+ // We have to handle the case where we have 0.0000somenumber.
+ // We need to be mindful of the case where we only have zeroes...
+ // E.g., 0.000000000...000.
+ const char *start = start_digits;
+ while ((start != pend) && (*start == '0' || *start == decimal_point)) {
+ if(*start == '0') { digit_count --; }
+ start++;
+ }
+ if (digit_count > 19) {
+ answer.too_many_digits = true;
+ // Let us start again, this time, avoiding overflows.
+ // We don't need to check if is_integer, since we use the
+ // pre-tokenized spans from above.
+ i = 0;
+ p = answer.integer.ptr;
+ const char* int_end = p + answer.integer.len();
+ const uint64_t minimal_nineteen_digit_integer{1000000000000000000};
+ while((i < minimal_nineteen_digit_integer) && (p != int_end)) {
+ i = i * 10 + uint64_t(*p - '0');
+ ++p;
+ }
+ if (i >= minimal_nineteen_digit_integer) { // We have a big integers
+ exponent = end_of_integer_part - p + exp_number;
+ } else { // We have a value with a fractional component.
+ p = answer.fraction.ptr;
+ const char* frac_end = p + answer.fraction.len();
+ while((i < minimal_nineteen_digit_integer) && (p != frac_end)) {
+ i = i * 10 + uint64_t(*p - '0');
+ ++p;
+ }
+ exponent = answer.fraction.ptr - p + exp_number;
+ }
+ // We have now corrected both exponent and i, to a truncated value
+ }
+ }
+ answer.exponent = exponent;
+ answer.mantissa = i;
+ return answer;
+}
+
+} // namespace fast_float
+
+#endif
+
+#ifndef FASTFLOAT_FAST_TABLE_H
+#define FASTFLOAT_FAST_TABLE_H
+
+#include <cstdint>
+
+namespace fast_float {
+
+/**
+ * When mapping numbers from decimal to binary,
+ * we go from w * 10^q to m * 2^p but we have
+ * 10^q = 5^q * 2^q, so effectively
+ * we are trying to match
+ * w * 2^q * 5^q to m * 2^p. Thus the powers of two
+ * are not a concern since they can be represented
+ * exactly using the binary notation, only the powers of five
+ * affect the binary significand.
+ */
+
+/**
+ * The smallest non-zero float (binary64) is 2^−1074.
+ * We take as input numbers of the form w x 10^q where w < 2^64.
+ * We have that w * 10^-343 < 2^(64-344) 5^-343 < 2^-1076.
+ * However, we have that
+ * (2^64-1) * 10^-342 = (2^64-1) * 2^-342 * 5^-342 > 2^−1074.
+ * Thus it is possible for a number of the form w * 10^-342 where
+ * w is a 64-bit value to be a non-zero floating-point number.
+ *********
+ * Any number of form w * 10^309 where w>= 1 is going to be
+ * infinite in binary64 so we never need to worry about powers
+ * of 5 greater than 308.
+ */
+template <class unused = void>
+struct powers_template {
+
+constexpr static int smallest_power_of_five = binary_format<double>::smallest_power_of_ten();
+constexpr static int largest_power_of_five = binary_format<double>::largest_power_of_ten();
+constexpr static int number_of_entries = 2 * (largest_power_of_five - smallest_power_of_five + 1);
+// Powers of five from 5^-342 all the way to 5^308 rounded toward one.
+static const uint64_t power_of_five_128[number_of_entries];
+};
+
+template <class unused>
+const uint64_t powers_template<unused>::power_of_five_128[number_of_entries] = {
+ 0xeef453d6923bd65a,0x113faa2906a13b3f,
+ 0x9558b4661b6565f8,0x4ac7ca59a424c507,
+ 0xbaaee17fa23ebf76,0x5d79bcf00d2df649,
+ 0xe95a99df8ace6f53,0xf4d82c2c107973dc,
+ 0x91d8a02bb6c10594,0x79071b9b8a4be869,
+ 0xb64ec836a47146f9,0x9748e2826cdee284,
+ 0xe3e27a444d8d98b7,0xfd1b1b2308169b25,
+ 0x8e6d8c6ab0787f72,0xfe30f0f5e50e20f7,
+ 0xb208ef855c969f4f,0xbdbd2d335e51a935,
+ 0xde8b2b66b3bc4723,0xad2c788035e61382,
+ 0x8b16fb203055ac76,0x4c3bcb5021afcc31,
+ 0xaddcb9e83c6b1793,0xdf4abe242a1bbf3d,
+ 0xd953e8624b85dd78,0xd71d6dad34a2af0d,
+ 0x87d4713d6f33aa6b,0x8672648c40e5ad68,
+ 0xa9c98d8ccb009506,0x680efdaf511f18c2,
+ 0xd43bf0effdc0ba48,0x212bd1b2566def2,
+ 0x84a57695fe98746d,0x14bb630f7604b57,
+ 0xa5ced43b7e3e9188,0x419ea3bd35385e2d,
+ 0xcf42894a5dce35ea,0x52064cac828675b9,
+ 0x818995ce7aa0e1b2,0x7343efebd1940993,
+ 0xa1ebfb4219491a1f,0x1014ebe6c5f90bf8,
+ 0xca66fa129f9b60a6,0xd41a26e077774ef6,
+ 0xfd00b897478238d0,0x8920b098955522b4,
+ 0x9e20735e8cb16382,0x55b46e5f5d5535b0,
+ 0xc5a890362fddbc62,0xeb2189f734aa831d,
+ 0xf712b443bbd52b7b,0xa5e9ec7501d523e4,
+ 0x9a6bb0aa55653b2d,0x47b233c92125366e,
+ 0xc1069cd4eabe89f8,0x999ec0bb696e840a,
+ 0xf148440a256e2c76,0xc00670ea43ca250d,
+ 0x96cd2a865764dbca,0x380406926a5e5728,
+ 0xbc807527ed3e12bc,0xc605083704f5ecf2,
+ 0xeba09271e88d976b,0xf7864a44c633682e,
+ 0x93445b8731587ea3,0x7ab3ee6afbe0211d,
+ 0xb8157268fdae9e4c,0x5960ea05bad82964,
+ 0xe61acf033d1a45df,0x6fb92487298e33bd,
+ 0x8fd0c16206306bab,0xa5d3b6d479f8e056,
+ 0xb3c4f1ba87bc8696,0x8f48a4899877186c,
+ 0xe0b62e2929aba83c,0x331acdabfe94de87,
+ 0x8c71dcd9ba0b4925,0x9ff0c08b7f1d0b14,
+ 0xaf8e5410288e1b6f,0x7ecf0ae5ee44dd9,
+ 0xdb71e91432b1a24a,0xc9e82cd9f69d6150,
+ 0x892731ac9faf056e,0xbe311c083a225cd2,
+ 0xab70fe17c79ac6ca,0x6dbd630a48aaf406,
+ 0xd64d3d9db981787d,0x92cbbccdad5b108,
+ 0x85f0468293f0eb4e,0x25bbf56008c58ea5,
+ 0xa76c582338ed2621,0xaf2af2b80af6f24e,
+ 0xd1476e2c07286faa,0x1af5af660db4aee1,
+ 0x82cca4db847945ca,0x50d98d9fc890ed4d,
+ 0xa37fce126597973c,0xe50ff107bab528a0,
+ 0xcc5fc196fefd7d0c,0x1e53ed49a96272c8,
+ 0xff77b1fcbebcdc4f,0x25e8e89c13bb0f7a,
+ 0x9faacf3df73609b1,0x77b191618c54e9ac,
+ 0xc795830d75038c1d,0xd59df5b9ef6a2417,
+ 0xf97ae3d0d2446f25,0x4b0573286b44ad1d,
+ 0x9becce62836ac577,0x4ee367f9430aec32,
+ 0xc2e801fb244576d5,0x229c41f793cda73f,
+ 0xf3a20279ed56d48a,0x6b43527578c1110f,
+ 0x9845418c345644d6,0x830a13896b78aaa9,
+ 0xbe5691ef416bd60c,0x23cc986bc656d553,
+ 0xedec366b11c6cb8f,0x2cbfbe86b7ec8aa8,
+ 0x94b3a202eb1c3f39,0x7bf7d71432f3d6a9,
+ 0xb9e08a83a5e34f07,0xdaf5ccd93fb0cc53,
+ 0xe858ad248f5c22c9,0xd1b3400f8f9cff68,
+ 0x91376c36d99995be,0x23100809b9c21fa1,
+ 0xb58547448ffffb2d,0xabd40a0c2832a78a,
+ 0xe2e69915b3fff9f9,0x16c90c8f323f516c,
+ 0x8dd01fad907ffc3b,0xae3da7d97f6792e3,
+ 0xb1442798f49ffb4a,0x99cd11cfdf41779c,
+ 0xdd95317f31c7fa1d,0x40405643d711d583,
+ 0x8a7d3eef7f1cfc52,0x482835ea666b2572,
+ 0xad1c8eab5ee43b66,0xda3243650005eecf,
+ 0xd863b256369d4a40,0x90bed43e40076a82,
+ 0x873e4f75e2224e68,0x5a7744a6e804a291,
+ 0xa90de3535aaae202,0x711515d0a205cb36,
+ 0xd3515c2831559a83,0xd5a5b44ca873e03,
+ 0x8412d9991ed58091,0xe858790afe9486c2,
+ 0xa5178fff668ae0b6,0x626e974dbe39a872,
+ 0xce5d73ff402d98e3,0xfb0a3d212dc8128f,
+ 0x80fa687f881c7f8e,0x7ce66634bc9d0b99,
+ 0xa139029f6a239f72,0x1c1fffc1ebc44e80,
+ 0xc987434744ac874e,0xa327ffb266b56220,
+ 0xfbe9141915d7a922,0x4bf1ff9f0062baa8,
+ 0x9d71ac8fada6c9b5,0x6f773fc3603db4a9,
+ 0xc4ce17b399107c22,0xcb550fb4384d21d3,
+ 0xf6019da07f549b2b,0x7e2a53a146606a48,
+ 0x99c102844f94e0fb,0x2eda7444cbfc426d,
+ 0xc0314325637a1939,0xfa911155fefb5308,
+ 0xf03d93eebc589f88,0x793555ab7eba27ca,
+ 0x96267c7535b763b5,0x4bc1558b2f3458de,
+ 0xbbb01b9283253ca2,0x9eb1aaedfb016f16,
+ 0xea9c227723ee8bcb,0x465e15a979c1cadc,
+ 0x92a1958a7675175f,0xbfacd89ec191ec9,
+ 0xb749faed14125d36,0xcef980ec671f667b,
+ 0xe51c79a85916f484,0x82b7e12780e7401a,
+ 0x8f31cc0937ae58d2,0xd1b2ecb8b0908810,
+ 0xb2fe3f0b8599ef07,0x861fa7e6dcb4aa15,
+ 0xdfbdcece67006ac9,0x67a791e093e1d49a,
+ 0x8bd6a141006042bd,0xe0c8bb2c5c6d24e0,
+ 0xaecc49914078536d,0x58fae9f773886e18,
+ 0xda7f5bf590966848,0xaf39a475506a899e,
+ 0x888f99797a5e012d,0x6d8406c952429603,
+ 0xaab37fd7d8f58178,0xc8e5087ba6d33b83,
+ 0xd5605fcdcf32e1d6,0xfb1e4a9a90880a64,
+ 0x855c3be0a17fcd26,0x5cf2eea09a55067f,
+ 0xa6b34ad8c9dfc06f,0xf42faa48c0ea481e,
+ 0xd0601d8efc57b08b,0xf13b94daf124da26,
+ 0x823c12795db6ce57,0x76c53d08d6b70858,
+ 0xa2cb1717b52481ed,0x54768c4b0c64ca6e,
+ 0xcb7ddcdda26da268,0xa9942f5dcf7dfd09,
+ 0xfe5d54150b090b02,0xd3f93b35435d7c4c,
+ 0x9efa548d26e5a6e1,0xc47bc5014a1a6daf,
+ 0xc6b8e9b0709f109a,0x359ab6419ca1091b,
+ 0xf867241c8cc6d4c0,0xc30163d203c94b62,
+ 0x9b407691d7fc44f8,0x79e0de63425dcf1d,
+ 0xc21094364dfb5636,0x985915fc12f542e4,
+ 0xf294b943e17a2bc4,0x3e6f5b7b17b2939d,
+ 0x979cf3ca6cec5b5a,0xa705992ceecf9c42,
+ 0xbd8430bd08277231,0x50c6ff782a838353,
+ 0xece53cec4a314ebd,0xa4f8bf5635246428,
+ 0x940f4613ae5ed136,0x871b7795e136be99,
+ 0xb913179899f68584,0x28e2557b59846e3f,
+ 0xe757dd7ec07426e5,0x331aeada2fe589cf,
+ 0x9096ea6f3848984f,0x3ff0d2c85def7621,
+ 0xb4bca50b065abe63,0xfed077a756b53a9,
+ 0xe1ebce4dc7f16dfb,0xd3e8495912c62894,
+ 0x8d3360f09cf6e4bd,0x64712dd7abbbd95c,
+ 0xb080392cc4349dec,0xbd8d794d96aacfb3,
+ 0xdca04777f541c567,0xecf0d7a0fc5583a0,
+ 0x89e42caaf9491b60,0xf41686c49db57244,
+ 0xac5d37d5b79b6239,0x311c2875c522ced5,
+ 0xd77485cb25823ac7,0x7d633293366b828b,
+ 0x86a8d39ef77164bc,0xae5dff9c02033197,
+ 0xa8530886b54dbdeb,0xd9f57f830283fdfc,
+ 0xd267caa862a12d66,0xd072df63c324fd7b,
+ 0x8380dea93da4bc60,0x4247cb9e59f71e6d,
+ 0xa46116538d0deb78,0x52d9be85f074e608,
+ 0xcd795be870516656,0x67902e276c921f8b,
+ 0x806bd9714632dff6,0xba1cd8a3db53b6,
+ 0xa086cfcd97bf97f3,0x80e8a40eccd228a4,
+ 0xc8a883c0fdaf7df0,0x6122cd128006b2cd,
+ 0xfad2a4b13d1b5d6c,0x796b805720085f81,
+ 0x9cc3a6eec6311a63,0xcbe3303674053bb0,
+ 0xc3f490aa77bd60fc,0xbedbfc4411068a9c,
+ 0xf4f1b4d515acb93b,0xee92fb5515482d44,
+ 0x991711052d8bf3c5,0x751bdd152d4d1c4a,
+ 0xbf5cd54678eef0b6,0xd262d45a78a0635d,
+ 0xef340a98172aace4,0x86fb897116c87c34,
+ 0x9580869f0e7aac0e,0xd45d35e6ae3d4da0,
+ 0xbae0a846d2195712,0x8974836059cca109,
+ 0xe998d258869facd7,0x2bd1a438703fc94b,
+ 0x91ff83775423cc06,0x7b6306a34627ddcf,
+ 0xb67f6455292cbf08,0x1a3bc84c17b1d542,
+ 0xe41f3d6a7377eeca,0x20caba5f1d9e4a93,
+ 0x8e938662882af53e,0x547eb47b7282ee9c,
+ 0xb23867fb2a35b28d,0xe99e619a4f23aa43,
+ 0xdec681f9f4c31f31,0x6405fa00e2ec94d4,
+ 0x8b3c113c38f9f37e,0xde83bc408dd3dd04,
+ 0xae0b158b4738705e,0x9624ab50b148d445,
+ 0xd98ddaee19068c76,0x3badd624dd9b0957,
+ 0x87f8a8d4cfa417c9,0xe54ca5d70a80e5d6,
+ 0xa9f6d30a038d1dbc,0x5e9fcf4ccd211f4c,
+ 0xd47487cc8470652b,0x7647c3200069671f,
+ 0x84c8d4dfd2c63f3b,0x29ecd9f40041e073,
+ 0xa5fb0a17c777cf09,0xf468107100525890,
+ 0xcf79cc9db955c2cc,0x7182148d4066eeb4,
+ 0x81ac1fe293d599bf,0xc6f14cd848405530,
+ 0xa21727db38cb002f,0xb8ada00e5a506a7c,
+ 0xca9cf1d206fdc03b,0xa6d90811f0e4851c,
+ 0xfd442e4688bd304a,0x908f4a166d1da663,
+ 0x9e4a9cec15763e2e,0x9a598e4e043287fe,
+ 0xc5dd44271ad3cdba,0x40eff1e1853f29fd,
+ 0xf7549530e188c128,0xd12bee59e68ef47c,
+ 0x9a94dd3e8cf578b9,0x82bb74f8301958ce,
+ 0xc13a148e3032d6e7,0xe36a52363c1faf01,
+ 0xf18899b1bc3f8ca1,0xdc44e6c3cb279ac1,
+ 0x96f5600f15a7b7e5,0x29ab103a5ef8c0b9,
+ 0xbcb2b812db11a5de,0x7415d448f6b6f0e7,
+ 0xebdf661791d60f56,0x111b495b3464ad21,
+ 0x936b9fcebb25c995,0xcab10dd900beec34,
+ 0xb84687c269ef3bfb,0x3d5d514f40eea742,
+ 0xe65829b3046b0afa,0xcb4a5a3112a5112,
+ 0x8ff71a0fe2c2e6dc,0x47f0e785eaba72ab,
+ 0xb3f4e093db73a093,0x59ed216765690f56,
+ 0xe0f218b8d25088b8,0x306869c13ec3532c,
+ 0x8c974f7383725573,0x1e414218c73a13fb,
+ 0xafbd2350644eeacf,0xe5d1929ef90898fa,
+ 0xdbac6c247d62a583,0xdf45f746b74abf39,
+ 0x894bc396ce5da772,0x6b8bba8c328eb783,
+ 0xab9eb47c81f5114f,0x66ea92f3f326564,
+ 0xd686619ba27255a2,0xc80a537b0efefebd,
+ 0x8613fd0145877585,0xbd06742ce95f5f36,
+ 0xa798fc4196e952e7,0x2c48113823b73704,
+ 0xd17f3b51fca3a7a0,0xf75a15862ca504c5,
+ 0x82ef85133de648c4,0x9a984d73dbe722fb,
+ 0xa3ab66580d5fdaf5,0xc13e60d0d2e0ebba,
+ 0xcc963fee10b7d1b3,0x318df905079926a8,
+ 0xffbbcfe994e5c61f,0xfdf17746497f7052,
+ 0x9fd561f1fd0f9bd3,0xfeb6ea8bedefa633,
+ 0xc7caba6e7c5382c8,0xfe64a52ee96b8fc0,
+ 0xf9bd690a1b68637b,0x3dfdce7aa3c673b0,
+ 0x9c1661a651213e2d,0x6bea10ca65c084e,
+ 0xc31bfa0fe5698db8,0x486e494fcff30a62,
+ 0xf3e2f893dec3f126,0x5a89dba3c3efccfa,
+ 0x986ddb5c6b3a76b7,0xf89629465a75e01c,
+ 0xbe89523386091465,0xf6bbb397f1135823,
+ 0xee2ba6c0678b597f,0x746aa07ded582e2c,
+ 0x94db483840b717ef,0xa8c2a44eb4571cdc,
+ 0xba121a4650e4ddeb,0x92f34d62616ce413,
+ 0xe896a0d7e51e1566,0x77b020baf9c81d17,
+ 0x915e2486ef32cd60,0xace1474dc1d122e,
+ 0xb5b5ada8aaff80b8,0xd819992132456ba,
+ 0xe3231912d5bf60e6,0x10e1fff697ed6c69,
+ 0x8df5efabc5979c8f,0xca8d3ffa1ef463c1,
+ 0xb1736b96b6fd83b3,0xbd308ff8a6b17cb2,
+ 0xddd0467c64bce4a0,0xac7cb3f6d05ddbde,
+ 0x8aa22c0dbef60ee4,0x6bcdf07a423aa96b,
+ 0xad4ab7112eb3929d,0x86c16c98d2c953c6,
+ 0xd89d64d57a607744,0xe871c7bf077ba8b7,
+ 0x87625f056c7c4a8b,0x11471cd764ad4972,
+ 0xa93af6c6c79b5d2d,0xd598e40d3dd89bcf,
+ 0xd389b47879823479,0x4aff1d108d4ec2c3,
+ 0x843610cb4bf160cb,0xcedf722a585139ba,
+ 0xa54394fe1eedb8fe,0xc2974eb4ee658828,
+ 0xce947a3da6a9273e,0x733d226229feea32,
+ 0x811ccc668829b887,0x806357d5a3f525f,
+ 0xa163ff802a3426a8,0xca07c2dcb0cf26f7,
+ 0xc9bcff6034c13052,0xfc89b393dd02f0b5,
+ 0xfc2c3f3841f17c67,0xbbac2078d443ace2,
+ 0x9d9ba7832936edc0,0xd54b944b84aa4c0d,
+ 0xc5029163f384a931,0xa9e795e65d4df11,
+ 0xf64335bcf065d37d,0x4d4617b5ff4a16d5,
+ 0x99ea0196163fa42e,0x504bced1bf8e4e45,
+ 0xc06481fb9bcf8d39,0xe45ec2862f71e1d6,
+ 0xf07da27a82c37088,0x5d767327bb4e5a4c,
+ 0x964e858c91ba2655,0x3a6a07f8d510f86f,
+ 0xbbe226efb628afea,0x890489f70a55368b,
+ 0xeadab0aba3b2dbe5,0x2b45ac74ccea842e,
+ 0x92c8ae6b464fc96f,0x3b0b8bc90012929d,
+ 0xb77ada0617e3bbcb,0x9ce6ebb40173744,
+ 0xe55990879ddcaabd,0xcc420a6a101d0515,
+ 0x8f57fa54c2a9eab6,0x9fa946824a12232d,
+ 0xb32df8e9f3546564,0x47939822dc96abf9,
+ 0xdff9772470297ebd,0x59787e2b93bc56f7,
+ 0x8bfbea76c619ef36,0x57eb4edb3c55b65a,
+ 0xaefae51477a06b03,0xede622920b6b23f1,
+ 0xdab99e59958885c4,0xe95fab368e45eced,
+ 0x88b402f7fd75539b,0x11dbcb0218ebb414,
+ 0xaae103b5fcd2a881,0xd652bdc29f26a119,
+ 0xd59944a37c0752a2,0x4be76d3346f0495f,
+ 0x857fcae62d8493a5,0x6f70a4400c562ddb,
+ 0xa6dfbd9fb8e5b88e,0xcb4ccd500f6bb952,
+ 0xd097ad07a71f26b2,0x7e2000a41346a7a7,
+ 0x825ecc24c873782f,0x8ed400668c0c28c8,
+ 0xa2f67f2dfa90563b,0x728900802f0f32fa,
+ 0xcbb41ef979346bca,0x4f2b40a03ad2ffb9,
+ 0xfea126b7d78186bc,0xe2f610c84987bfa8,
+ 0x9f24b832e6b0f436,0xdd9ca7d2df4d7c9,
+ 0xc6ede63fa05d3143,0x91503d1c79720dbb,
+ 0xf8a95fcf88747d94,0x75a44c6397ce912a,
+ 0x9b69dbe1b548ce7c,0xc986afbe3ee11aba,
+ 0xc24452da229b021b,0xfbe85badce996168,
+ 0xf2d56790ab41c2a2,0xfae27299423fb9c3,
+ 0x97c560ba6b0919a5,0xdccd879fc967d41a,
+ 0xbdb6b8e905cb600f,0x5400e987bbc1c920,
+ 0xed246723473e3813,0x290123e9aab23b68,
+ 0x9436c0760c86e30b,0xf9a0b6720aaf6521,
+ 0xb94470938fa89bce,0xf808e40e8d5b3e69,
+ 0xe7958cb87392c2c2,0xb60b1d1230b20e04,
+ 0x90bd77f3483bb9b9,0xb1c6f22b5e6f48c2,
+ 0xb4ecd5f01a4aa828,0x1e38aeb6360b1af3,
+ 0xe2280b6c20dd5232,0x25c6da63c38de1b0,
+ 0x8d590723948a535f,0x579c487e5a38ad0e,
+ 0xb0af48ec79ace837,0x2d835a9df0c6d851,
+ 0xdcdb1b2798182244,0xf8e431456cf88e65,
+ 0x8a08f0f8bf0f156b,0x1b8e9ecb641b58ff,
+ 0xac8b2d36eed2dac5,0xe272467e3d222f3f,
+ 0xd7adf884aa879177,0x5b0ed81dcc6abb0f,
+ 0x86ccbb52ea94baea,0x98e947129fc2b4e9,
+ 0xa87fea27a539e9a5,0x3f2398d747b36224,
+ 0xd29fe4b18e88640e,0x8eec7f0d19a03aad,
+ 0x83a3eeeef9153e89,0x1953cf68300424ac,
+ 0xa48ceaaab75a8e2b,0x5fa8c3423c052dd7,
+ 0xcdb02555653131b6,0x3792f412cb06794d,
+ 0x808e17555f3ebf11,0xe2bbd88bbee40bd0,
+ 0xa0b19d2ab70e6ed6,0x5b6aceaeae9d0ec4,
+ 0xc8de047564d20a8b,0xf245825a5a445275,
+ 0xfb158592be068d2e,0xeed6e2f0f0d56712,
+ 0x9ced737bb6c4183d,0x55464dd69685606b,
+ 0xc428d05aa4751e4c,0xaa97e14c3c26b886,
+ 0xf53304714d9265df,0xd53dd99f4b3066a8,
+ 0x993fe2c6d07b7fab,0xe546a8038efe4029,
+ 0xbf8fdb78849a5f96,0xde98520472bdd033,
+ 0xef73d256a5c0f77c,0x963e66858f6d4440,
+ 0x95a8637627989aad,0xdde7001379a44aa8,
+ 0xbb127c53b17ec159,0x5560c018580d5d52,
+ 0xe9d71b689dde71af,0xaab8f01e6e10b4a6,
+ 0x9226712162ab070d,0xcab3961304ca70e8,
+ 0xb6b00d69bb55c8d1,0x3d607b97c5fd0d22,
+ 0xe45c10c42a2b3b05,0x8cb89a7db77c506a,
+ 0x8eb98a7a9a5b04e3,0x77f3608e92adb242,
+ 0xb267ed1940f1c61c,0x55f038b237591ed3,
+ 0xdf01e85f912e37a3,0x6b6c46dec52f6688,
+ 0x8b61313bbabce2c6,0x2323ac4b3b3da015,
+ 0xae397d8aa96c1b77,0xabec975e0a0d081a,
+ 0xd9c7dced53c72255,0x96e7bd358c904a21,
+ 0x881cea14545c7575,0x7e50d64177da2e54,
+ 0xaa242499697392d2,0xdde50bd1d5d0b9e9,
+ 0xd4ad2dbfc3d07787,0x955e4ec64b44e864,
+ 0x84ec3c97da624ab4,0xbd5af13bef0b113e,
+ 0xa6274bbdd0fadd61,0xecb1ad8aeacdd58e,
+ 0xcfb11ead453994ba,0x67de18eda5814af2,
+ 0x81ceb32c4b43fcf4,0x80eacf948770ced7,
+ 0xa2425ff75e14fc31,0xa1258379a94d028d,
+ 0xcad2f7f5359a3b3e,0x96ee45813a04330,
+ 0xfd87b5f28300ca0d,0x8bca9d6e188853fc,
+ 0x9e74d1b791e07e48,0x775ea264cf55347e,
+ 0xc612062576589dda,0x95364afe032a819e,
+ 0xf79687aed3eec551,0x3a83ddbd83f52205,
+ 0x9abe14cd44753b52,0xc4926a9672793543,
+ 0xc16d9a0095928a27,0x75b7053c0f178294,
+ 0xf1c90080baf72cb1,0x5324c68b12dd6339,
+ 0x971da05074da7bee,0xd3f6fc16ebca5e04,
+ 0xbce5086492111aea,0x88f4bb1ca6bcf585,
+ 0xec1e4a7db69561a5,0x2b31e9e3d06c32e6,
+ 0x9392ee8e921d5d07,0x3aff322e62439fd0,
+ 0xb877aa3236a4b449,0x9befeb9fad487c3,
+ 0xe69594bec44de15b,0x4c2ebe687989a9b4,
+ 0x901d7cf73ab0acd9,0xf9d37014bf60a11,
+ 0xb424dc35095cd80f,0x538484c19ef38c95,
+ 0xe12e13424bb40e13,0x2865a5f206b06fba,
+ 0x8cbccc096f5088cb,0xf93f87b7442e45d4,
+ 0xafebff0bcb24aafe,0xf78f69a51539d749,
+ 0xdbe6fecebdedd5be,0xb573440e5a884d1c,
+ 0x89705f4136b4a597,0x31680a88f8953031,
+ 0xabcc77118461cefc,0xfdc20d2b36ba7c3e,
+ 0xd6bf94d5e57a42bc,0x3d32907604691b4d,
+ 0x8637bd05af6c69b5,0xa63f9a49c2c1b110,
+ 0xa7c5ac471b478423,0xfcf80dc33721d54,
+ 0xd1b71758e219652b,0xd3c36113404ea4a9,
+ 0x83126e978d4fdf3b,0x645a1cac083126ea,
+ 0xa3d70a3d70a3d70a,0x3d70a3d70a3d70a4,
+ 0xcccccccccccccccc,0xcccccccccccccccd,
+ 0x8000000000000000,0x0,
+ 0xa000000000000000,0x0,
+ 0xc800000000000000,0x0,
+ 0xfa00000000000000,0x0,
+ 0x9c40000000000000,0x0,
+ 0xc350000000000000,0x0,
+ 0xf424000000000000,0x0,
+ 0x9896800000000000,0x0,
+ 0xbebc200000000000,0x0,
+ 0xee6b280000000000,0x0,
+ 0x9502f90000000000,0x0,
+ 0xba43b74000000000,0x0,
+ 0xe8d4a51000000000,0x0,
+ 0x9184e72a00000000,0x0,
+ 0xb5e620f480000000,0x0,
+ 0xe35fa931a0000000,0x0,
+ 0x8e1bc9bf04000000,0x0,
+ 0xb1a2bc2ec5000000,0x0,
+ 0xde0b6b3a76400000,0x0,
+ 0x8ac7230489e80000,0x0,
+ 0xad78ebc5ac620000,0x0,
+ 0xd8d726b7177a8000,0x0,
+ 0x878678326eac9000,0x0,
+ 0xa968163f0a57b400,0x0,
+ 0xd3c21bcecceda100,0x0,
+ 0x84595161401484a0,0x0,
+ 0xa56fa5b99019a5c8,0x0,
+ 0xcecb8f27f4200f3a,0x0,
+ 0x813f3978f8940984,0x4000000000000000,
+ 0xa18f07d736b90be5,0x5000000000000000,
+ 0xc9f2c9cd04674ede,0xa400000000000000,
+ 0xfc6f7c4045812296,0x4d00000000000000,
+ 0x9dc5ada82b70b59d,0xf020000000000000,
+ 0xc5371912364ce305,0x6c28000000000000,
+ 0xf684df56c3e01bc6,0xc732000000000000,
+ 0x9a130b963a6c115c,0x3c7f400000000000,
+ 0xc097ce7bc90715b3,0x4b9f100000000000,
+ 0xf0bdc21abb48db20,0x1e86d40000000000,
+ 0x96769950b50d88f4,0x1314448000000000,
+ 0xbc143fa4e250eb31,0x17d955a000000000,
+ 0xeb194f8e1ae525fd,0x5dcfab0800000000,
+ 0x92efd1b8d0cf37be,0x5aa1cae500000000,
+ 0xb7abc627050305ad,0xf14a3d9e40000000,
+ 0xe596b7b0c643c719,0x6d9ccd05d0000000,
+ 0x8f7e32ce7bea5c6f,0xe4820023a2000000,
+ 0xb35dbf821ae4f38b,0xdda2802c8a800000,
+ 0xe0352f62a19e306e,0xd50b2037ad200000,
+ 0x8c213d9da502de45,0x4526f422cc340000,
+ 0xaf298d050e4395d6,0x9670b12b7f410000,
+ 0xdaf3f04651d47b4c,0x3c0cdd765f114000,
+ 0x88d8762bf324cd0f,0xa5880a69fb6ac800,
+ 0xab0e93b6efee0053,0x8eea0d047a457a00,
+ 0xd5d238a4abe98068,0x72a4904598d6d880,
+ 0x85a36366eb71f041,0x47a6da2b7f864750,
+ 0xa70c3c40a64e6c51,0x999090b65f67d924,
+ 0xd0cf4b50cfe20765,0xfff4b4e3f741cf6d,
+ 0x82818f1281ed449f,0xbff8f10e7a8921a4,
+ 0xa321f2d7226895c7,0xaff72d52192b6a0d,
+ 0xcbea6f8ceb02bb39,0x9bf4f8a69f764490,
+ 0xfee50b7025c36a08,0x2f236d04753d5b4,
+ 0x9f4f2726179a2245,0x1d762422c946590,
+ 0xc722f0ef9d80aad6,0x424d3ad2b7b97ef5,
+ 0xf8ebad2b84e0d58b,0xd2e0898765a7deb2,
+ 0x9b934c3b330c8577,0x63cc55f49f88eb2f,
+ 0xc2781f49ffcfa6d5,0x3cbf6b71c76b25fb,
+ 0xf316271c7fc3908a,0x8bef464e3945ef7a,
+ 0x97edd871cfda3a56,0x97758bf0e3cbb5ac,
+ 0xbde94e8e43d0c8ec,0x3d52eeed1cbea317,
+ 0xed63a231d4c4fb27,0x4ca7aaa863ee4bdd,
+ 0x945e455f24fb1cf8,0x8fe8caa93e74ef6a,
+ 0xb975d6b6ee39e436,0xb3e2fd538e122b44,
+ 0xe7d34c64a9c85d44,0x60dbbca87196b616,
+ 0x90e40fbeea1d3a4a,0xbc8955e946fe31cd,
+ 0xb51d13aea4a488dd,0x6babab6398bdbe41,
+ 0xe264589a4dcdab14,0xc696963c7eed2dd1,
+ 0x8d7eb76070a08aec,0xfc1e1de5cf543ca2,
+ 0xb0de65388cc8ada8,0x3b25a55f43294bcb,
+ 0xdd15fe86affad912,0x49ef0eb713f39ebe,
+ 0x8a2dbf142dfcc7ab,0x6e3569326c784337,
+ 0xacb92ed9397bf996,0x49c2c37f07965404,
+ 0xd7e77a8f87daf7fb,0xdc33745ec97be906,
+ 0x86f0ac99b4e8dafd,0x69a028bb3ded71a3,
+ 0xa8acd7c0222311bc,0xc40832ea0d68ce0c,
+ 0xd2d80db02aabd62b,0xf50a3fa490c30190,
+ 0x83c7088e1aab65db,0x792667c6da79e0fa,
+ 0xa4b8cab1a1563f52,0x577001b891185938,
+ 0xcde6fd5e09abcf26,0xed4c0226b55e6f86,
+ 0x80b05e5ac60b6178,0x544f8158315b05b4,
+ 0xa0dc75f1778e39d6,0x696361ae3db1c721,
+ 0xc913936dd571c84c,0x3bc3a19cd1e38e9,
+ 0xfb5878494ace3a5f,0x4ab48a04065c723,
+ 0x9d174b2dcec0e47b,0x62eb0d64283f9c76,
+ 0xc45d1df942711d9a,0x3ba5d0bd324f8394,
+ 0xf5746577930d6500,0xca8f44ec7ee36479,
+ 0x9968bf6abbe85f20,0x7e998b13cf4e1ecb,
+ 0xbfc2ef456ae276e8,0x9e3fedd8c321a67e,
+ 0xefb3ab16c59b14a2,0xc5cfe94ef3ea101e,
+ 0x95d04aee3b80ece5,0xbba1f1d158724a12,
+ 0xbb445da9ca61281f,0x2a8a6e45ae8edc97,
+ 0xea1575143cf97226,0xf52d09d71a3293bd,
+ 0x924d692ca61be758,0x593c2626705f9c56,
+ 0xb6e0c377cfa2e12e,0x6f8b2fb00c77836c,
+ 0xe498f455c38b997a,0xb6dfb9c0f956447,
+ 0x8edf98b59a373fec,0x4724bd4189bd5eac,
+ 0xb2977ee300c50fe7,0x58edec91ec2cb657,
+ 0xdf3d5e9bc0f653e1,0x2f2967b66737e3ed,
+ 0x8b865b215899f46c,0xbd79e0d20082ee74,
+ 0xae67f1e9aec07187,0xecd8590680a3aa11,
+ 0xda01ee641a708de9,0xe80e6f4820cc9495,
+ 0x884134fe908658b2,0x3109058d147fdcdd,
+ 0xaa51823e34a7eede,0xbd4b46f0599fd415,
+ 0xd4e5e2cdc1d1ea96,0x6c9e18ac7007c91a,
+ 0x850fadc09923329e,0x3e2cf6bc604ddb0,
+ 0xa6539930bf6bff45,0x84db8346b786151c,
+ 0xcfe87f7cef46ff16,0xe612641865679a63,
+ 0x81f14fae158c5f6e,0x4fcb7e8f3f60c07e,
+ 0xa26da3999aef7749,0xe3be5e330f38f09d,
+ 0xcb090c8001ab551c,0x5cadf5bfd3072cc5,
+ 0xfdcb4fa002162a63,0x73d9732fc7c8f7f6,
+ 0x9e9f11c4014dda7e,0x2867e7fddcdd9afa,
+ 0xc646d63501a1511d,0xb281e1fd541501b8,
+ 0xf7d88bc24209a565,0x1f225a7ca91a4226,
+ 0x9ae757596946075f,0x3375788de9b06958,
+ 0xc1a12d2fc3978937,0x52d6b1641c83ae,
+ 0xf209787bb47d6b84,0xc0678c5dbd23a49a,
+ 0x9745eb4d50ce6332,0xf840b7ba963646e0,
+ 0xbd176620a501fbff,0xb650e5a93bc3d898,
+ 0xec5d3fa8ce427aff,0xa3e51f138ab4cebe,
+ 0x93ba47c980e98cdf,0xc66f336c36b10137,
+ 0xb8a8d9bbe123f017,0xb80b0047445d4184,
+ 0xe6d3102ad96cec1d,0xa60dc059157491e5,
+ 0x9043ea1ac7e41392,0x87c89837ad68db2f,
+ 0xb454e4a179dd1877,0x29babe4598c311fb,
+ 0xe16a1dc9d8545e94,0xf4296dd6fef3d67a,
+ 0x8ce2529e2734bb1d,0x1899e4a65f58660c,
+ 0xb01ae745b101e9e4,0x5ec05dcff72e7f8f,
+ 0xdc21a1171d42645d,0x76707543f4fa1f73,
+ 0x899504ae72497eba,0x6a06494a791c53a8,
+ 0xabfa45da0edbde69,0x487db9d17636892,
+ 0xd6f8d7509292d603,0x45a9d2845d3c42b6,
+ 0x865b86925b9bc5c2,0xb8a2392ba45a9b2,
+ 0xa7f26836f282b732,0x8e6cac7768d7141e,
+ 0xd1ef0244af2364ff,0x3207d795430cd926,
+ 0x8335616aed761f1f,0x7f44e6bd49e807b8,
+ 0xa402b9c5a8d3a6e7,0x5f16206c9c6209a6,
+ 0xcd036837130890a1,0x36dba887c37a8c0f,
+ 0x802221226be55a64,0xc2494954da2c9789,
+ 0xa02aa96b06deb0fd,0xf2db9baa10b7bd6c,
+ 0xc83553c5c8965d3d,0x6f92829494e5acc7,
+ 0xfa42a8b73abbf48c,0xcb772339ba1f17f9,
+ 0x9c69a97284b578d7,0xff2a760414536efb,
+ 0xc38413cf25e2d70d,0xfef5138519684aba,
+ 0xf46518c2ef5b8cd1,0x7eb258665fc25d69,
+ 0x98bf2f79d5993802,0xef2f773ffbd97a61,
+ 0xbeeefb584aff8603,0xaafb550ffacfd8fa,
+ 0xeeaaba2e5dbf6784,0x95ba2a53f983cf38,
+ 0x952ab45cfa97a0b2,0xdd945a747bf26183,
+ 0xba756174393d88df,0x94f971119aeef9e4,
+ 0xe912b9d1478ceb17,0x7a37cd5601aab85d,
+ 0x91abb422ccb812ee,0xac62e055c10ab33a,
+ 0xb616a12b7fe617aa,0x577b986b314d6009,
+ 0xe39c49765fdf9d94,0xed5a7e85fda0b80b,
+ 0x8e41ade9fbebc27d,0x14588f13be847307,
+ 0xb1d219647ae6b31c,0x596eb2d8ae258fc8,
+ 0xde469fbd99a05fe3,0x6fca5f8ed9aef3bb,
+ 0x8aec23d680043bee,0x25de7bb9480d5854,
+ 0xada72ccc20054ae9,0xaf561aa79a10ae6a,
+ 0xd910f7ff28069da4,0x1b2ba1518094da04,
+ 0x87aa9aff79042286,0x90fb44d2f05d0842,
+ 0xa99541bf57452b28,0x353a1607ac744a53,
+ 0xd3fa922f2d1675f2,0x42889b8997915ce8,
+ 0x847c9b5d7c2e09b7,0x69956135febada11,
+ 0xa59bc234db398c25,0x43fab9837e699095,
+ 0xcf02b2c21207ef2e,0x94f967e45e03f4bb,
+ 0x8161afb94b44f57d,0x1d1be0eebac278f5,
+ 0xa1ba1ba79e1632dc,0x6462d92a69731732,
+ 0xca28a291859bbf93,0x7d7b8f7503cfdcfe,
+ 0xfcb2cb35e702af78,0x5cda735244c3d43e,
+ 0x9defbf01b061adab,0x3a0888136afa64a7,
+ 0xc56baec21c7a1916,0x88aaa1845b8fdd0,
+ 0xf6c69a72a3989f5b,0x8aad549e57273d45,
+ 0x9a3c2087a63f6399,0x36ac54e2f678864b,
+ 0xc0cb28a98fcf3c7f,0x84576a1bb416a7dd,
+ 0xf0fdf2d3f3c30b9f,0x656d44a2a11c51d5,
+ 0x969eb7c47859e743,0x9f644ae5a4b1b325,
+ 0xbc4665b596706114,0x873d5d9f0dde1fee,
+ 0xeb57ff22fc0c7959,0xa90cb506d155a7ea,
+ 0x9316ff75dd87cbd8,0x9a7f12442d588f2,
+ 0xb7dcbf5354e9bece,0xc11ed6d538aeb2f,
+ 0xe5d3ef282a242e81,0x8f1668c8a86da5fa,
+ 0x8fa475791a569d10,0xf96e017d694487bc,
+ 0xb38d92d760ec4455,0x37c981dcc395a9ac,
+ 0xe070f78d3927556a,0x85bbe253f47b1417,
+ 0x8c469ab843b89562,0x93956d7478ccec8e,
+ 0xaf58416654a6babb,0x387ac8d1970027b2,
+ 0xdb2e51bfe9d0696a,0x6997b05fcc0319e,
+ 0x88fcf317f22241e2,0x441fece3bdf81f03,
+ 0xab3c2fddeeaad25a,0xd527e81cad7626c3,
+ 0xd60b3bd56a5586f1,0x8a71e223d8d3b074,
+ 0x85c7056562757456,0xf6872d5667844e49,
+ 0xa738c6bebb12d16c,0xb428f8ac016561db,
+ 0xd106f86e69d785c7,0xe13336d701beba52,
+ 0x82a45b450226b39c,0xecc0024661173473,
+ 0xa34d721642b06084,0x27f002d7f95d0190,
+ 0xcc20ce9bd35c78a5,0x31ec038df7b441f4,
+ 0xff290242c83396ce,0x7e67047175a15271,
+ 0x9f79a169bd203e41,0xf0062c6e984d386,
+ 0xc75809c42c684dd1,0x52c07b78a3e60868,
+ 0xf92e0c3537826145,0xa7709a56ccdf8a82,
+ 0x9bbcc7a142b17ccb,0x88a66076400bb691,
+ 0xc2abf989935ddbfe,0x6acff893d00ea435,
+ 0xf356f7ebf83552fe,0x583f6b8c4124d43,
+ 0x98165af37b2153de,0xc3727a337a8b704a,
+ 0xbe1bf1b059e9a8d6,0x744f18c0592e4c5c,
+ 0xeda2ee1c7064130c,0x1162def06f79df73,
+ 0x9485d4d1c63e8be7,0x8addcb5645ac2ba8,
+ 0xb9a74a0637ce2ee1,0x6d953e2bd7173692,
+ 0xe8111c87c5c1ba99,0xc8fa8db6ccdd0437,
+ 0x910ab1d4db9914a0,0x1d9c9892400a22a2,
+ 0xb54d5e4a127f59c8,0x2503beb6d00cab4b,
+ 0xe2a0b5dc971f303a,0x2e44ae64840fd61d,
+ 0x8da471a9de737e24,0x5ceaecfed289e5d2,
+ 0xb10d8e1456105dad,0x7425a83e872c5f47,
+ 0xdd50f1996b947518,0xd12f124e28f77719,
+ 0x8a5296ffe33cc92f,0x82bd6b70d99aaa6f,
+ 0xace73cbfdc0bfb7b,0x636cc64d1001550b,
+ 0xd8210befd30efa5a,0x3c47f7e05401aa4e,
+ 0x8714a775e3e95c78,0x65acfaec34810a71,
+ 0xa8d9d1535ce3b396,0x7f1839a741a14d0d,
+ 0xd31045a8341ca07c,0x1ede48111209a050,
+ 0x83ea2b892091e44d,0x934aed0aab460432,
+ 0xa4e4b66b68b65d60,0xf81da84d5617853f,
+ 0xce1de40642e3f4b9,0x36251260ab9d668e,
+ 0x80d2ae83e9ce78f3,0xc1d72b7c6b426019,
+ 0xa1075a24e4421730,0xb24cf65b8612f81f,
+ 0xc94930ae1d529cfc,0xdee033f26797b627,
+ 0xfb9b7cd9a4a7443c,0x169840ef017da3b1,
+ 0x9d412e0806e88aa5,0x8e1f289560ee864e,
+ 0xc491798a08a2ad4e,0xf1a6f2bab92a27e2,
+ 0xf5b5d7ec8acb58a2,0xae10af696774b1db,
+ 0x9991a6f3d6bf1765,0xacca6da1e0a8ef29,
+ 0xbff610b0cc6edd3f,0x17fd090a58d32af3,
+ 0xeff394dcff8a948e,0xddfc4b4cef07f5b0,
+ 0x95f83d0a1fb69cd9,0x4abdaf101564f98e,
+ 0xbb764c4ca7a4440f,0x9d6d1ad41abe37f1,
+ 0xea53df5fd18d5513,0x84c86189216dc5ed,
+ 0x92746b9be2f8552c,0x32fd3cf5b4e49bb4,
+ 0xb7118682dbb66a77,0x3fbc8c33221dc2a1,
+ 0xe4d5e82392a40515,0xfabaf3feaa5334a,
+ 0x8f05b1163ba6832d,0x29cb4d87f2a7400e,
+ 0xb2c71d5bca9023f8,0x743e20e9ef511012,
+ 0xdf78e4b2bd342cf6,0x914da9246b255416,
+ 0x8bab8eefb6409c1a,0x1ad089b6c2f7548e,
+ 0xae9672aba3d0c320,0xa184ac2473b529b1,
+ 0xda3c0f568cc4f3e8,0xc9e5d72d90a2741e,
+ 0x8865899617fb1871,0x7e2fa67c7a658892,
+ 0xaa7eebfb9df9de8d,0xddbb901b98feeab7,
+ 0xd51ea6fa85785631,0x552a74227f3ea565,
+ 0x8533285c936b35de,0xd53a88958f87275f,
+ 0xa67ff273b8460356,0x8a892abaf368f137,
+ 0xd01fef10a657842c,0x2d2b7569b0432d85,
+ 0x8213f56a67f6b29b,0x9c3b29620e29fc73,
+ 0xa298f2c501f45f42,0x8349f3ba91b47b8f,
+ 0xcb3f2f7642717713,0x241c70a936219a73,
+ 0xfe0efb53d30dd4d7,0xed238cd383aa0110,
+ 0x9ec95d1463e8a506,0xf4363804324a40aa,
+ 0xc67bb4597ce2ce48,0xb143c6053edcd0d5,
+ 0xf81aa16fdc1b81da,0xdd94b7868e94050a,
+ 0x9b10a4e5e9913128,0xca7cf2b4191c8326,
+ 0xc1d4ce1f63f57d72,0xfd1c2f611f63a3f0,
+ 0xf24a01a73cf2dccf,0xbc633b39673c8cec,
+ 0x976e41088617ca01,0xd5be0503e085d813,
+ 0xbd49d14aa79dbc82,0x4b2d8644d8a74e18,
+ 0xec9c459d51852ba2,0xddf8e7d60ed1219e,
+ 0x93e1ab8252f33b45,0xcabb90e5c942b503,
+ 0xb8da1662e7b00a17,0x3d6a751f3b936243,
+ 0xe7109bfba19c0c9d,0xcc512670a783ad4,
+ 0x906a617d450187e2,0x27fb2b80668b24c5,
+ 0xb484f9dc9641e9da,0xb1f9f660802dedf6,
+ 0xe1a63853bbd26451,0x5e7873f8a0396973,
+ 0x8d07e33455637eb2,0xdb0b487b6423e1e8,
+ 0xb049dc016abc5e5f,0x91ce1a9a3d2cda62,
+ 0xdc5c5301c56b75f7,0x7641a140cc7810fb,
+ 0x89b9b3e11b6329ba,0xa9e904c87fcb0a9d,
+ 0xac2820d9623bf429,0x546345fa9fbdcd44,
+ 0xd732290fbacaf133,0xa97c177947ad4095,
+ 0x867f59a9d4bed6c0,0x49ed8eabcccc485d,
+ 0xa81f301449ee8c70,0x5c68f256bfff5a74,
+ 0xd226fc195c6a2f8c,0x73832eec6fff3111,
+ 0x83585d8fd9c25db7,0xc831fd53c5ff7eab,
+ 0xa42e74f3d032f525,0xba3e7ca8b77f5e55,
+ 0xcd3a1230c43fb26f,0x28ce1bd2e55f35eb,
+ 0x80444b5e7aa7cf85,0x7980d163cf5b81b3,
+ 0xa0555e361951c366,0xd7e105bcc332621f,
+ 0xc86ab5c39fa63440,0x8dd9472bf3fefaa7,
+ 0xfa856334878fc150,0xb14f98f6f0feb951,
+ 0x9c935e00d4b9d8d2,0x6ed1bf9a569f33d3,
+ 0xc3b8358109e84f07,0xa862f80ec4700c8,
+ 0xf4a642e14c6262c8,0xcd27bb612758c0fa,
+ 0x98e7e9cccfbd7dbd,0x8038d51cb897789c,
+ 0xbf21e44003acdd2c,0xe0470a63e6bd56c3,
+ 0xeeea5d5004981478,0x1858ccfce06cac74,
+ 0x95527a5202df0ccb,0xf37801e0c43ebc8,
+ 0xbaa718e68396cffd,0xd30560258f54e6ba,
+ 0xe950df20247c83fd,0x47c6b82ef32a2069,
+ 0x91d28b7416cdd27e,0x4cdc331d57fa5441,
+ 0xb6472e511c81471d,0xe0133fe4adf8e952,
+ 0xe3d8f9e563a198e5,0x58180fddd97723a6,
+ 0x8e679c2f5e44ff8f,0x570f09eaa7ea7648,};
+using powers = powers_template<>;
+
+}
+
+#endif
+
+#ifndef FASTFLOAT_DECIMAL_TO_BINARY_H
+#define FASTFLOAT_DECIMAL_TO_BINARY_H
+
+#include <cfloat>
+#include <cinttypes>
+#include <cmath>
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+
+namespace fast_float {
+
+// This will compute or rather approximate w * 5**q and return a pair of 64-bit words approximating
+// the result, with the "high" part corresponding to the most significant bits and the
+// low part corresponding to the least significant bits.
+//
+template <int bit_precision>
+fastfloat_really_inline
+value128 compute_product_approximation(int64_t q, uint64_t w) {
+ const int index = 2 * int(q - powers::smallest_power_of_five);
+ // For small values of q, e.g., q in [0,27], the answer is always exact because
+ // The line value128 firstproduct = full_multiplication(w, power_of_five_128[index]);
+ // gives the exact answer.
+ value128 firstproduct = full_multiplication(w, powers::power_of_five_128[index]);
+ static_assert((bit_precision >= 0) && (bit_precision <= 64), " precision should be in (0,64]");
+ constexpr uint64_t precision_mask = (bit_precision < 64) ?
+ (uint64_t(0xFFFFFFFFFFFFFFFF) >> bit_precision)
+ : uint64_t(0xFFFFFFFFFFFFFFFF);
+ if((firstproduct.high & precision_mask) == precision_mask) { // could further guard with (lower + w < lower)
+ // regarding the second product, we only need secondproduct.high, but our expectation is that the compiler will optimize this extra work away if needed.
+ value128 secondproduct = full_multiplication(w, powers::power_of_five_128[index + 1]);
+ firstproduct.low += secondproduct.high;
+ if(secondproduct.high > firstproduct.low) {
+ firstproduct.high++;
+ }
+ }
+ return firstproduct;
+}
+
+namespace detail {
+/**
+ * For q in (0,350), we have that
+ * f = (((152170 + 65536) * q ) >> 16);
+ * is equal to
+ * floor(p) + q
+ * where
+ * p = log(5**q)/log(2) = q * log(5)/log(2)
+ *
+ * For negative values of q in (-400,0), we have that
+ * f = (((152170 + 65536) * q ) >> 16);
+ * is equal to
+ * -ceil(p) + q
+ * where
+ * p = log(5**-q)/log(2) = -q * log(5)/log(2)
+ */
+ constexpr fastfloat_really_inline int32_t power(int32_t q) noexcept {
+ return (((152170 + 65536) * q) >> 16) + 63;
+ }
+} // namespace detail
+
+// create an adjusted mantissa, biased by the invalid power2
+// for significant digits already multiplied by 10 ** q.
+template <typename binary>
+fastfloat_really_inline
+adjusted_mantissa compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept {
+ int hilz = int(w >> 63) ^ 1;
+ adjusted_mantissa answer;
+ answer.mantissa = w << hilz;
+ int bias = binary::mantissa_explicit_bits() - binary::minimum_exponent();
+ answer.power2 = int32_t(detail::power(int32_t(q)) + bias - hilz - lz - 62 + invalid_am_bias);
+ return answer;
+}
+
+// w * 10 ** q, without rounding the representation up.
+// the power2 in the exponent will be adjusted by invalid_am_bias.
+template <typename binary>
+fastfloat_really_inline
+adjusted_mantissa compute_error(int64_t q, uint64_t w) noexcept {
+ int lz = leading_zeroes(w);
+ w <<= lz;
+ value128 product = compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w);
+ return compute_error_scaled<binary>(q, product.high, lz);
+}
+
+// w * 10 ** q
+// The returned value should be a valid ieee64 number that simply need to be packed.
+// However, in some very rare cases, the computation will fail. In such cases, we
+// return an adjusted_mantissa with a negative power of 2: the caller should recompute
+// in such cases.
+template <typename binary>
+fastfloat_really_inline
+adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept {
+ adjusted_mantissa answer;
+ if ((w == 0) || (q < binary::smallest_power_of_ten())) {
+ answer.power2 = 0;
+ answer.mantissa = 0;
+ // result should be zero
+ return answer;
+ }
+ if (q > binary::largest_power_of_ten()) {
+ // we want to get infinity:
+ answer.power2 = binary::infinite_power();
+ answer.mantissa = 0;
+ return answer;
+ }
+ // At this point in time q is in [powers::smallest_power_of_five, powers::largest_power_of_five].
+
+ // We want the most significant bit of i to be 1. Shift if needed.
+ int lz = leading_zeroes(w);
+ w <<= lz;
+
+ // The required precision is binary::mantissa_explicit_bits() + 3 because
+ // 1. We need the implicit bit
+ // 2. We need an extra bit for rounding purposes
+ // 3. We might lose a bit due to the "upperbit" routine (result too small, requiring a shift)
+
+ value128 product = compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w);
+ if(product.low == 0xFFFFFFFFFFFFFFFF) { // could guard it further
+ // In some very rare cases, this could happen, in which case we might need a more accurate
+ // computation that what we can provide cheaply. This is very, very unlikely.
+ //
+ const bool inside_safe_exponent = (q >= -27) && (q <= 55); // always good because 5**q <2**128 when q>=0,
+ // and otherwise, for q<0, we have 5**-q<2**64 and the 128-bit reciprocal allows for exact computation.
+ if(!inside_safe_exponent) {
+ return compute_error_scaled<binary>(q, product.high, lz);
+ }
+ }
+ // The "compute_product_approximation" function can be slightly slower than a branchless approach:
+ // value128 product = compute_product(q, w);
+ // but in practice, we can win big with the compute_product_approximation if its additional branch
+ // is easily predicted. Which is best is data specific.
+ int upperbit = int(product.high >> 63);
+
+ answer.mantissa = product.high >> (upperbit + 64 - binary::mantissa_explicit_bits() - 3);
+
+ answer.power2 = int32_t(detail::power(int32_t(q)) + upperbit - lz - binary::minimum_exponent());
+ if (answer.power2 <= 0) { // we have a subnormal?
+ // Here have that answer.power2 <= 0 so -answer.power2 >= 0
+ if(-answer.power2 + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure.
+ answer.power2 = 0;
+ answer.mantissa = 0;
+ // result should be zero
+ return answer;
+ }
+ // next line is safe because -answer.power2 + 1 < 64
+ answer.mantissa >>= -answer.power2 + 1;
+ // Thankfully, we can't have both "round-to-even" and subnormals because
+ // "round-to-even" only occurs for powers close to 0.
+ answer.mantissa += (answer.mantissa & 1); // round up
+ answer.mantissa >>= 1;
+ // There is a weird scenario where we don't have a subnormal but just.
+ // Suppose we start with 2.2250738585072013e-308, we end up
+ // with 0x3fffffffffffff x 2^-1023-53 which is technically subnormal
+ // whereas 0x40000000000000 x 2^-1023-53 is normal. Now, we need to round
+ // up 0x3fffffffffffff x 2^-1023-53 and once we do, we are no longer
+ // subnormal, but we can only know this after rounding.
+ // So we only declare a subnormal if we are smaller than the threshold.
+ answer.power2 = (answer.mantissa < (uint64_t(1) << binary::mantissa_explicit_bits())) ? 0 : 1;
+ return answer;
+ }
+
+ // usually, we round *up*, but if we fall right in between and and we have an
+ // even basis, we need to round down
+ // We are only concerned with the cases where 5**q fits in single 64-bit word.
+ if ((product.low <= 1) && (q >= binary::min_exponent_round_to_even()) && (q <= binary::max_exponent_round_to_even()) &&
+ ((answer.mantissa & 3) == 1) ) { // we may fall between two floats!
+ // To be in-between two floats we need that in doing
+ // answer.mantissa = product.high >> (upperbit + 64 - binary::mantissa_explicit_bits() - 3);
+ // ... we dropped out only zeroes. But if this happened, then we can go back!!!
+ if((answer.mantissa << (upperbit + 64 - binary::mantissa_explicit_bits() - 3)) == product.high) {
+ answer.mantissa &= ~uint64_t(1); // flip it so that we do not round up
+ }
+ }
+
+ answer.mantissa += (answer.mantissa & 1); // round up
+ answer.mantissa >>= 1;
+ if (answer.mantissa >= (uint64_t(2) << binary::mantissa_explicit_bits())) {
+ answer.mantissa = (uint64_t(1) << binary::mantissa_explicit_bits());
+ answer.power2++; // undo previous addition
+ }
+
+ answer.mantissa &= ~(uint64_t(1) << binary::mantissa_explicit_bits());
+ if (answer.power2 >= binary::infinite_power()) { // infinity
+ answer.power2 = binary::infinite_power();
+ answer.mantissa = 0;
+ }
+ return answer;
+}
+
+} // namespace fast_float
+
+#endif
+
+#ifndef FASTFLOAT_BIGINT_H
+#define FASTFLOAT_BIGINT_H
+
+#include <algorithm>
+#include <cstdint>
+#include <climits>
+#include <cstring>
+
+
+namespace fast_float {
+
+// the limb width: we want efficient multiplication of double the bits in
+// limb, or for 64-bit limbs, at least 64-bit multiplication where we can
+// extract the high and low parts efficiently. this is every 64-bit
+// architecture except for sparc, which emulates 128-bit multiplication.
+// we might have platforms where `CHAR_BIT` is not 8, so let's avoid
+// doing `8 * sizeof(limb)`.
+#if defined(FASTFLOAT_64BIT) && !defined(__sparc)
+#define FASTFLOAT_64BIT_LIMB
+typedef uint64_t limb;
+constexpr size_t limb_bits = 64;
+#else
+#define FASTFLOAT_32BIT_LIMB
+typedef uint32_t limb;
+constexpr size_t limb_bits = 32;
+#endif
+
+typedef span<limb> limb_span;
+
+// number of bits in a bigint. this needs to be at least the number
+// of bits required to store the largest bigint, which is
+// `log2(10**(digits + max_exp))`, or `log2(10**(767 + 342))`, or
+// ~3600 bits, so we round to 4000.
+constexpr size_t bigint_bits = 4000;
+constexpr size_t bigint_limbs = bigint_bits / limb_bits;
+
+// vector-like type that is allocated on the stack. the entire
+// buffer is pre-allocated, and only the length changes.
+template <uint16_t size>
+struct stackvec {
+ limb data[size];
+ // we never need more than 150 limbs
+ uint16_t length{0};
+
+ stackvec() = default;
+ stackvec(const stackvec &) = delete;
+ stackvec &operator=(const stackvec &) = delete;
+ stackvec(stackvec &&) = delete;
+ stackvec &operator=(stackvec &&other) = delete;
+
+ // create stack vector from existing limb span.
+ stackvec(limb_span s) {
+ FASTFLOAT_ASSERT(try_extend(s));
+ }
+
+ limb& operator[](size_t index) noexcept {
+ FASTFLOAT_DEBUG_ASSERT(index < length);
+ return data[index];
+ }
+ const limb& operator[](size_t index) const noexcept {
+ FASTFLOAT_DEBUG_ASSERT(index < length);
+ return data[index];
+ }
+ // index from the end of the container
+ const limb& rindex(size_t index) const noexcept {
+ FASTFLOAT_DEBUG_ASSERT(index < length);
+ size_t rindex = length - index - 1;
+ return data[rindex];
+ }
+
+ // set the length, without bounds checking.
+ void set_len(size_t len) noexcept {
+ length = uint16_t(len);
+ }
+ constexpr size_t len() const noexcept {
+ return length;
+ }
+ constexpr bool is_empty() const noexcept {
+ return length == 0;
+ }
+ constexpr size_t capacity() const noexcept {
+ return size;
+ }
+ // append item to vector, without bounds checking
+ void push_unchecked(limb value) noexcept {
+ data[length] = value;
+ length++;
+ }
+ // append item to vector, returning if item was added
+ bool try_push(limb value) noexcept {
+ if (len() < capacity()) {
+ push_unchecked(value);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ // add items to the vector, from a span, without bounds checking
+ void extend_unchecked(limb_span s) noexcept {
+ limb* ptr = data + length;
+ ::memcpy((void*)ptr, (const void*)s.ptr, sizeof(limb) * s.len());
+ set_len(len() + s.len());
+ }
+ // try to add items to the vector, returning if items were added
+ bool try_extend(limb_span s) noexcept {
+ if (len() + s.len() <= capacity()) {
+ extend_unchecked(s);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ // resize the vector, without bounds checking
+ // if the new size is longer than the vector, assign value to each
+ // appended item.
+ void resize_unchecked(size_t new_len, limb value) noexcept {
+ if (new_len > len()) {
+ size_t count = new_len - len();
+ limb* first = data + len();
+ limb* last = first + count;
+ ::std::fill(first, last, value);
+ set_len(new_len);
+ } else {
+ set_len(new_len);
+ }
+ }
+ // try to resize the vector, returning if the vector was resized.
+ bool try_resize(size_t new_len, limb value) noexcept {
+ if (new_len > capacity()) {
+ return false;
+ } else {
+ resize_unchecked(new_len, value);
+ return true;
+ }
+ }
+ // check if any limbs are non-zero after the given index.
+ // this needs to be done in reverse order, since the index
+ // is relative to the most significant limbs.
+ bool nonzero(size_t index) const noexcept {
+ while (index < len()) {
+ if (rindex(index) != 0) {
+ return true;
+ }
+ index++;
+ }
+ return false;
+ }
+ // normalize the big integer, so most-significant zero limbs are removed.
+ void normalize() noexcept {
+ while (len() > 0 && rindex(0) == 0) {
+ length--;
+ }
+ }
+};
+
+fastfloat_really_inline
+uint64_t empty_hi64(bool& truncated) noexcept {
+ truncated = false;
+ return 0;
+}
+
+fastfloat_really_inline
+uint64_t uint64_hi64(uint64_t r0, bool& truncated) noexcept {
+ truncated = false;
+ int shl = leading_zeroes(r0);
+ return r0 << shl;
+}
+
+fastfloat_really_inline
+uint64_t uint64_hi64(uint64_t r0, uint64_t r1, bool& truncated) noexcept {
+ int shl = leading_zeroes(r0);
+ if (shl == 0) {
+ truncated = r1 != 0;
+ return r0;
+ } else {
+ int shr = 64 - shl;
+ truncated = (r1 << shl) != 0;
+ return (r0 << shl) | (r1 >> shr);
+ }
+}
+
+fastfloat_really_inline
+uint64_t uint32_hi64(uint32_t r0, bool& truncated) noexcept {
+ return uint64_hi64(r0, truncated);
+}
+
+fastfloat_really_inline
+uint64_t uint32_hi64(uint32_t r0, uint32_t r1, bool& truncated) noexcept {
+ uint64_t x0 = r0;
+ uint64_t x1 = r1;
+ return uint64_hi64((x0 << 32) | x1, truncated);
+}
+
+fastfloat_really_inline
+uint64_t uint32_hi64(uint32_t r0, uint32_t r1, uint32_t r2, bool& truncated) noexcept {
+ uint64_t x0 = r0;
+ uint64_t x1 = r1;
+ uint64_t x2 = r2;
+ return uint64_hi64(x0, (x1 << 32) | x2, truncated);
+}
+
+// add two small integers, checking for overflow.
+// we want an efficient operation. for msvc, where
+// we don't have built-in intrinsics, this is still
+// pretty fast.
+fastfloat_really_inline
+limb scalar_add(limb x, limb y, bool& overflow) noexcept {
+ limb z;
+
+// gcc and clang
+#if defined(__has_builtin)
+ #if __has_builtin(__builtin_add_overflow)
+ overflow = __builtin_add_overflow(x, y, &z);
+ return z;
+ #endif
+#endif
+
+ // generic, this still optimizes correctly on MSVC.
+ z = x + y;
+ overflow = z < x;
+ return z;
+}
+
+// multiply two small integers, getting both the high and low bits.
+fastfloat_really_inline
+limb scalar_mul(limb x, limb y, limb& carry) noexcept {
+#ifdef FASTFLOAT_64BIT_LIMB
+ #if defined(__SIZEOF_INT128__)
+ // GCC and clang both define it as an extension.
+ __uint128_t z = __uint128_t(x) * __uint128_t(y) + __uint128_t(carry);
+ carry = limb(z >> limb_bits);
+ return limb(z);
+ #else
+ // fallback, no native 128-bit integer multiplication with carry.
+ // on msvc, this optimizes identically, somehow.
+ value128 z = full_multiplication(x, y);
+ bool overflow;
+ z.low = scalar_add(z.low, carry, overflow);
+ z.high += uint64_t(overflow); // cannot overflow
+ carry = z.high;
+ return z.low;
+ #endif
+#else
+ uint64_t z = uint64_t(x) * uint64_t(y) + uint64_t(carry);
+ carry = limb(z >> limb_bits);
+ return limb(z);
+#endif
+}
+
+// add scalar value to bigint starting from offset.
+// used in grade school multiplication
+template <uint16_t size>
+inline bool small_add_from(stackvec<size>& vec, limb y, size_t start) noexcept {
+ size_t index = start;
+ limb carry = y;
+ bool overflow;
+ while (carry != 0 && index < vec.len()) {
+ vec[index] = scalar_add(vec[index], carry, overflow);
+ carry = limb(overflow);
+ index += 1;
+ }
+ if (carry != 0) {
+ FASTFLOAT_TRY(vec.try_push(carry));
+ }
+ return true;
+}
+
+// add scalar value to bigint.
+template <uint16_t size>
+fastfloat_really_inline bool small_add(stackvec<size>& vec, limb y) noexcept {
+ return small_add_from(vec, y, 0);
+}
+
+// multiply bigint by scalar value.
+template <uint16_t size>
+inline bool small_mul(stackvec<size>& vec, limb y) noexcept {
+ limb carry = 0;
+ for (size_t index = 0; index < vec.len(); index++) {
+ vec[index] = scalar_mul(vec[index], y, carry);
+ }
+ if (carry != 0) {
+ FASTFLOAT_TRY(vec.try_push(carry));
+ }
+ return true;
+}
+
+// add bigint to bigint starting from index.
+// used in grade school multiplication
+template <uint16_t size>
+bool large_add_from(stackvec<size>& x, limb_span y, size_t start) noexcept {
+ // the effective x buffer is from `xstart..x.len()`, so exit early
+ // if we can't get that current range.
+ if (x.len() < start || y.len() > x.len() - start) {
+ FASTFLOAT_TRY(x.try_resize(y.len() + start, 0));
+ }
+
+ bool carry = false;
+ for (size_t index = 0; index < y.len(); index++) {
+ limb xi = x[index + start];
+ limb yi = y[index];
+ bool c1 = false;
+ bool c2 = false;
+ xi = scalar_add(xi, yi, c1);
+ if (carry) {
+ xi = scalar_add(xi, 1, c2);
+ }
+ x[index + start] = xi;
+ carry = c1 | c2;
+ }
+
+ // handle overflow
+ if (carry) {
+ FASTFLOAT_TRY(small_add_from(x, 1, y.len() + start));
+ }
+ return true;
+}
+
+// add bigint to bigint.
+template <uint16_t size>
+fastfloat_really_inline bool large_add_from(stackvec<size>& x, limb_span y) noexcept {
+ return large_add_from(x, y, 0);
+}
+
+// grade-school multiplication algorithm
+template <uint16_t size>
+bool long_mul(stackvec<size>& x, limb_span y) noexcept {
+ limb_span xs = limb_span(x.data, x.len());
+ stackvec<size> z(xs);
+ limb_span zs = limb_span(z.data, z.len());
+
+ if (y.len() != 0) {
+ limb y0 = y[0];
+ FASTFLOAT_TRY(small_mul(x, y0));
+ for (size_t index = 1; index < y.len(); index++) {
+ limb yi = y[index];
+ stackvec<size> zi;
+ if (yi != 0) {
+ // re-use the same buffer throughout
+ zi.set_len(0);
+ FASTFLOAT_TRY(zi.try_extend(zs));
+ FASTFLOAT_TRY(small_mul(zi, yi));
+ limb_span zis = limb_span(zi.data, zi.len());
+ FASTFLOAT_TRY(large_add_from(x, zis, index));
+ }
+ }
+ }
+
+ x.normalize();
+ return true;
+}
+
+// grade-school multiplication algorithm
+template <uint16_t size>
+bool large_mul(stackvec<size>& x, limb_span y) noexcept {
+ if (y.len() == 1) {
+ FASTFLOAT_TRY(small_mul(x, y[0]));
+ } else {
+ FASTFLOAT_TRY(long_mul(x, y));
+ }
+ return true;
+}
+
+// big integer type. implements a small subset of big integer
+// arithmetic, using simple algorithms since asymptotically
+// faster algorithms are slower for a small number of limbs.
+// all operations assume the big-integer is normalized.
+struct bigint {
+ // storage of the limbs, in little-endian order.
+ stackvec<bigint_limbs> vec;
+
+ bigint(): vec() {}
+ bigint(const bigint &) = delete;
+ bigint &operator=(const bigint &) = delete;
+ bigint(bigint &&) = delete;
+ bigint &operator=(bigint &&other) = delete;
+
+ bigint(uint64_t value): vec() {
+#ifdef FASTFLOAT_64BIT_LIMB
+ vec.push_unchecked(value);
+#else
+ vec.push_unchecked(uint32_t(value));
+ vec.push_unchecked(uint32_t(value >> 32));
+#endif
+ vec.normalize();
+ }
+
+ // get the high 64 bits from the vector, and if bits were truncated.
+ // this is to get the significant digits for the float.
+ uint64_t hi64(bool& truncated) const noexcept {
+#ifdef FASTFLOAT_64BIT_LIMB
+ if (vec.len() == 0) {
+ return empty_hi64(truncated);
+ } else if (vec.len() == 1) {
+ return uint64_hi64(vec.rindex(0), truncated);
+ } else {
+ uint64_t result = uint64_hi64(vec.rindex(0), vec.rindex(1), truncated);
+ truncated |= vec.nonzero(2);
+ return result;
+ }
+#else
+ if (vec.len() == 0) {
+ return empty_hi64(truncated);
+ } else if (vec.len() == 1) {
+ return uint32_hi64(vec.rindex(0), truncated);
+ } else if (vec.len() == 2) {
+ return uint32_hi64(vec.rindex(0), vec.rindex(1), truncated);
+ } else {
+ uint64_t result = uint32_hi64(vec.rindex(0), vec.rindex(1), vec.rindex(2), truncated);
+ truncated |= vec.nonzero(3);
+ return result;
+ }
+#endif
+ }
+
+ // compare two big integers, returning the large value.
+ // assumes both are normalized. if the return value is
+ // negative, other is larger, if the return value is
+ // positive, this is larger, otherwise they are equal.
+ // the limbs are stored in little-endian order, so we
+ // must compare the limbs in ever order.
+ int compare(const bigint& other) const noexcept {
+ if (vec.len() > other.vec.len()) {
+ return 1;
+ } else if (vec.len() < other.vec.len()) {
+ return -1;
+ } else {
+ for (size_t index = vec.len(); index > 0; index--) {
+ limb xi = vec[index - 1];
+ limb yi = other.vec[index - 1];
+ if (xi > yi) {
+ return 1;
+ } else if (xi < yi) {
+ return -1;
+ }
+ }
+ return 0;
+ }
+ }
+
+ // shift left each limb n bits, carrying over to the new limb
+ // returns true if we were able to shift all the digits.
+ bool shl_bits(size_t n) noexcept {
+ // Internally, for each item, we shift left by n, and add the previous
+ // right shifted limb-bits.
+ // For example, we transform (for u8) shifted left 2, to:
+ // b10100100 b01000010
+ // b10 b10010001 b00001000
+ FASTFLOAT_DEBUG_ASSERT(n != 0);
+ FASTFLOAT_DEBUG_ASSERT(n < sizeof(limb) * 8);
+
+ size_t shl = n;
+ size_t shr = limb_bits - shl;
+ limb prev = 0;
+ for (size_t index = 0; index < vec.len(); index++) {
+ limb xi = vec[index];
+ vec[index] = (xi << shl) | (prev >> shr);
+ prev = xi;
+ }
+
+ limb carry = prev >> shr;
+ if (carry != 0) {
+ return vec.try_push(carry);
+ }
+ return true;
+ }
+
+ // move the limbs left by `n` limbs.
+ bool shl_limbs(size_t n) noexcept {
+ FASTFLOAT_DEBUG_ASSERT(n != 0);
+ if (n + vec.len() > vec.capacity()) {
+ return false;
+ } else if (!vec.is_empty()) {
+ // move limbs
+ limb* dst = vec.data + n;
+ const limb* src = vec.data;
+ ::memmove(dst, src, sizeof(limb) * vec.len());
+ // fill in empty limbs
+ limb* first = vec.data;
+ limb* last = first + n;
+ ::std::fill(first, last, 0);
+ vec.set_len(n + vec.len());
+ return true;
+ } else {
+ return true;
+ }
+ }
+
+ // move the limbs left by `n` bits.
+ bool shl(size_t n) noexcept {
+ size_t rem = n % limb_bits;
+ size_t div = n / limb_bits;
+ if (rem != 0) {
+ FASTFLOAT_TRY(shl_bits(rem));
+ }
+ if (div != 0) {
+ FASTFLOAT_TRY(shl_limbs(div));
+ }
+ return true;
+ }
+
+ // get the number of leading zeros in the bigint.
+ int ctlz() const noexcept {
+ if (vec.is_empty()) {
+ return 0;
+ } else {
+#ifdef FASTFLOAT_64BIT_LIMB
+ return leading_zeroes(vec.rindex(0));
+#else
+ // no use defining a specialized leading_zeroes for a 32-bit type.
+ uint64_t r0 = vec.rindex(0);
+ return leading_zeroes(r0 << 32);
+#endif
+ }
+ }
+
+ // get the number of bits in the bigint.
+ int bit_length() const noexcept {
+ int lz = ctlz();
+ return int(limb_bits * vec.len()) - lz;
+ }
+
+ bool mul(limb y) noexcept {
+ return small_mul(vec, y);
+ }
+
+ bool add(limb y) noexcept {
+ return small_add(vec, y);
+ }
+
+ // multiply as if by 2 raised to a power.
+ bool pow2(uint32_t exp) noexcept {
+ return shl(exp);
+ }
+
+ // multiply as if by 5 raised to a power.
+ bool pow5(uint32_t exp) noexcept {
+ // multiply by a power of 5
+ static constexpr uint32_t large_step = 135;
+ static constexpr uint64_t small_power_of_5[] = {
+ 1UL, 5UL, 25UL, 125UL, 625UL, 3125UL, 15625UL, 78125UL, 390625UL,
+ 1953125UL, 9765625UL, 48828125UL, 244140625UL, 1220703125UL,
+ 6103515625UL, 30517578125UL, 152587890625UL, 762939453125UL,
+ 3814697265625UL, 19073486328125UL, 95367431640625UL, 476837158203125UL,
+ 2384185791015625UL, 11920928955078125UL, 59604644775390625UL,
+ 298023223876953125UL, 1490116119384765625UL, 7450580596923828125UL,
+ };
+#ifdef FASTFLOAT_64BIT_LIMB
+ constexpr static limb large_power_of_5[] = {
+ 1414648277510068013UL, 9180637584431281687UL, 4539964771860779200UL,
+ 10482974169319127550UL, 198276706040285095UL};
+#else
+ constexpr static limb large_power_of_5[] = {
+ 4279965485U, 329373468U, 4020270615U, 2137533757U, 4287402176U,
+ 1057042919U, 1071430142U, 2440757623U, 381945767U, 46164893U};
+#endif
+ size_t large_length = sizeof(large_power_of_5) / sizeof(limb);
+ limb_span large = limb_span(large_power_of_5, large_length);
+ while (exp >= large_step) {
+ FASTFLOAT_TRY(large_mul(vec, large));
+ exp -= large_step;
+ }
+#ifdef FASTFLOAT_64BIT_LIMB
+ uint32_t small_step = 27;
+ limb max_native = 7450580596923828125UL;
+#else
+ uint32_t small_step = 13;
+ limb max_native = 1220703125U;
+#endif
+ while (exp >= small_step) {
+ FASTFLOAT_TRY(small_mul(vec, max_native));
+ exp -= small_step;
+ }
+ if (exp != 0) {
+ FASTFLOAT_TRY(small_mul(vec, limb(small_power_of_5[exp])));
+ }
+
+ return true;
+ }
+
+ // multiply as if by 10 raised to a power.
+ bool pow10(uint32_t exp) noexcept {
+ FASTFLOAT_TRY(pow5(exp));
+ return pow2(exp);
+ }
+};
+
+} // namespace fast_float
+
+#endif
+
+#ifndef FASTFLOAT_ASCII_NUMBER_H
+#define FASTFLOAT_ASCII_NUMBER_H
+
+#include <cctype>
+#include <cstdint>
+#include <cstring>
+#include <iterator>
+
+
+namespace fast_float {
+
+// Next function can be micro-optimized, but compilers are entirely
+// able to optimize it well.
+fastfloat_really_inline bool is_integer(char c) noexcept { return c >= '0' && c <= '9'; }
+
+fastfloat_really_inline uint64_t byteswap(uint64_t val) {
+ return (val & 0xFF00000000000000) >> 56
+ | (val & 0x00FF000000000000) >> 40
+ | (val & 0x0000FF0000000000) >> 24
+ | (val & 0x000000FF00000000) >> 8
+ | (val & 0x00000000FF000000) << 8
+ | (val & 0x0000000000FF0000) << 24
+ | (val & 0x000000000000FF00) << 40
+ | (val & 0x00000000000000FF) << 56;
+}
+
+fastfloat_really_inline uint64_t read_u64(const char *chars) {
+ uint64_t val;
+ ::memcpy(&val, chars, sizeof(uint64_t));
+#if FASTFLOAT_IS_BIG_ENDIAN == 1
+ // Need to read as-if the number was in little-endian order.
+ val = byteswap(val);
+#endif
+ return val;
+}
+
+fastfloat_really_inline void write_u64(uint8_t *chars, uint64_t val) {
+#if FASTFLOAT_IS_BIG_ENDIAN == 1
+ // Need to read as-if the number was in little-endian order.
+ val = byteswap(val);
+#endif
+ ::memcpy(chars, &val, sizeof(uint64_t));
+}
+
+// credit @aqrit
+fastfloat_really_inline uint32_t parse_eight_digits_unrolled(uint64_t val) {
+ const uint64_t mask = 0x000000FF000000FF;
+ const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
+ const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
+ val -= 0x3030303030303030;
+ val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8;
+ val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32;
+ return uint32_t(val);
+}
+
+fastfloat_really_inline uint32_t parse_eight_digits_unrolled(const char *chars) noexcept {
+ return parse_eight_digits_unrolled(read_u64(chars));
+}
+
+// credit @aqrit
+fastfloat_really_inline bool is_made_of_eight_digits_fast(uint64_t val) noexcept {
+ return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) &
+ 0x8080808080808080));
+}
+
+fastfloat_really_inline bool is_made_of_eight_digits_fast(const char *chars) noexcept {
+ return is_made_of_eight_digits_fast(read_u64(chars));
+}
+
+typedef span<const char> byte_span;
+
+struct parsed_number_string {
+ int64_t exponent{0};
+ uint64_t mantissa{0};
+ const char *lastmatch{nullptr};
+ bool negative{false};
+ bool valid{false};
+ bool too_many_digits{false};
+ // contains the range of the significant digits
+ byte_span integer{}; // non-nullable
+ byte_span fraction{}; // nullable
+};
+
+// Assuming that you use no more than 19 digits, this will
+// parse an ASCII string.
+fastfloat_really_inline
+parsed_number_string parse_number_string(const char *p, const char *pend, parse_options options) noexcept {
+ const chars_format fmt = options.format;
+ const char decimal_point = options.decimal_point;
+
+ parsed_number_string answer;
+ answer.valid = false;
+ answer.too_many_digits = false;
+ answer.negative = (*p == '-');
+ if (*p == '-') { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
+ ++p;
+ if (p == pend) {
+ return answer;
+ }
+ if (!is_integer(*p) && (*p != decimal_point)) { // a sign must be followed by an integer or the dot
+ return answer;
+ }
+ }
+ const char *const start_digits = p;
+
+ uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad)
+
+ while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) {
+ i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok
+ p += 8;
+ }
+ while ((p != pend) && is_integer(*p)) {
+ // a multiplication by 10 is cheaper than an arbitrary integer
+ // multiplication
+ i = 10 * i +
+ uint64_t(*p - '0'); // might overflow, we will handle the overflow later
+ ++p;
+ }
+ const char *const end_of_integer_part = p;
+ int64_t digit_count = int64_t(end_of_integer_part - start_digits);
+ answer.integer = byte_span(start_digits, size_t(digit_count));
+ int64_t exponent = 0;
+ if ((p != pend) && (*p == decimal_point)) {
+ ++p;
+ const char* before = p;
+ // can occur at most twice without overflowing, but let it occur more, since
+ // for integers with many digits, digit parsing is the primary bottleneck.
+ while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) {
+ i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok
+ p += 8;
+ }
+ while ((p != pend) && is_integer(*p)) {
+ uint8_t digit = uint8_t(*p - '0');
+ ++p;
+ i = i * 10 + digit; // in rare cases, this will overflow, but that's ok
+ }
+ exponent = before - p;
+ answer.fraction = byte_span(before, size_t(p - before));
+ digit_count -= exponent;
+ }
+ // we must have encountered at least one integer!
+ if (digit_count == 0) {
+ return answer;
+ }
+ int64_t exp_number = 0; // explicit exponential part
+ if ((fmt & chars_format::scientific) && (p != pend) && (('e' == *p) || ('E' == *p))) {
+ const char * location_of_e = p;
+ ++p;
+ bool neg_exp = false;
+ if ((p != pend) && ('-' == *p)) {
+ neg_exp = true;
+ ++p;
+ } else if ((p != pend) && ('+' == *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1)
+ ++p;
+ }
+ if ((p == pend) || !is_integer(*p)) {
+ if(!(fmt & chars_format::fixed)) {
+ // We are in error.
+ return answer;
+ }
+ // Otherwise, we will be ignoring the 'e'.
+ p = location_of_e;
+ } else {
+ while ((p != pend) && is_integer(*p)) {
+ uint8_t digit = uint8_t(*p - '0');
+ if (exp_number < 0x10000000) {
+ exp_number = 10 * exp_number + digit;
+ }
+ ++p;
+ }
+ if(neg_exp) { exp_number = - exp_number; }
+ exponent += exp_number;
+ }
+ } else {
+ // If it scientific and not fixed, we have to bail out.
+ if((fmt & chars_format::scientific) && !(fmt & chars_format::fixed)) { return answer; }
+ }
+ answer.lastmatch = p;
+ answer.valid = true;
+
+ // If we frequently had to deal with long strings of digits,
+ // we could extend our code by using a 128-bit integer instead
+ // of a 64-bit integer. However, this is uncommon.
+ //
+ // We can deal with up to 19 digits.
+ if (digit_count > 19) { // this is uncommon
+ // It is possible that the integer had an overflow.
+ // We have to handle the case where we have 0.0000somenumber.
+ // We need to be mindful of the case where we only have zeroes...
+ // E.g., 0.000000000...000.
+ const char *start = start_digits;
+ while ((start != pend) && (*start == '0' || *start == decimal_point)) {
+ if(*start == '0') { digit_count --; }
+ start++;
+ }
+ if (digit_count > 19) {
+ answer.too_many_digits = true;
+ // Let us start again, this time, avoiding overflows.
+ // We don't need to check if is_integer, since we use the
+ // pre-tokenized spans from above.
+ i = 0;
+ p = answer.integer.ptr;
+ const char* int_end = p + answer.integer.len();
+ const uint64_t minimal_nineteen_digit_integer{1000000000000000000};
+ while((i < minimal_nineteen_digit_integer) && (p != int_end)) {
+ i = i * 10 + uint64_t(*p - '0');
+ ++p;
+ }
+ if (i >= minimal_nineteen_digit_integer) { // We have a big integers
+ exponent = end_of_integer_part - p + exp_number;
+ } else { // We have a value with a fractional component.
+ p = answer.fraction.ptr;
+ const char* frac_end = p + answer.fraction.len();
+ while((i < minimal_nineteen_digit_integer) && (p != frac_end)) {
+ i = i * 10 + uint64_t(*p - '0');
+ ++p;
+ }
+ exponent = answer.fraction.ptr - p + exp_number;
+ }
+ // We have now corrected both exponent and i, to a truncated value
+ }
+ }
+ answer.exponent = exponent;
+ answer.mantissa = i;
+ return answer;
+}
+
+} // namespace fast_float
+
+#endif
+
+#ifndef FASTFLOAT_DIGIT_COMPARISON_H
+#define FASTFLOAT_DIGIT_COMPARISON_H
+
+#include <algorithm>
+#include <cstdint>
+#include <cstring>
+#include <iterator>
+
+
+namespace fast_float {
+
+// 1e0 to 1e19
+constexpr static uint64_t powers_of_ten_uint64[] = {
+ 1UL, 10UL, 100UL, 1000UL, 10000UL, 100000UL, 1000000UL, 10000000UL, 100000000UL,
+ 1000000000UL, 10000000000UL, 100000000000UL, 1000000000000UL, 10000000000000UL,
+ 100000000000000UL, 1000000000000000UL, 10000000000000000UL, 100000000000000000UL,
+ 1000000000000000000UL, 10000000000000000000UL};
+
+// calculate the exponent, in scientific notation, of the number.
+// this algorithm is not even close to optimized, but it has no practical
+// effect on performance: in order to have a faster algorithm, we'd need
+// to slow down performance for faster algorithms, and this is still fast.
+fastfloat_really_inline int32_t scientific_exponent(parsed_number_string& num) noexcept {
+ uint64_t mantissa = num.mantissa;
+ int32_t exponent = int32_t(num.exponent);
+ while (mantissa >= 10000) {
+ mantissa /= 10000;
+ exponent += 4;
+ }
+ while (mantissa >= 100) {
+ mantissa /= 100;
+ exponent += 2;
+ }
+ while (mantissa >= 10) {
+ mantissa /= 10;
+ exponent += 1;
+ }
+ return exponent;
+}
+
+// this converts a native floating-point number to an extended-precision float.
+template <typename T>
+fastfloat_really_inline adjusted_mantissa to_extended(T value) noexcept {
+ using equiv_uint = typename binary_format<T>::equiv_uint;
+ constexpr equiv_uint exponent_mask = binary_format<T>::exponent_mask();
+ constexpr equiv_uint mantissa_mask = binary_format<T>::mantissa_mask();
+ constexpr equiv_uint hidden_bit_mask = binary_format<T>::hidden_bit_mask();
+
+ adjusted_mantissa am;
+ int32_t bias = binary_format<T>::mantissa_explicit_bits() - binary_format<T>::minimum_exponent();
+ equiv_uint bits;
+ ::memcpy(&bits, &value, sizeof(T));
+ if ((bits & exponent_mask) == 0) {
+ // denormal
+ am.power2 = 1 - bias;
+ am.mantissa = bits & mantissa_mask;
+ } else {
+ // normal
+ am.power2 = int32_t((bits & exponent_mask) >> binary_format<T>::mantissa_explicit_bits());
+ am.power2 -= bias;
+ am.mantissa = (bits & mantissa_mask) | hidden_bit_mask;
+ }
+
+ return am;
+}
+
+// get the extended precision value of the halfway point between b and b+u.
+// we are given a native float that represents b, so we need to adjust it
+// halfway between b and b+u.
+template <typename T>
+fastfloat_really_inline adjusted_mantissa to_extended_halfway(T value) noexcept {
+ adjusted_mantissa am = to_extended(value);
+ am.mantissa <<= 1;
+ am.mantissa += 1;
+ am.power2 -= 1;
+ return am;
+}
+
+// round an extended-precision float to the nearest machine float.
+template <typename T, typename callback>
+fastfloat_really_inline void round(adjusted_mantissa& am, callback cb) noexcept {
+ int32_t mantissa_shift = 64 - binary_format<T>::mantissa_explicit_bits() - 1;
+ if (-am.power2 >= mantissa_shift) {
+ // have a denormal float
+ int32_t shift = -am.power2 + 1;
+ cb(am, std::min(shift, 64));
+ // check for round-up: if rounding-nearest carried us to the hidden bit.
+ am.power2 = (am.mantissa < (uint64_t(1) << binary_format<T>::mantissa_explicit_bits())) ? 0 : 1;
+ return;
+ }
+
+ // have a normal float, use the default shift.
+ cb(am, mantissa_shift);
+
+ // check for carry
+ if (am.mantissa >= (uint64_t(2) << binary_format<T>::mantissa_explicit_bits())) {
+ am.mantissa = (uint64_t(1) << binary_format<T>::mantissa_explicit_bits());
+ am.power2++;
+ }
+
+ // check for infinite: we could have carried to an infinite power
+ am.mantissa &= ~(uint64_t(1) << binary_format<T>::mantissa_explicit_bits());
+ if (am.power2 >= binary_format<T>::infinite_power()) {
+ am.power2 = binary_format<T>::infinite_power();
+ am.mantissa = 0;
+ }
+}
+
+template <typename callback>
+fastfloat_really_inline
+void round_nearest_tie_even(adjusted_mantissa& am, int32_t shift, callback cb) noexcept {
+ uint64_t mask;
+ uint64_t halfway;
+ if (shift == 64) {
+ mask = UINT64_MAX;
+ } else {
+ mask = (uint64_t(1) << shift) - 1;
+ }
+ if (shift == 0) {
+ halfway = 0;
+ } else {
+ halfway = uint64_t(1) << (shift - 1);
+ }
+ uint64_t truncated_bits = am.mantissa & mask;
+ uint64_t is_above = truncated_bits > halfway;
+ uint64_t is_halfway = truncated_bits == halfway;
+
+ // shift digits into position
+ if (shift == 64) {
+ am.mantissa = 0;
+ } else {
+ am.mantissa >>= shift;
+ }
+ am.power2 += shift;
+
+ bool is_odd = (am.mantissa & 1) == 1;
+ am.mantissa += uint64_t(cb(is_odd, is_halfway, is_above));
+}
+
+fastfloat_really_inline void round_down(adjusted_mantissa& am, int32_t shift) noexcept {
+ if (shift == 64) {
+ am.mantissa = 0;
+ } else {
+ am.mantissa >>= shift;
+ }
+ am.power2 += shift;
+}
+
+fastfloat_really_inline void skip_zeros(const char*& first, const char* last) noexcept {
+ uint64_t val;
+ while (std::distance(first, last) >= 8) {
+ ::memcpy(&val, first, sizeof(uint64_t));
+ if (val != 0x3030303030303030) {
+ break;
+ }
+ first += 8;
+ }
+ while (first != last) {
+ if (*first != '0') {
+ break;
+ }
+ first++;
+ }
+}
+
+// determine if any non-zero digits were truncated.
+// all characters must be valid digits.
+fastfloat_really_inline bool is_truncated(const char* first, const char* last) noexcept {
+ // do 8-bit optimizations, can just compare to 8 literal 0s.
+ uint64_t val;
+ while (std::distance(first, last) >= 8) {
+ ::memcpy(&val, first, sizeof(uint64_t));
+ if (val != 0x3030303030303030) {
+ return true;
+ }
+ first += 8;
+ }
+ while (first != last) {
+ if (*first != '0') {
+ return true;
+ }
+ first++;
+ }
+ return false;
+}
+
+fastfloat_really_inline bool is_truncated(byte_span s) noexcept {
+ return is_truncated(s.ptr, s.ptr + s.len());
+}
+
+fastfloat_really_inline
+void parse_eight_digits(const char*& p, limb& value, size_t& counter, size_t& count) noexcept {
+ value = value * 100000000 + parse_eight_digits_unrolled(p);
+ p += 8;
+ counter += 8;
+ count += 8;
+}
+
+fastfloat_really_inline
+void parse_one_digit(const char*& p, limb& value, size_t& counter, size_t& count) noexcept {
+ value = value * 10 + limb(*p - '0');
+ p++;
+ counter++;
+ count++;
+}
+
+fastfloat_really_inline
+void add_native(bigint& big, limb power, limb value) noexcept {
+ big.mul(power);
+ big.add(value);
+}
+
+fastfloat_really_inline void round_up_bigint(bigint& big, size_t& count) noexcept {
+ // need to round-up the digits, but need to avoid rounding
+ // ....9999 to ...10000, which could cause a false halfway point.
+ add_native(big, 10, 1);
+ count++;
+}
+
+// parse the significant digits into a big integer
+inline void parse_mantissa(bigint& result, parsed_number_string& num, size_t max_digits, size_t& digits) noexcept {
+ // try to minimize the number of big integer and scalar multiplication.
+ // therefore, try to parse 8 digits at a time, and multiply by the largest
+ // scalar value (9 or 19 digits) for each step.
+ size_t counter = 0;
+ digits = 0;
+ limb value = 0;
+#ifdef FASTFLOAT_64BIT_LIMB
+ size_t step = 19;
+#else
+ size_t step = 9;
+#endif
+
+ // process all integer digits.
+ const char* p = num.integer.ptr;
+ const char* pend = p + num.integer.len();
+ skip_zeros(p, pend);
+ // process all digits, in increments of step per loop
+ while (p != pend) {
+ while ((std::distance(p, pend) >= 8) && (step - counter >= 8) && (max_digits - digits >= 8)) {
+ parse_eight_digits(p, value, counter, digits);
+ }
+ while (counter < step && p != pend && digits < max_digits) {
+ parse_one_digit(p, value, counter, digits);
+ }
+ if (digits == max_digits) {
+ // add the temporary value, then check if we've truncated any digits
+ add_native(result, limb(powers_of_ten_uint64[counter]), value);
+ bool truncated = is_truncated(p, pend);
+ if (num.fraction.ptr != nullptr) {
+ truncated |= is_truncated(num.fraction);
+ }
+ if (truncated) {
+ round_up_bigint(result, digits);
+ }
+ return;
+ } else {
+ add_native(result, limb(powers_of_ten_uint64[counter]), value);
+ counter = 0;
+ value = 0;
+ }
+ }
+
+ // add our fraction digits, if they're available.
+ if (num.fraction.ptr != nullptr) {
+ p = num.fraction.ptr;
+ pend = p + num.fraction.len();
+ if (digits == 0) {
+ skip_zeros(p, pend);
+ }
+ // process all digits, in increments of step per loop
+ while (p != pend) {
+ while ((std::distance(p, pend) >= 8) && (step - counter >= 8) && (max_digits - digits >= 8)) {
+ parse_eight_digits(p, value, counter, digits);
+ }
+ while (counter < step && p != pend && digits < max_digits) {
+ parse_one_digit(p, value, counter, digits);
+ }
+ if (digits == max_digits) {
+ // add the temporary value, then check if we've truncated any digits
+ add_native(result, limb(powers_of_ten_uint64[counter]), value);
+ bool truncated = is_truncated(p, pend);
+ if (truncated) {
+ round_up_bigint(result, digits);
+ }
+ return;
+ } else {
+ add_native(result, limb(powers_of_ten_uint64[counter]), value);
+ counter = 0;
+ value = 0;
+ }
+ }
+ }
+
+ if (counter != 0) {
+ add_native(result, limb(powers_of_ten_uint64[counter]), value);
+ }
+}
+
+template <typename T>
+inline adjusted_mantissa positive_digit_comp(bigint& bigmant, int32_t exponent) noexcept {
+ FASTFLOAT_ASSERT(bigmant.pow10(uint32_t(exponent)));
+ adjusted_mantissa answer;
+ bool truncated;
+ answer.mantissa = bigmant.hi64(truncated);
+ int bias = binary_format<T>::mantissa_explicit_bits() - binary_format<T>::minimum_exponent();
+ answer.power2 = bigmant.bit_length() - 64 + bias;
+
+ round<T>(answer, [truncated](adjusted_mantissa& a, int32_t shift) {
+ round_nearest_tie_even(a, shift, [truncated](bool is_odd, bool is_halfway, bool is_above) -> bool {
+ return is_above || (is_halfway && truncated) || (is_odd && is_halfway);
+ });
+ });
+
+ return answer;
+}
+
+// the scaling here is quite simple: we have, for the real digits `m * 10^e`,
+// and for the theoretical digits `n * 2^f`. Since `e` is always negative,
+// to scale them identically, we do `n * 2^f * 5^-f`, so we now have `m * 2^e`.
+// we then need to scale by `2^(f- e)`, and then the two significant digits
+// are of the same magnitude.
+template <typename T>
+inline adjusted_mantissa negative_digit_comp(bigint& bigmant, adjusted_mantissa am, int32_t exponent) noexcept {
+ bigint& real_digits = bigmant;
+ int32_t real_exp = exponent;
+
+ // get the value of `b`, rounded down, and get a bigint representation of b+h
+ adjusted_mantissa am_b = am;
+ // gcc7 buf: use a lambda to remove the noexcept qualifier bug with -Wnoexcept-type.
+ round<T>(am_b, [](adjusted_mantissa&a, int32_t shift) { round_down(a, shift); });
+ T b;
+ to_float(false, am_b, b);
+ adjusted_mantissa theor = to_extended_halfway(b);
+ bigint theor_digits(theor.mantissa);
+ int32_t theor_exp = theor.power2;
+
+ // scale real digits and theor digits to be same power.
+ int32_t pow2_exp = theor_exp - real_exp;
+ uint32_t pow5_exp = uint32_t(-real_exp);
+ if (pow5_exp != 0) {
+ FASTFLOAT_ASSERT(theor_digits.pow5(pow5_exp));
+ }
+ if (pow2_exp > 0) {
+ FASTFLOAT_ASSERT(theor_digits.pow2(uint32_t(pow2_exp)));
+ } else if (pow2_exp < 0) {
+ FASTFLOAT_ASSERT(real_digits.pow2(uint32_t(-pow2_exp)));
+ }
+
+ // compare digits, and use it to director rounding
+ int ord = real_digits.compare(theor_digits);
+ adjusted_mantissa answer = am;
+ round<T>(answer, [ord](adjusted_mantissa& a, int32_t shift) {
+ round_nearest_tie_even(a, shift, [ord](bool is_odd, bool _, bool __) -> bool {
+ (void)_; // not needed, since we've done our comparison
+ (void)__; // not needed, since we've done our comparison
+ if (ord > 0) {
+ return true;
+ } else if (ord < 0) {
+ return false;
+ } else {
+ return is_odd;
+ }
+ });
+ });
+
+ return answer;
+}
+
+// parse the significant digits as a big integer to unambiguously round the
+// the significant digits. here, we are trying to determine how to round
+// an extended float representation close to `b+h`, halfway between `b`
+// (the float rounded-down) and `b+u`, the next positive float. this
+// algorithm is always correct, and uses one of two approaches. when
+// the exponent is positive relative to the significant digits (such as
+// 1234), we create a big-integer representation, get the high 64-bits,
+// determine if any lower bits are truncated, and use that to direct
+// rounding. in case of a negative exponent relative to the significant
+// digits (such as 1.2345), we create a theoretical representation of
+// `b` as a big-integer type, scaled to the same binary exponent as
+// the actual digits. we then compare the big integer representations
+// of both, and use that to direct rounding.
+template <typename T>
+inline adjusted_mantissa digit_comp(parsed_number_string& num, adjusted_mantissa am) noexcept {
+ // remove the invalid exponent bias
+ am.power2 -= invalid_am_bias;
+
+ int32_t sci_exp = scientific_exponent(num);
+ size_t max_digits = binary_format<T>::max_digits();
+ size_t digits = 0;
+ bigint bigmant;
+ parse_mantissa(bigmant, num, max_digits, digits);
+ // can't underflow, since digits is at most max_digits.
+ int32_t exponent = sci_exp + 1 - int32_t(digits);
+ if (exponent >= 0) {
+ return positive_digit_comp<T>(bigmant, exponent);
+ } else {
+ return negative_digit_comp<T>(bigmant, am, exponent);
+ }
+}
+
+} // namespace fast_float
+
+#endif
+
+#ifndef FASTFLOAT_PARSE_NUMBER_H
+#define FASTFLOAT_PARSE_NUMBER_H
+
+
+#include <cmath>
+#include <cstring>
+#include <limits>
+#include <system_error>
+
+namespace fast_float {
+
+
+namespace detail {
+/**
+ * Special case +inf, -inf, nan, infinity, -infinity.
+ * The case comparisons could be made much faster given that we know that the
+ * strings a null-free and fixed.
+ **/
+template <typename T>
+from_chars_result parse_infnan(const char *first, const char *last, T &value) noexcept {
+ from_chars_result answer;
+ answer.ptr = first;
+ answer.ec = std::errc(); // be optimistic
+ bool minusSign = false;
+ if (*first == '-') { // assume first < last, so dereference without checks; C++17 20.19.3.(7.1) explicitly forbids '+' here
+ minusSign = true;
+ ++first;
+ }
+ if (last - first >= 3) {
+ if (fastfloat_strncasecmp(first, "nan", 3)) {
+ answer.ptr = (first += 3);
+ value = minusSign ? -std::numeric_limits<T>::quiet_NaN() : std::numeric_limits<T>::quiet_NaN();
+ // Check for possible nan(n-char-seq-opt), C++17 20.19.3.7, C11 7.20.1.3.3. At least MSVC produces nan(ind) and nan(snan).
+ if(first != last && *first == '(') {
+ for(const char* ptr = first + 1; ptr != last; ++ptr) {
+ if (*ptr == ')') {
+ answer.ptr = ptr + 1; // valid nan(n-char-seq-opt)
+ break;
+ }
+ else if(!(('a' <= *ptr && *ptr <= 'z') || ('A' <= *ptr && *ptr <= 'Z') || ('0' <= *ptr && *ptr <= '9') || *ptr == '_'))
+ break; // forbidden char, not nan(n-char-seq-opt)
+ }
+ }
+ return answer;
+ }
+ if (fastfloat_strncasecmp(first, "inf", 3)) {
+ if ((last - first >= 8) && fastfloat_strncasecmp(first + 3, "inity", 5)) {
+ answer.ptr = first + 8;
+ } else {
+ answer.ptr = first + 3;
+ }
+ value = minusSign ? -std::numeric_limits<T>::infinity() : std::numeric_limits<T>::infinity();
+ return answer;
+ }
+ }
+ answer.ec = std::errc::invalid_argument;
+ return answer;
+}
+
+} // namespace detail
+
+template<typename T>
+from_chars_result from_chars(const char *first, const char *last,
+ T &value, chars_format fmt /*= chars_format::general*/) noexcept {
+ return from_chars_advanced(first, last, value, parse_options{fmt});
+}
+
+template<typename T>
+from_chars_result from_chars_advanced(const char *first, const char *last,
+ T &value, parse_options options) noexcept {
+
+ static_assert (std::is_same<T, double>::value || std::is_same<T, float>::value, "only float and double are supported");
+
+
+ from_chars_result answer;
+ if (first == last) {
+ answer.ec = std::errc::invalid_argument;
+ answer.ptr = first;
+ return answer;
+ }
+ parsed_number_string pns = parse_number_string(first, last, options);
+ if (!pns.valid) {
+ return detail::parse_infnan(first, last, value);
+ }
+ answer.ec = std::errc(); // be optimistic
+ answer.ptr = pns.lastmatch;
+ // Next is Clinger's fast path.
+ if (binary_format<T>::min_exponent_fast_path() <= pns.exponent && pns.exponent <= binary_format<T>::max_exponent_fast_path() && pns.mantissa <=binary_format<T>::max_mantissa_fast_path() && !pns.too_many_digits) {
+ value = T(pns.mantissa);
+ if (pns.exponent < 0) { value = value / binary_format<T>::exact_power_of_ten(-pns.exponent); }
+ else { value = value * binary_format<T>::exact_power_of_ten(pns.exponent); }
+ if (pns.negative) { value = -value; }
+ return answer;
+ }
+ adjusted_mantissa am = compute_float<binary_format<T>>(pns.exponent, pns.mantissa);
+ if(pns.too_many_digits && am.power2 >= 0) {
+ if(am != compute_float<binary_format<T>>(pns.exponent, pns.mantissa + 1)) {
+ am = compute_error<binary_format<T>>(pns.exponent, pns.mantissa);
+ }
+ }
+ // If we called compute_float<binary_format<T>>(pns.exponent, pns.mantissa) and we have an invalid power (am.power2 < 0),
+ // then we need to go the long way around again. This is very uncommon.
+ if(am.power2 < 0) { am = digit_comp<T>(pns, am); }
+ to_float(pns.negative, am, value);
+ return answer;
+}
+
+} // namespace fast_float
+
+#endif
+
diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py
index ee051766c50..ed054b041d8 100644
--- a/intern/cycles/blender/addon/properties.py
+++ b/intern/cycles/blender/addon/properties.py
@@ -1507,7 +1507,7 @@ class CyclesPreferences(bpy.types.AddonPreferences):
col.label(text="and NVIDIA driver version 470 or newer", icon='BLANK1')
elif device_type == 'HIP':
import sys
- col.label(text="Requires discrete AMD GPU with Vega architecture", icon='BLANK1')
+ col.label(text="Requires discrete AMD GPU with RDNA architecture", icon='BLANK1')
if sys.platform[:3] == "win":
col.label(text="and AMD Radeon Pro 21.Q4 driver or newer", icon='BLANK1')
elif device_type == 'METAL':
diff --git a/intern/cycles/device/hip/util.h b/intern/cycles/device/hip/util.h
index 4e4906171d1..adb68a2d44c 100644
--- a/intern/cycles/device/hip/util.h
+++ b/intern/cycles/device/hip/util.h
@@ -51,7 +51,7 @@ static inline bool hipSupportsDevice(const int hipDevId)
hipDeviceGetAttribute(&major, hipDeviceAttributeComputeCapabilityMajor, hipDevId);
hipDeviceGetAttribute(&minor, hipDeviceAttributeComputeCapabilityMinor, hipDevId);
- return (major >= 9);
+ return (major > 10) || (major == 10 && minor >= 1);
}
CCL_NAMESPACE_END
diff --git a/intern/cycles/device/metal/kernel.mm b/intern/cycles/device/metal/kernel.mm
index 1434b297ddd..9555ca03c8e 100644
--- a/intern/cycles/device/metal/kernel.mm
+++ b/intern/cycles/device/metal/kernel.mm
@@ -459,7 +459,7 @@ bool MetalDeviceKernels::load(MetalDevice *device, int kernel_type)
tbb::task_arena local_arena(max_mtlcompiler_threads);
local_arena.execute([&]() {
- tbb::parallel_for(int(0), int(DEVICE_KERNEL_NUM), [&](int i) {
+ parallel_for(int(0), int(DEVICE_KERNEL_NUM), [&](int i) {
/* skip megakernel */
if (i == DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL) {
return;
diff --git a/intern/cycles/hydra/camera.cpp b/intern/cycles/hydra/camera.cpp
index c746a107899..62042cbbcd2 100644
--- a/intern/cycles/hydra/camera.cpp
+++ b/intern/cycles/hydra/camera.cpp
@@ -3,6 +3,7 @@
* Copyright 2022 Blender Foundation */
#include "hydra/camera.h"
+#include "hydra/session.h"
#include "scene/camera.h"
#include <pxr/base/gf/frustum.h>
@@ -12,6 +13,19 @@
HDCYCLES_NAMESPACE_OPEN_SCOPE
extern Transform convert_transform(const GfMatrix4d &matrix);
+Transform convert_camera_transform(const GfMatrix4d &matrix, float metersPerUnit)
+{
+ Transform t = convert_transform(matrix);
+ // Flip Z axis
+ t.x.z *= -1.0f;
+ t.y.z *= -1.0f;
+ t.z.z *= -1.0f;
+ // Scale translation
+ t.x.w *= metersPerUnit;
+ t.y.w *= metersPerUnit;
+ t.z.w *= metersPerUnit;
+ return t;
+}
#if PXR_VERSION < 2102
// clang-format off
@@ -61,13 +75,20 @@ void HdCyclesCamera::Sync(HdSceneDelegate *sceneDelegate,
if (*dirtyBits & DirtyBits::DirtyTransform) {
sceneDelegate->SampleTransform(id, &_transformSamples);
+ bool transform_found = false;
for (size_t i = 0; i < _transformSamples.count; ++i) {
if (_transformSamples.times[i] == 0.0f) {
_transform = _transformSamples.values[i];
_data.SetTransform(_transform);
+ transform_found = true;
break;
}
}
+
+ if (!transform_found && _transformSamples.count) {
+ _transform = _transformSamples.values[0];
+ _data.SetTransform(_transform);
+ }
}
#else
if (*dirtyBits & DirtyBits::DirtyViewMatrix) {
@@ -236,18 +257,21 @@ void HdCyclesCamera::Finalize(HdRenderParam *renderParam)
HdCamera::Finalize(renderParam);
}
-void HdCyclesCamera::ApplyCameraSettings(Camera *cam) const
+void HdCyclesCamera::ApplyCameraSettings(HdRenderParam *renderParam, Camera *cam) const
{
- ApplyCameraSettings(_data, _windowPolicy, cam);
+ ApplyCameraSettings(renderParam, _data, _windowPolicy, cam);
+
+ const float metersPerUnit = static_cast<HdCyclesSession *>(renderParam)->GetStageMetersPerUnit();
array<Transform> motion(_transformSamples.count);
- for (size_t i = 0; i < _transformSamples.count; ++i)
- motion[i] = convert_transform(_transformSamples.values[i]) *
- transform_scale(1.0f, 1.0f, -1.0f);
+ for (size_t i = 0; i < _transformSamples.count; ++i) {
+ motion[i] = convert_camera_transform(_transformSamples.values[i], metersPerUnit);
+ }
cam->set_motion(motion);
}
-void HdCyclesCamera::ApplyCameraSettings(const GfCamera &dataUnconformedWindow,
+void HdCyclesCamera::ApplyCameraSettings(HdRenderParam *renderParam,
+ const GfCamera &dataUnconformedWindow,
CameraUtilConformWindowPolicy windowPolicy,
Camera *cam)
{
@@ -261,20 +285,22 @@ void HdCyclesCamera::ApplyCameraSettings(const GfCamera &dataUnconformedWindow,
GfCamera::Orthographic == CAMERA_ORTHOGRAPHIC);
cam->set_camera_type(static_cast<CameraType>(data.GetProjection()));
+ const float metersPerUnit = static_cast<HdCyclesSession *>(renderParam)->GetStageMetersPerUnit();
+
auto viewplane = data.GetFrustum().GetWindow();
auto focalLength = 1.0f;
if (data.GetProjection() == GfCamera::Perspective) {
viewplane *= 2.0 / viewplane.GetSize()[1]; // Normalize viewplane
- focalLength = data.GetFocalLength() * 1e-3f;
+ focalLength = data.GetFocalLength() * GfCamera::FOCAL_LENGTH_UNIT * metersPerUnit;
cam->set_fov(GfDegreesToRadians(data.GetFieldOfView(GfCamera::FOVVertical)));
}
- cam->set_sensorwidth(data.GetHorizontalAperture() * GfCamera::APERTURE_UNIT);
- cam->set_sensorheight(data.GetVerticalAperture() * GfCamera::APERTURE_UNIT);
+ cam->set_sensorwidth(data.GetHorizontalAperture() * GfCamera::APERTURE_UNIT * metersPerUnit);
+ cam->set_sensorheight(data.GetVerticalAperture() * GfCamera::APERTURE_UNIT * metersPerUnit);
- cam->set_nearclip(data.GetClippingRange().GetMin());
- cam->set_farclip(data.GetClippingRange().GetMax());
+ cam->set_nearclip(data.GetClippingRange().GetMin() * metersPerUnit);
+ cam->set_farclip(data.GetClippingRange().GetMax() * metersPerUnit);
cam->set_viewplane_left(viewplane.GetMin()[0]);
cam->set_viewplane_right(viewplane.GetMax()[0]);
@@ -282,14 +308,15 @@ void HdCyclesCamera::ApplyCameraSettings(const GfCamera &dataUnconformedWindow,
cam->set_viewplane_top(viewplane.GetMax()[1]);
if (data.GetFStop() != 0.0f) {
- cam->set_focaldistance(data.GetFocusDistance());
+ cam->set_focaldistance(data.GetFocusDistance() * metersPerUnit);
cam->set_aperturesize(focalLength / (2.0f * data.GetFStop()));
}
- cam->set_matrix(convert_transform(data.GetTransform()) * transform_scale(1.0f, 1.0f, -1.0f));
+ cam->set_matrix(convert_camera_transform(data.GetTransform(), metersPerUnit));
}
-void HdCyclesCamera::ApplyCameraSettings(const GfMatrix4d &worldToViewMatrix,
+void HdCyclesCamera::ApplyCameraSettings(HdRenderParam *renderParam,
+ const GfMatrix4d &worldToViewMatrix,
const GfMatrix4d &projectionMatrix,
const std::vector<GfVec4d> &clipPlanes,
Camera *cam)
@@ -298,7 +325,7 @@ void HdCyclesCamera::ApplyCameraSettings(const GfMatrix4d &worldToViewMatrix,
GfCamera data;
data.SetFromViewAndProjectionMatrix(worldToViewMatrix, projectionMatrix);
- ApplyCameraSettings(data, CameraUtilFit, cam);
+ ApplyCameraSettings(renderParam, data, CameraUtilFit, cam);
#else
TF_CODING_ERROR("Not implemented");
#endif
diff --git a/intern/cycles/hydra/camera.h b/intern/cycles/hydra/camera.h
index 8b7fed5a6bb..d839164317f 100644
--- a/intern/cycles/hydra/camera.h
+++ b/intern/cycles/hydra/camera.h
@@ -17,12 +17,14 @@ class HdCyclesCamera final : public PXR_NS::HdCamera {
HdCyclesCamera(const PXR_NS::SdfPath &sprimId);
~HdCyclesCamera() override;
- void ApplyCameraSettings(CCL_NS::Camera *targetCamera) const;
+ void ApplyCameraSettings(PXR_NS::HdRenderParam *renderParam, CCL_NS::Camera *targetCamera) const;
- static void ApplyCameraSettings(const PXR_NS::GfCamera &cameraData,
+ static void ApplyCameraSettings(PXR_NS::HdRenderParam *renderParam,
+ const PXR_NS::GfCamera &cameraData,
PXR_NS::CameraUtilConformWindowPolicy windowPolicy,
CCL_NS::Camera *targetCamera);
- static void ApplyCameraSettings(const PXR_NS::GfMatrix4d &worldToViewMatrix,
+ static void ApplyCameraSettings(PXR_NS::HdRenderParam *renderParam,
+ const PXR_NS::GfMatrix4d &worldToViewMatrix,
const PXR_NS::GfMatrix4d &projectionMatrix,
const std::vector<PXR_NS::GfVec4d> &clipPlanes,
CCL_NS::Camera *targetCamera);
diff --git a/intern/cycles/hydra/geometry.inl b/intern/cycles/hydra/geometry.inl
index 007fc6f2667..3e02a59ea83 100644
--- a/intern/cycles/hydra/geometry.inl
+++ b/intern/cycles/hydra/geometry.inl
@@ -153,7 +153,11 @@ void HdCyclesGeometry<Base, CyclesBase>::Sync(HdSceneDelegate *sceneDelegate,
// Update transforms of all instances
for (size_t i = 0; i < transforms.size(); ++i) {
- const Transform tfm = convert_transform(_geomTransform * transforms[i]);
+ const float metersPerUnit =
+ static_cast<HdCyclesSession *>(renderParam)->GetStageMetersPerUnit();
+
+ const Transform tfm = transform_scale(make_float3(metersPerUnit)) *
+ convert_transform(_geomTransform * transforms[i]);
_instances[i]->set_tfm(tfm);
}
}
diff --git a/intern/cycles/hydra/light.cpp b/intern/cycles/hydra/light.cpp
index b691da0d6a6..c0b4b3a3f38 100644
--- a/intern/cycles/hydra/light.cpp
+++ b/intern/cycles/hydra/light.cpp
@@ -54,11 +54,16 @@ void HdCyclesLight::Sync(HdSceneDelegate *sceneDelegate,
const SdfPath &id = GetId();
if (*dirtyBits & DirtyBits::DirtyTransform) {
+ const float metersPerUnit =
+ static_cast<HdCyclesSession *>(renderParam)->GetStageMetersPerUnit();
+
+ const Transform tfm = transform_scale(make_float3(metersPerUnit)) *
#if PXR_VERSION >= 2011
- const Transform tfm = convert_transform(sceneDelegate->GetTransform(id));
+ convert_transform(sceneDelegate->GetTransform(id));
#else
- const Transform tfm = convert_transform(
- sceneDelegate->GetLightParamValue(id, HdTokens->transform).Get<GfMatrix4d>());
+ convert_transform(
+ sceneDelegate->GetLightParamValue(id, HdTokens->transform)
+ .Get<GfMatrix4d>());
#endif
_light->set_tfm(tfm);
diff --git a/intern/cycles/hydra/render_delegate.cpp b/intern/cycles/hydra/render_delegate.cpp
index a954c3e4d72..faefe9382e9 100644
--- a/intern/cycles/hydra/render_delegate.cpp
+++ b/intern/cycles/hydra/render_delegate.cpp
@@ -33,11 +33,12 @@ TF_DEFINE_PRIVATE_TOKENS(_tokens,
);
TF_DEFINE_PRIVATE_TOKENS(HdCyclesRenderSettingsTokens,
+ (stageMetersPerUnit)
((device, "cycles:device"))
((threads, "cycles:threads"))
- ((time_limit, "cycles:time_limit"))
+ ((timeLimit, "cycles:time_limit"))
((samples, "cycles:samples"))
- ((sample_offset, "cycles:sample_offset"))
+ ((sampleOffset, "cycles:sample_offset"))
);
// clang-format on
@@ -424,7 +425,7 @@ HdRenderSettingDescriptorList HdCyclesDelegate::GetRenderSettingDescriptors() co
descriptors.push_back({
"Time Limit",
- HdCyclesRenderSettingsTokens->time_limit,
+ HdCyclesRenderSettingsTokens->timeLimit,
VtValue(0.0),
});
descriptors.push_back({
@@ -434,7 +435,7 @@ HdRenderSettingDescriptorList HdCyclesDelegate::GetRenderSettingDescriptors() co
});
descriptors.push_back({
"Sample Offset",
- HdCyclesRenderSettingsTokens->sample_offset,
+ HdCyclesRenderSettingsTokens->sampleOffset,
VtValue(0),
});
@@ -452,7 +453,11 @@ void HdCyclesDelegate::SetRenderSetting(const PXR_NS::TfToken &key, const PXR_NS
Scene *const scene = _renderParam->session->scene;
Session *const session = _renderParam->session;
- if (key == HdCyclesRenderSettingsTokens->time_limit) {
+ if (key == HdCyclesRenderSettingsTokens->stageMetersPerUnit) {
+ _renderParam->SetStageMetersPerUnit(
+ VtValue::Cast<double>(value).GetWithDefault(_renderParam->GetStageMetersPerUnit()));
+ }
+ else if (key == HdCyclesRenderSettingsTokens->timeLimit) {
session->set_time_limit(
VtValue::Cast<double>(value).GetWithDefault(session->params.time_limit));
}
@@ -462,7 +467,7 @@ void HdCyclesDelegate::SetRenderSetting(const PXR_NS::TfToken &key, const PXR_NS
samples = std::min(std::max(1, samples), max_samples);
session->set_samples(samples);
}
- else if (key == HdCyclesRenderSettingsTokens->sample_offset) {
+ else if (key == HdCyclesRenderSettingsTokens->sampleOffset) {
session->params.sample_offset = VtValue::Cast<int>(value).GetWithDefault(
session->params.sample_offset);
++_settingsVersion;
@@ -484,19 +489,22 @@ VtValue HdCyclesDelegate::GetRenderSetting(const TfToken &key) const
Scene *const scene = _renderParam->session->scene;
Session *const session = _renderParam->session;
- if (key == HdCyclesRenderSettingsTokens->device) {
+ if (key == HdCyclesRenderSettingsTokens->stageMetersPerUnit) {
+ return VtValue(_renderParam->GetStageMetersPerUnit());
+ }
+ else if (key == HdCyclesRenderSettingsTokens->device) {
return VtValue(TfToken(Device::string_from_type(session->params.device.type)));
}
else if (key == HdCyclesRenderSettingsTokens->threads) {
return VtValue(session->params.threads);
}
- else if (key == HdCyclesRenderSettingsTokens->time_limit) {
+ else if (key == HdCyclesRenderSettingsTokens->timeLimit) {
return VtValue(session->params.time_limit);
}
else if (key == HdCyclesRenderSettingsTokens->samples) {
return VtValue(session->params.samples);
}
- else if (key == HdCyclesRenderSettingsTokens->sample_offset) {
+ else if (key == HdCyclesRenderSettingsTokens->sampleOffset) {
return VtValue(session->params.sample_offset);
}
else {
diff --git a/intern/cycles/hydra/render_pass.cpp b/intern/cycles/hydra/render_pass.cpp
index 9d47dfc5c8d..8f6f934b898 100644
--- a/intern/cycles/hydra/render_pass.cpp
+++ b/intern/cycles/hydra/render_pass.cpp
@@ -117,10 +117,11 @@ void HdCyclesRenderPass::_Execute(const HdRenderPassStateSharedPtr &renderPassSt
#endif
if (const auto camera = static_cast<const HdCyclesCamera *>(renderPassState->GetCamera())) {
- camera->ApplyCameraSettings(scene->camera);
+ camera->ApplyCameraSettings(_renderParam, scene->camera);
}
else {
- HdCyclesCamera::ApplyCameraSettings(renderPassState->GetWorldToViewMatrix(),
+ HdCyclesCamera::ApplyCameraSettings(_renderParam,
+ renderPassState->GetWorldToViewMatrix(),
renderPassState->GetProjectionMatrix(),
renderPassState->GetClipPlanes(),
scene->camera);
diff --git a/intern/cycles/hydra/session.h b/intern/cycles/hydra/session.h
index 7e649c1847a..8d5553bf6d7 100644
--- a/intern/cycles/hydra/session.h
+++ b/intern/cycles/hydra/session.h
@@ -29,6 +29,16 @@ class HdCyclesSession final : public PXR_NS::HdRenderParam {
void UpdateScene();
+ double GetStageMetersPerUnit() const
+ {
+ return _stageMetersPerUnit;
+ }
+
+ void SetStageMetersPerUnit(double stageMetersPerUnit)
+ {
+ _stageMetersPerUnit = stageMetersPerUnit;
+ }
+
PXR_NS::HdRenderPassAovBinding GetDisplayAovBinding() const
{
return _displayAovBinding;
@@ -52,6 +62,7 @@ class HdCyclesSession final : public PXR_NS::HdRenderParam {
private:
const bool _ownCyclesSession;
+ double _stageMetersPerUnit = 0.01;
PXR_NS::HdRenderPassAovBindingVector _aovBindings;
PXR_NS::HdRenderPassAovBinding _displayAovBinding;
};
diff --git a/intern/cycles/integrator/pass_accessor_cpu.cpp b/intern/cycles/integrator/pass_accessor_cpu.cpp
index 509190c8a7e..02260a54bf4 100644
--- a/intern/cycles/integrator/pass_accessor_cpu.cpp
+++ b/intern/cycles/integrator/pass_accessor_cpu.cpp
@@ -44,7 +44,7 @@ inline void PassAccessorCPU::run_get_pass_kernel_processor_float(
const int pixel_stride = destination.pixel_stride ? destination.pixel_stride :
destination.num_components;
- tbb::parallel_for(0, buffer_params.window_height, [&](int64_t y) {
+ parallel_for(0, buffer_params.window_height, [&](int64_t y) {
const float *buffer = window_data + y * buffer_row_stride;
float *pixel = destination.pixels +
(y * buffer_params.width + destination.offset) * pixel_stride;
@@ -69,7 +69,7 @@ inline void PassAccessorCPU::run_get_pass_kernel_processor_half_rgba(
const int destination_stride = destination.stride != 0 ? destination.stride :
buffer_params.width;
- tbb::parallel_for(0, buffer_params.window_height, [&](int64_t y) {
+ parallel_for(0, buffer_params.window_height, [&](int64_t y) {
const float *buffer = window_data + y * buffer_row_stride;
half4 *pixel = dst_start + y * destination_stride;
func(kfilm_convert, buffer, pixel, buffer_params.window_width, pass_stride);
diff --git a/intern/cycles/integrator/path_trace.cpp b/intern/cycles/integrator/path_trace.cpp
index ab134179602..4ecd3b829e8 100644
--- a/intern/cycles/integrator/path_trace.cpp
+++ b/intern/cycles/integrator/path_trace.cpp
@@ -334,7 +334,7 @@ void PathTrace::init_render_buffers(const RenderWork &render_work)
/* Handle initialization scheduled by the render scheduler. */
if (render_work.init_render_buffers) {
- tbb::parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) {
+ parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) {
path_trace_work->zero_render_buffers();
});
@@ -355,7 +355,9 @@ void PathTrace::path_trace(RenderWork &render_work)
const int num_works = path_trace_works_.size();
- tbb::parallel_for(0, num_works, [&](int i) {
+ thread_capture_fp_settings();
+
+ parallel_for(0, num_works, [&](int i) {
const double work_start_time = time_dt();
const int num_samples = render_work.path_trace.num_samples;
@@ -405,7 +407,7 @@ void PathTrace::adaptive_sample(RenderWork &render_work)
const double start_time = time_dt();
uint num_active_pixels = 0;
- tbb::parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) {
+ parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) {
const uint num_active_pixels_in_work =
path_trace_work->adaptive_sampling_converge_filter_count_active(
render_work.adaptive_sampling.threshold, render_work.adaptive_sampling.reset);
@@ -483,7 +485,7 @@ void PathTrace::cryptomatte_postprocess(const RenderWork &render_work)
}
VLOG(3) << "Perform cryptomatte work.";
- tbb::parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) {
+ parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) {
path_trace_work->cryptomatte_postproces();
});
}
@@ -536,7 +538,7 @@ void PathTrace::denoise(const RenderWork &render_work)
if (multi_device_buffers) {
multi_device_buffers->copy_from_device();
- tbb::parallel_for_each(
+ parallel_for_each(
path_trace_works_, [&multi_device_buffers](unique_ptr<PathTraceWork> &path_trace_work) {
path_trace_work->copy_from_denoised_render_buffers(multi_device_buffers.get());
});
@@ -806,7 +808,7 @@ void PathTrace::tile_buffer_read()
}
/* Read buffers back from device. */
- tbb::parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) {
+ parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) {
path_trace_work->copy_render_buffers_from_device();
});
@@ -814,7 +816,7 @@ void PathTrace::tile_buffer_read()
PathTraceTile tile(*this);
if (output_driver_->read_render_tile(tile)) {
/* Copy buffers to device again. */
- tbb::parallel_for_each(path_trace_works_, [](unique_ptr<PathTraceWork> &path_trace_work) {
+ parallel_for_each(path_trace_works_, [](unique_ptr<PathTraceWork> &path_trace_work) {
path_trace_work->copy_render_buffers_to_device();
});
}
@@ -878,20 +880,20 @@ void PathTrace::progress_set_status(const string &status, const string &substatu
void PathTrace::copy_to_render_buffers(RenderBuffers *render_buffers)
{
- tbb::parallel_for_each(path_trace_works_,
- [&render_buffers](unique_ptr<PathTraceWork> &path_trace_work) {
- path_trace_work->copy_to_render_buffers(render_buffers);
- });
+ parallel_for_each(path_trace_works_,
+ [&render_buffers](unique_ptr<PathTraceWork> &path_trace_work) {
+ path_trace_work->copy_to_render_buffers(render_buffers);
+ });
render_buffers->copy_to_device();
}
void PathTrace::copy_from_render_buffers(RenderBuffers *render_buffers)
{
render_buffers->copy_from_device();
- tbb::parallel_for_each(path_trace_works_,
- [&render_buffers](unique_ptr<PathTraceWork> &path_trace_work) {
- path_trace_work->copy_from_render_buffers(render_buffers);
- });
+ parallel_for_each(path_trace_works_,
+ [&render_buffers](unique_ptr<PathTraceWork> &path_trace_work) {
+ path_trace_work->copy_from_render_buffers(render_buffers);
+ });
}
bool PathTrace::copy_render_tile_from_device()
@@ -903,7 +905,7 @@ bool PathTrace::copy_render_tile_from_device()
bool success = true;
- tbb::parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) {
+ parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) {
if (!success) {
return;
}
@@ -1004,7 +1006,7 @@ bool PathTrace::get_render_tile_pixels(const PassAccessor &pass_accessor,
bool success = true;
- tbb::parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) {
+ parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) {
if (!success) {
return;
}
@@ -1021,7 +1023,7 @@ bool PathTrace::set_render_tile_pixels(PassAccessor &pass_accessor,
{
bool success = true;
- tbb::parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) {
+ parallel_for_each(path_trace_works_, [&](unique_ptr<PathTraceWork> &path_trace_work) {
if (!success) {
return;
}
diff --git a/intern/cycles/integrator/path_trace_work_cpu.cpp b/intern/cycles/integrator/path_trace_work_cpu.cpp
index 147e273284b..518ef3185f9 100644
--- a/intern/cycles/integrator/path_trace_work_cpu.cpp
+++ b/intern/cycles/integrator/path_trace_work_cpu.cpp
@@ -73,7 +73,7 @@ void PathTraceWorkCPU::render_samples(RenderStatistics &statistics,
tbb::task_arena local_arena = local_tbb_arena_create(device_);
local_arena.execute([&]() {
- tbb::parallel_for(int64_t(0), total_pixels_num, [&](int64_t work_index) {
+ parallel_for(int64_t(0), total_pixels_num, [&](int64_t work_index) {
if (is_cancel_requested()) {
return;
}
@@ -219,7 +219,7 @@ int PathTraceWorkCPU::adaptive_sampling_converge_filter_count_active(float thres
/* Check convergency and do x-filter in a single `parallel_for`, to reduce threading overhead. */
local_arena.execute([&]() {
- tbb::parallel_for(full_y, full_y + height, [&](int y) {
+ parallel_for(full_y, full_y + height, [&](int y) {
CPUKernelThreadGlobals *kernel_globals = &kernel_thread_globals_[0];
bool row_converged = true;
@@ -243,7 +243,7 @@ int PathTraceWorkCPU::adaptive_sampling_converge_filter_count_active(float thres
if (num_active_pixels) {
local_arena.execute([&]() {
- tbb::parallel_for(full_x, full_x + width, [&](int x) {
+ parallel_for(full_x, full_x + width, [&](int x) {
CPUKernelThreadGlobals *kernel_globals = &kernel_thread_globals_[0];
kernels_.adaptive_sampling_filter_y(
kernel_globals, render_buffer, x, full_y, height, offset, stride);
@@ -265,7 +265,7 @@ void PathTraceWorkCPU::cryptomatte_postproces()
/* Check convergency and do x-filter in a single `parallel_for`, to reduce threading overhead. */
local_arena.execute([&]() {
- tbb::parallel_for(0, height, [&](int y) {
+ parallel_for(0, height, [&](int y) {
CPUKernelThreadGlobals *kernel_globals = &kernel_thread_globals_[0];
int pixel_index = y * width;
diff --git a/intern/cycles/integrator/shader_eval.cpp b/intern/cycles/integrator/shader_eval.cpp
index f5036b4020d..92b9d1c662d 100644
--- a/intern/cycles/integrator/shader_eval.cpp
+++ b/intern/cycles/integrator/shader_eval.cpp
@@ -92,7 +92,7 @@ bool ShaderEval::eval_cpu(Device *device,
tbb::task_arena local_arena(device->info.cpu_threads);
local_arena.execute([&]() {
- tbb::parallel_for(int64_t(0), work_size, [&](int64_t work_index) {
+ parallel_for(int64_t(0), work_size, [&](int64_t work_index) {
/* TODO: is this fast enough? */
if (progress_.get_cancel()) {
success = false;
diff --git a/intern/cycles/kernel/svm/blackbody.h b/intern/cycles/kernel/svm/blackbody.h
index 1618341b655..af59c2fe747 100644
--- a/intern/cycles/kernel/svm/blackbody.h
+++ b/intern/cycles/kernel/svm/blackbody.h
@@ -23,7 +23,7 @@ ccl_device_noinline void svm_node_blackbody(KernelGlobals kg,
/* Input */
float temperature = stack_load_float(stack, temperature_offset);
- float3 color_rgb = svm_math_blackbody_color(temperature);
+ float3 color_rgb = rec709_to_rgb(kg, svm_math_blackbody_color_rec709(temperature));
stack_store_float3(stack, col_offset, color_rgb);
}
diff --git a/intern/cycles/kernel/svm/closure.h b/intern/cycles/kernel/svm/closure.h
index 88b44cdbacf..305bd404d27 100644
--- a/intern/cycles/kernel/svm/closure.h
+++ b/intern/cycles/kernel/svm/closure.h
@@ -1111,7 +1111,8 @@ ccl_device_noinline int svm_node_principled_volume(KernelGlobals kg,
if (intensity > CLOSURE_WEIGHT_CUTOFF) {
float3 blackbody_tint = stack_load_float3(stack, node.w);
- float3 bb = blackbody_tint * intensity * svm_math_blackbody_color(T);
+ float3 bb = blackbody_tint * intensity *
+ rec709_to_rgb(kg, svm_math_blackbody_color_rec709(T));
emission_setup(sd, bb);
}
}
diff --git a/intern/cycles/kernel/svm/math_util.h b/intern/cycles/kernel/svm/math_util.h
index 2a496aee1e1..9f2d9561e26 100644
--- a/intern/cycles/kernel/svm/math_util.h
+++ b/intern/cycles/kernel/svm/math_util.h
@@ -189,10 +189,8 @@ ccl_device float svm_math(NodeMathType type, float a, float b, float c)
}
}
-ccl_device float3 svm_math_blackbody_color(float t)
+ccl_device float3 svm_math_blackbody_color_rec709(float t)
{
- /* TODO(lukas): Reimplement in XYZ. */
-
/* Calculate color in range 800..12000 using an approximation
* a/x+bx+c for R and G and ((at + b)t + c)t + d) for B
* Max absolute error for RGB is (0.00095, 0.00077, 0.00057),
diff --git a/intern/cycles/kernel/types.h b/intern/cycles/kernel/types.h
index db499a1e1bc..422285cd346 100644
--- a/intern/cycles/kernel/types.h
+++ b/intern/cycles/kernel/types.h
@@ -1117,13 +1117,18 @@ typedef struct KernelFilm {
float4 xyz_to_g;
float4 xyz_to_b;
float4 rgb_to_y;
+ /* Rec709 to rendering color space. */
+ float4 rec709_to_r;
+ float4 rec709_to_g;
+ float4 rec709_to_b;
+ int is_rec709;
int pass_bake_primitive;
int pass_bake_differential;
int use_approximate_shadow_catcher;
- int pad1, pad2;
+ int pad1;
} KernelFilm;
static_assert_align(KernelFilm, 16);
diff --git a/intern/cycles/kernel/util/color.h b/intern/cycles/kernel/util/color.h
index 95b6b33795d..28978d873d6 100644
--- a/intern/cycles/kernel/util/color.h
+++ b/intern/cycles/kernel/util/color.h
@@ -14,6 +14,15 @@ ccl_device float3 xyz_to_rgb(KernelGlobals kg, float3 xyz)
dot(float4_to_float3(kernel_data.film.xyz_to_b), xyz));
}
+ccl_device float3 rec709_to_rgb(KernelGlobals kg, float3 rec709)
+{
+ return (kernel_data.film.is_rec709) ?
+ rec709 :
+ make_float3(dot(float4_to_float3(kernel_data.film.rec709_to_r), rec709),
+ dot(float4_to_float3(kernel_data.film.rec709_to_g), rec709),
+ dot(float4_to_float3(kernel_data.film.rec709_to_b), rec709));
+}
+
ccl_device float linear_rgb_to_gray(KernelGlobals kg, float3 c)
{
return dot(c, float4_to_float3(kernel_data.film.rgb_to_y));
diff --git a/intern/cycles/scene/mesh.cpp b/intern/cycles/scene/mesh.cpp
index 05024a7790e..b5c0d9d92fb 100644
--- a/intern/cycles/scene/mesh.cpp
+++ b/intern/cycles/scene/mesh.cpp
@@ -690,12 +690,16 @@ void Mesh::pack_shaders(Scene *scene, uint *tri_shader)
bool last_smooth = false;
size_t triangles_size = num_triangles();
- int *shader_ptr = shader.data();
+ const int *shader_ptr = shader.data();
+ const bool *smooth_ptr = smooth.data();
for (size_t i = 0; i < triangles_size; i++) {
- if (shader_ptr[i] != last_shader || last_smooth != smooth[i]) {
- last_shader = shader_ptr[i];
- last_smooth = smooth[i];
+ const int new_shader = shader_ptr ? shader_ptr[i] : INT_MAX;
+ const bool new_smooth = smooth_ptr ? smooth_ptr[i] : false;
+
+ if (new_shader != last_shader || last_smooth != new_smooth) {
+ last_shader = new_shader;
+ last_smooth = new_smooth;
Shader *shader = (last_shader < used_shaders.size()) ?
static_cast<Shader *>(used_shaders[last_shader]) :
scene->default_surface;
diff --git a/intern/cycles/scene/shader.cpp b/intern/cycles/scene/shader.cpp
index 8a08f2a5be9..e1af92ea8cf 100644
--- a/intern/cycles/scene/shader.cpp
+++ b/intern/cycles/scene/shader.cpp
@@ -579,6 +579,10 @@ void ShaderManager::device_update_common(Device * /*device*/,
kfilm->xyz_to_g = float3_to_float4(xyz_to_g);
kfilm->xyz_to_b = float3_to_float4(xyz_to_b);
kfilm->rgb_to_y = float3_to_float4(rgb_to_y);
+ kfilm->rec709_to_r = float3_to_float4(rec709_to_r);
+ kfilm->rec709_to_g = float3_to_float4(rec709_to_g);
+ kfilm->rec709_to_b = float3_to_float4(rec709_to_b);
+ kfilm->is_rec709 = is_rec709;
}
void ShaderManager::device_free_common(Device *, DeviceScene *dscene, Scene *scene)
@@ -740,6 +744,11 @@ float ShaderManager::linear_rgb_to_gray(float3 c)
return dot(c, rgb_to_y);
}
+float3 ShaderManager::rec709_to_scene_linear(float3 c)
+{
+ return make_float3(dot(rec709_to_r, c), dot(rec709_to_g, c), dot(rec709_to_b, c));
+}
+
string ShaderManager::get_cryptomatte_materials(Scene *scene)
{
string manifest = "{";
@@ -802,11 +811,29 @@ void ShaderManager::init_xyz_transforms()
{
/* Default to ITU-BT.709 in case no appropriate transform found.
* Note XYZ here is defined as having a D65 white point. */
- xyz_to_r = make_float3(3.2404542f, -1.5371385f, -0.4985314f);
- xyz_to_g = make_float3(-0.9692660f, 1.8760108f, 0.0415560f);
- xyz_to_b = make_float3(0.0556434f, -0.2040259f, 1.0572252f);
+ const Transform xyz_to_rec709 = make_transform(3.2404542f,
+ -1.5371385f,
+ -0.4985314f,
+ 0.0f,
+ -0.9692660f,
+ 1.8760108f,
+ 0.0415560f,
+ 0.0f,
+ 0.0556434f,
+ -0.2040259f,
+ 1.0572252f,
+ 0.0f);
+
+ xyz_to_r = float4_to_float3(xyz_to_rec709.x);
+ xyz_to_g = float4_to_float3(xyz_to_rec709.y);
+ xyz_to_b = float4_to_float3(xyz_to_rec709.z);
rgb_to_y = make_float3(0.2126729f, 0.7151522f, 0.0721750f);
+ rec709_to_r = make_float3(1.0f, 0.0f, 0.0f);
+ rec709_to_g = make_float3(0.0f, 1.0f, 0.0f);
+ rec709_to_b = make_float3(0.0f, 0.0f, 1.0f);
+ is_rec709 = true;
+
#ifdef WITH_OCIO
/* Get from OpenColorO config if it has the required roles. */
OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig();
@@ -857,6 +884,12 @@ void ShaderManager::init_xyz_transforms()
const Transform rgb_to_xyz = transform_inverse(xyz_to_rgb);
rgb_to_y = float4_to_float3(rgb_to_xyz.y);
+
+ const Transform rec709_to_rgb = xyz_to_rgb * transform_inverse(xyz_to_rec709);
+ rec709_to_r = float4_to_float3(rec709_to_rgb.x);
+ rec709_to_g = float4_to_float3(rec709_to_rgb.y);
+ rec709_to_b = float4_to_float3(rec709_to_rgb.z);
+ is_rec709 = transform_equal_threshold(xyz_to_rgb, xyz_to_rec709, 0.0001f);
#endif
}
diff --git a/intern/cycles/scene/shader.h b/intern/cycles/scene/shader.h
index cbe331d8ec2..274bb9b4fa1 100644
--- a/intern/cycles/scene/shader.h
+++ b/intern/cycles/scene/shader.h
@@ -208,6 +208,7 @@ class ShaderManager {
static void free_memory();
float linear_rgb_to_gray(float3 c);
+ float3 rec709_to_scene_linear(float3 c);
string get_cryptomatte_materials(Scene *scene);
@@ -239,6 +240,10 @@ class ShaderManager {
float3 xyz_to_g;
float3 xyz_to_b;
float3 rgb_to_y;
+ float3 rec709_to_r;
+ float3 rec709_to_g;
+ float3 rec709_to_b;
+ bool is_rec709;
};
CCL_NAMESPACE_END
diff --git a/intern/cycles/scene/shader_nodes.cpp b/intern/cycles/scene/shader_nodes.cpp
index a951a558731..95fccf725f3 100644
--- a/intern/cycles/scene/shader_nodes.cpp
+++ b/intern/cycles/scene/shader_nodes.cpp
@@ -5763,7 +5763,9 @@ BlackbodyNode::BlackbodyNode() : ShaderNode(get_node_type())
void BlackbodyNode::constant_fold(const ConstantFolder &folder)
{
if (folder.all_inputs_constant()) {
- folder.make_constant(svm_math_blackbody_color(temperature));
+ const float3 rgb_rec709 = svm_math_blackbody_color_rec709(temperature);
+ const float3 rgb = folder.scene->shader_manager->rec709_to_scene_linear(rgb_rec709);
+ folder.make_constant(rgb);
}
}
diff --git a/intern/cycles/util/simd.h b/intern/cycles/util/simd.h
index 15dda4e76a8..03783abd20f 100644
--- a/intern/cycles/util/simd.h
+++ b/intern/cycles/util/simd.h
@@ -32,6 +32,12 @@
# define SIMD_SET_FLUSH_TO_ZERO \
_MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); \
_MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
+#elif defined(__aarch64__) || defined(_M_ARM64)
+# define _MM_FLUSH_ZERO_ON 24
+# define __get_fpcr(__fpcr) __asm__ __volatile__("mrs %0,fpcr" : "=r"(__fpcr))
+# define __set_fpcr(__fpcr) __asm__ __volatile__("msr fpcr,%0" : : "ri"(__fpcr))
+# define SIMD_SET_FLUSH_TO_ZERO set_fz(_MM_FLUSH_ZERO_ON);
+# define SIMD_GET_FLUSH_TO_ZERO get_fz(_MM_FLUSH_ZERO_ON)
#else
# define SIMD_SET_FLUSH_TO_ZERO
#endif
@@ -105,6 +111,23 @@ static struct StepTy {
} step ccl_attr_maybe_unused;
#endif
+#if defined(__aarch64__) || defined(_M_ARM64)
+__forceinline int set_fz(uint32_t flag)
+{
+ uint64_t old_fpcr, new_fpcr;
+ __get_fpcr(old_fpcr);
+ new_fpcr = old_fpcr | (1ULL << flag);
+ __set_fpcr(new_fpcr);
+ __get_fpcr(old_fpcr);
+ return old_fpcr == new_fpcr;
+}
+__forceinline int get_fz(uint32_t flag)
+{
+ uint64_t cur_fpcr;
+ __get_fpcr(cur_fpcr);
+ return (cur_fpcr & (1ULL << flag)) > 0 ? 1 : 0;
+}
+#endif
/* Utilities used by Neon */
#if defined(__KERNEL_NEON__)
diff --git a/intern/cycles/util/tbb.h b/intern/cycles/util/tbb.h
index 7105ddda0f8..948bf2b3e0e 100644
--- a/intern/cycles/util/tbb.h
+++ b/intern/cycles/util/tbb.h
@@ -25,6 +25,17 @@ CCL_NAMESPACE_BEGIN
using tbb::blocked_range;
using tbb::enumerable_thread_specific;
using tbb::parallel_for;
+using tbb::parallel_for_each;
+
+static inline void thread_capture_fp_settings()
+{
+#if TBB_INTERFACE_VERSION_MAJOR >= 12
+ tbb::task_group_context *ctx = tbb::task::current_context();
+#else
+ tbb::task_group_context *ctx = tbb::task::self().group();
+#endif
+ ctx->capture_fp_settings();
+}
static inline void parallel_for_cancel()
{
diff --git a/intern/cycles/util/transform.h b/intern/cycles/util/transform.h
index 371dbb0f4aa..477272f0ba6 100644
--- a/intern/cycles/util/transform.h
+++ b/intern/cycles/util/transform.h
@@ -285,6 +285,21 @@ ccl_device_inline bool operator!=(const Transform &A, const Transform &B)
return !(A == B);
}
+ccl_device_inline bool transform_equal_threshold(const Transform &A,
+ const Transform &B,
+ const float threshold)
+{
+ for (int x = 0; x < 3; x++) {
+ for (int y = 0; y < 4; y++) {
+ if (fabsf(A[x][y] - B[x][y]) > threshold) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
ccl_device_inline float3 transform_get_column(const Transform *t, int column)
{
return make_float3(t->x[column], t->y[column], t->z[column]);
diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h
index a82f634183d..ec641938f1f 100644
--- a/intern/ghost/GHOST_C-api.h
+++ b/intern/ghost/GHOST_C-api.h
@@ -30,8 +30,10 @@ extern GHOST_SystemHandle GHOST_CreateSystem(void);
/**
* Specifies whether debug messages are to be enabled for the specific system handle.
+ * \param systemhandle: The handle to the system.
+ * \param debug: Flag for systems to debug.
*/
-extern void GHOST_SystemInitDebug(GHOST_SystemHandle systemhandle, int is_debug_enabled);
+extern void GHOST_SystemInitDebug(GHOST_SystemHandle systemhandle, GHOST_Debug debug);
/**
* Disposes the one and only system.
diff --git a/intern/ghost/GHOST_ISystem.h b/intern/ghost/GHOST_ISystem.h
index ed193ee7e5d..bb91abbadec 100644
--- a/intern/ghost/GHOST_ISystem.h
+++ b/intern/ghost/GHOST_ISystem.h
@@ -452,8 +452,9 @@ class GHOST_ISystem {
/**
* Specify whether debug messages are to be shown.
+ * \param debug: Flag for systems to debug.
*/
- virtual void initDebug(bool is_debug_enabled) = 0;
+ virtual void initDebug(GHOST_Debug debug) = 0;
/**
* Check whether debug messages are to be shown.
diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h
index c654367072f..85913fbd10c 100644
--- a/intern/ghost/GHOST_Types.h
+++ b/intern/ghost/GHOST_Types.h
@@ -573,6 +573,16 @@ typedef struct {
uint32_t frequency;
} GHOST_DisplaySetting;
+typedef enum {
+ /** Axis that cursor grab will wrap. */
+ GHOST_kDebugDefault = (1 << 1),
+ GHOST_kDebugWintab = (1 << 2),
+} GHOST_TDebugFlags;
+
+typedef struct {
+ int flags;
+} GHOST_Debug;
+
#ifdef _WIN32
typedef void *GHOST_TEmbedderWindowID;
#endif // _WIN32
diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp
index e3d01c24283..93e94893162 100644
--- a/intern/ghost/intern/GHOST_C-api.cpp
+++ b/intern/ghost/intern/GHOST_C-api.cpp
@@ -30,11 +30,11 @@ GHOST_SystemHandle GHOST_CreateSystem(void)
return (GHOST_SystemHandle)system;
}
-void GHOST_SystemInitDebug(GHOST_SystemHandle systemhandle, int is_debug_enabled)
+void GHOST_SystemInitDebug(GHOST_SystemHandle systemhandle, GHOST_Debug debug)
{
GHOST_ISystem *system = (GHOST_ISystem *)systemhandle;
- system->initDebug(is_debug_enabled);
+ system->initDebug(debug);
}
GHOST_TSuccess GHOST_DisposeSystem(GHOST_SystemHandle systemhandle)
diff --git a/intern/ghost/intern/GHOST_System.cpp b/intern/ghost/intern/GHOST_System.cpp
index 3df85e18bc7..0d0d41972fd 100644
--- a/intern/ghost/intern/GHOST_System.cpp
+++ b/intern/ghost/intern/GHOST_System.cpp
@@ -390,9 +390,9 @@ void GHOST_System::useWindowFocus(const bool use_focus)
m_windowFocus = use_focus;
}
-void GHOST_System::initDebug(bool is_debug_enabled)
+void GHOST_System::initDebug(GHOST_Debug debug)
{
- m_is_debug_enabled = is_debug_enabled;
+ m_is_debug_enabled = debug.flags & GHOST_kDebugDefault;
}
bool GHOST_System::isDebugEnabled()
diff --git a/intern/ghost/intern/GHOST_System.h b/intern/ghost/intern/GHOST_System.h
index 0e1e3f734ae..4a3cded1fbd 100644
--- a/intern/ghost/intern/GHOST_System.h
+++ b/intern/ghost/intern/GHOST_System.h
@@ -334,8 +334,9 @@ class GHOST_System : public GHOST_ISystem {
/**
* Specify whether debug messages are to be shown.
+ * \param debug: Flag for systems to debug.
*/
- virtual void initDebug(bool is_debug_enabled);
+ virtual void initDebug(GHOST_Debug debug);
/**
* Check whether debug messages are to be shown.
diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp
index e588c7485b4..83869188b65 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.cpp
+++ b/intern/ghost/intern/GHOST_SystemWin32.cpp
@@ -872,6 +872,13 @@ GHOST_EventButton *GHOST_SystemWin32::processButtonEvent(GHOST_TEventType type,
int msgPosY = GET_Y_LPARAM(msgPos);
system->pushEvent(new GHOST_EventCursor(
::GetMessageTime(), GHOST_kEventCursorMove, window, msgPosX, msgPosY, td));
+
+ if (type == GHOST_kEventButtonDown) {
+ WINTAB_PRINTF("HWND %p OS button down\n", window->getHWND());
+ }
+ else if (type == GHOST_kEventButtonUp) {
+ WINTAB_PRINTF("HWND %p OS button up\n", window->getHWND());
+ }
}
window->updateMouseCapture(type == GHOST_kEventButtonDown ? MousePressed : MouseReleased);
@@ -914,6 +921,8 @@ void GHOST_SystemWin32::processWintabEvent(GHOST_WindowWin32 *window)
break;
}
case GHOST_kEventButtonDown: {
+ WINTAB_PRINTF("HWND %p Wintab button down", window->getHWND());
+
UINT message;
switch (info.button) {
case GHOST_kButtonMaskLeft:
@@ -939,9 +948,12 @@ void GHOST_SystemWin32::processWintabEvent(GHOST_WindowWin32 *window)
/* Test for Win32/Wintab button down match. */
useWintabPos = wt->testCoordinates(msg.pt.x, msg.pt.y, info.x, info.y);
if (!useWintabPos) {
+ WINTAB_PRINTF(" ... but associated system button mismatched position\n");
continue;
}
+ WINTAB_PRINTF(" ... associated to system button\n");
+
/* Steal the Win32 event which was previously peeked. */
PeekMessage(&msg, window->getHWND(), message, message, PM_REMOVE | PM_NOYIELD);
@@ -958,9 +970,14 @@ void GHOST_SystemWin32::processWintabEvent(GHOST_WindowWin32 *window)
mouseMoveHandled = true;
break;
}
+ else {
+ WINTAB_PRINTF(" ... but no system button\n");
+ }
}
case GHOST_kEventButtonUp: {
+ WINTAB_PRINTF("HWND %p Wintab button up", window->getHWND());
if (!useWintabPos) {
+ WINTAB_PRINTF(" ... but Wintab position isn't trusted\n");
continue;
}
@@ -986,10 +1003,14 @@ void GHOST_SystemWin32::processWintabEvent(GHOST_WindowWin32 *window)
if (PeekMessage(&msg, window->getHWND(), message, message, PM_REMOVE | PM_NOYIELD) &&
msg.message != WM_QUIT) {
+ WINTAB_PRINTF(" ... associated to system button\n");
window->updateMouseCapture(MouseReleased);
system->pushEvent(
new GHOST_EventButton(info.time, info.type, window, info.button, info.tabletData));
}
+ else {
+ WINTAB_PRINTF(" ... but no system button\n");
+ }
break;
}
default:
@@ -1318,6 +1339,12 @@ void GHOST_SystemWin32::setTabletAPI(GHOST_TTabletAPI api)
}
}
+void GHOST_SystemWin32::initDebug(GHOST_Debug debug)
+{
+ GHOST_System::initDebug(debug);
+ GHOST_Wintab::setDebug(debug.flags & GHOST_kDebugWintab);
+}
+
void GHOST_SystemWin32::processMinMaxInfo(MINMAXINFO *minmax)
{
minmax->ptMinTrackSize.x = 320;
@@ -1593,6 +1620,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
// Wintab events, processed
////////////////////////////////////////////////////////////////////////
case WT_CSRCHANGE: {
+ WINTAB_PRINTF("HWND %p HCTX %p WT_CSRCHANGE\n", window->getHWND(), (void *)lParam);
GHOST_Wintab *wt = window->getWintab();
if (wt) {
wt->updateCursorInfo();
@@ -1601,6 +1629,20 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
break;
}
case WT_PROXIMITY: {
+ WINTAB_PRINTF("HWND %p HCTX %p WT_PROXIMITY\n", window->getHWND(), (void *)wParam);
+ if (LOWORD(lParam)) {
+ WINTAB_PRINTF(" Cursor entering context.\n");
+ }
+ else {
+ WINTAB_PRINTF(" Cursor leaving context.\n");
+ }
+ if (HIWORD(lParam)) {
+ WINTAB_PRINTF(" Cursor entering or leaving hardware proximity.\n");
+ }
+ else {
+ WINTAB_PRINTF(" Cursor neither entering nor leaving hardware proximity.\n");
+ }
+
GHOST_Wintab *wt = window->getWintab();
if (wt) {
bool inRange = LOWORD(lParam);
@@ -1616,6 +1658,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
break;
}
case WT_INFOCHANGE: {
+ WINTAB_PRINTF("HWND %p HCTX %p WT_INFOCHANGE\n", window->getHWND(), (void *)wParam);
GHOST_Wintab *wt = window->getWintab();
if (wt) {
wt->processInfoChange(lParam);
@@ -1632,6 +1675,32 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
eventHandled = true;
break;
////////////////////////////////////////////////////////////////////////
+ // Wintab events, debug
+ ////////////////////////////////////////////////////////////////////////
+ case WT_CTXOPEN:
+ WINTAB_PRINTF("HWND %p HCTX %p WT_CTXOPEN\n", window->getHWND(), (void *)wParam);
+ break;
+ case WT_CTXCLOSE:
+ WINTAB_PRINTF("HWND %p HCTX %p WT_CTXCLOSE\n", window->getHWND(), (void *)wParam);
+ break;
+ case WT_CTXUPDATE:
+ WINTAB_PRINTF("HWND %p HCTX %p WT_CTXUPDATE\n", window->getHWND(), (void *)wParam);
+ break;
+ case WT_CTXOVERLAP:
+ WINTAB_PRINTF("HWND %p HCTX %p WT_CTXOVERLAP", window->getHWND(), (void *)wParam);
+ switch (lParam) {
+ case CXS_DISABLED:
+ WINTAB_PRINTF(" CXS_DISABLED\n");
+ break;
+ case CXS_OBSCURED:
+ WINTAB_PRINTF(" CXS_OBSCURED\n");
+ break;
+ case CXS_ONTOP:
+ WINTAB_PRINTF(" CXS_ONTOP\n");
+ break;
+ }
+ break;
+ ////////////////////////////////////////////////////////////////////////
// Pointer events, processed
////////////////////////////////////////////////////////////////////////
case WM_POINTERUPDATE:
@@ -1692,6 +1761,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
break;
case WM_MOUSEMOVE:
if (!window->m_mousePresent) {
+ WINTAB_PRINTF("HWND %p mouse enter\n", window->getHWND());
TRACKMOUSEEVENT tme = {sizeof(tme)};
tme.dwFlags = TME_LEAVE;
tme.hwndTrack = hwnd;
@@ -1740,6 +1810,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
}
break;
case WM_MOUSELEAVE: {
+ WINTAB_PRINTF("HWND %p mouse leave\n", window->getHWND());
window->m_mousePresent = false;
if (window->getTabletData().Active == GHOST_kTabletModeNone) {
event = processCursorEvent(window);
diff --git a/intern/ghost/intern/GHOST_SystemWin32.h b/intern/ghost/intern/GHOST_SystemWin32.h
index 16ad5f041ca..9f8d52f9ca3 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.h
+++ b/intern/ghost/intern/GHOST_SystemWin32.h
@@ -259,6 +259,16 @@ class GHOST_SystemWin32 : public GHOST_System {
*/
void setTabletAPI(GHOST_TTabletAPI api) override;
+ /***************************************************************************************
+ ** Debug Info
+ ***************************************************************************************/
+
+ /**
+ * Specify which debug messages are to be shown.
+ * \param debug: Flag for systems to debug.
+ */
+ void initDebug(GHOST_Debug debug) override;
+
protected:
/**
* Initializes the system.
diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp
index 11a3c097958..2ce224b666b 100644
--- a/intern/ghost/intern/GHOST_WindowWin32.cpp
+++ b/intern/ghost/intern/GHOST_WindowWin32.cpp
@@ -960,6 +960,7 @@ GHOST_Wintab *GHOST_WindowWin32::getWintab() const
void GHOST_WindowWin32::loadWintab(bool enable)
{
if (!m_wintab) {
+ WINTAB_PRINTF("Loading Wintab for window %p\n", m_hWnd);
if (m_wintab = GHOST_Wintab::loadWintab(m_hWnd)) {
if (enable) {
m_wintab->enable();
@@ -982,6 +983,7 @@ void GHOST_WindowWin32::loadWintab(bool enable)
void GHOST_WindowWin32::closeWintab()
{
+ WINTAB_PRINTF("Closing Wintab for window %p\n", m_hWnd);
delete m_wintab;
m_wintab = NULL;
}
diff --git a/intern/ghost/intern/GHOST_Wintab.cpp b/intern/ghost/intern/GHOST_Wintab.cpp
index 2547a38c0d1..be1a0a4b314 100644
--- a/intern/ghost/intern/GHOST_Wintab.cpp
+++ b/intern/ghost/intern/GHOST_Wintab.cpp
@@ -11,7 +11,6 @@
GHOST_Wintab *GHOST_Wintab::loadWintab(HWND hwnd)
{
/* Load Wintab library if available. */
-
auto handle = unique_hmodule(::LoadLibrary("Wintab32.dll"), &::FreeLibrary);
if (!handle) {
return nullptr;
@@ -116,6 +115,11 @@ GHOST_Wintab *GHOST_Wintab::loadWintab(HWND hwnd)
}
}
+ int sanityQueueSize = queueSizeGet(hctx.get());
+ WINTAB_PRINTF("HCTX %p %s queueSize: %d, queueSizeGet: %d\n", hctx.get(), __func__, queueSize, sanityQueueSize);
+
+ WINTAB_PRINTF("Loaded Wintab context %p\n", hctx.get());
+
return new GHOST_Wintab(std::move(handle),
info,
get,
@@ -183,7 +187,17 @@ GHOST_Wintab::GHOST_Wintab(unique_hmodule handle,
m_pkts{queueSize}
{
m_fpInfo(WTI_INTERFACE, IFC_NDEVICES, &m_numDevices);
+ WINTAB_PRINTF("Wintab Devices: %d\n", m_numDevices);
+
updateCursorInfo();
+
+ /* Debug info. */
+ printContextDebugInfo();
+}
+
+GHOST_Wintab::~GHOST_Wintab()
+{
+ WINTAB_PRINTF("Closing Wintab context %p\n", m_context.get());
}
void GHOST_Wintab::enable()
@@ -249,6 +263,7 @@ void GHOST_Wintab::updateCursorInfo()
BOOL pressureSupport = m_fpInfo(WTI_DEVICES, DVC_NPRESSURE, &Pressure);
m_maxPressure = pressureSupport ? Pressure.axMax : 0;
+ WINTAB_PRINTF("HCTX %p %s maxPressure: %d\n", m_context.get(), __func__, m_maxPressure);
BOOL tiltSupport = m_fpInfo(WTI_DEVICES, DVC_ORIENTATION, &Orientation);
/* Check if tablet supports azimuth [0] and altitude [1], encoded in axResolution. */
@@ -259,6 +274,7 @@ void GHOST_Wintab::updateCursorInfo()
else {
m_maxAzimuth = m_maxAltitude = 0;
}
+ WINTAB_PRINTF("HCTX %p %s maxAzimuth: %d, maxAltitude: %d\n", m_context.get(), __func__, m_maxAzimuth, m_maxAltitude);
}
void GHOST_Wintab::processInfoChange(LPARAM lParam)
@@ -266,6 +282,7 @@ void GHOST_Wintab::processInfoChange(LPARAM lParam)
/* Update number of connected Wintab digitizers. */
if (LOWORD(lParam) == WTI_INTERFACE && HIWORD(lParam) == IFC_NDEVICES) {
m_fpInfo(WTI_INTERFACE, IFC_NDEVICES, &m_numDevices);
+ WINTAB_PRINTF("HCTX %p %s numDevices: %d\n", m_context.get(), __func__, m_numDevices);
}
}
@@ -456,3 +473,144 @@ bool GHOST_Wintab::testCoordinates(int sysX, int sysY, int wtX, int wtY)
return false;
}
}
+
+bool GHOST_Wintab::m_debug = false;
+
+void GHOST_Wintab::setDebug(bool debug)
+{
+ m_debug = debug;
+}
+
+bool GHOST_Wintab::getDebug()
+{
+ return m_debug;
+}
+
+void GHOST_Wintab::printContextDebugInfo()
+{
+ if (!m_debug) {
+ return;
+ }
+
+ /* Print button maps. */
+ BYTE logicalButtons[32] = {0};
+ BYTE systemButtons[32] = {0};
+ for (int i = 0; i < 3; i++) {
+ printf("initializeWintab cursor %d buttons\n", i);
+ UINT lbut = m_fpInfo(WTI_CURSORS + i, CSR_BUTTONMAP, &logicalButtons);
+ if (lbut) {
+ printf("%d", logicalButtons[0]);
+ for (int j = 1; j < lbut; j++) {
+ printf(", %d", logicalButtons[j]);
+ }
+ printf("\n");
+ }
+ else {
+ printf("logical button error\n");
+ }
+ UINT sbut = m_fpInfo(WTI_CURSORS + i, CSR_SYSBTNMAP, &systemButtons);
+ if (sbut) {
+ printf("%d", systemButtons[0]);
+ for (int j = 1; j < sbut; j++) {
+ printf(", %d", systemButtons[j]);
+ }
+ printf("\n");
+ }
+ else {
+ printf("system button error\n");
+ }
+ }
+
+ /* Print context information. */
+
+ /* Print open context constraints. */
+ UINT maxcontexts, opencontexts;
+ m_fpInfo(WTI_INTERFACE, IFC_NCONTEXTS, &maxcontexts);
+ m_fpInfo(WTI_STATUS, STA_CONTEXTS, &opencontexts);
+ printf("%u max contexts, %u open contexts\n", maxcontexts, opencontexts);
+
+ /* Print system information. */
+ printf("left: %d, top: %d, width: %d, height: %d\n",
+ ::GetSystemMetrics(SM_XVIRTUALSCREEN),
+ ::GetSystemMetrics(SM_YVIRTUALSCREEN),
+ ::GetSystemMetrics(SM_CXVIRTUALSCREEN),
+ ::GetSystemMetrics(SM_CYVIRTUALSCREEN));
+
+ auto printContextRanges = [](LOGCONTEXT &lc) {
+ printf("lcInOrgX: %d, lcInOrgY: %d, lcInExtX: %d, lcInExtY: %d\n",
+ lc.lcInOrgX,
+ lc.lcInOrgY,
+ lc.lcInExtX,
+ lc.lcInExtY);
+ printf("lcOutOrgX: %d, lcOutOrgY: %d, lcOutExtX: %d, lcOutExtY: %d\n",
+ lc.lcOutOrgX,
+ lc.lcOutOrgY,
+ lc.lcOutExtX,
+ lc.lcOutExtY);
+ printf("lcSysOrgX: %d, lcSysOrgY: %d, lcSysExtX: %d, lcSysExtY: %d\n",
+ lc.lcSysOrgX,
+ lc.lcSysOrgY,
+ lc.lcSysExtX,
+ lc.lcSysExtY);
+ };
+
+ LOGCONTEXT lc;
+
+ /* Print system context. */
+ m_fpInfo(WTI_DEFSYSCTX, 0, &lc);
+ printf("WTI_DEFSYSCTX\n");
+ printContextRanges(lc);
+
+ /* Print system context, manually populated. */
+ m_fpInfo(WTI_DEFSYSCTX, CTX_INORGX, &lc.lcInOrgX);
+ m_fpInfo(WTI_DEFSYSCTX, CTX_INORGY, &lc.lcInOrgY);
+ m_fpInfo(WTI_DEFSYSCTX, CTX_INEXTX, &lc.lcInExtX);
+ m_fpInfo(WTI_DEFSYSCTX, CTX_INEXTY, &lc.lcInExtY);
+ m_fpInfo(WTI_DEFSYSCTX, CTX_OUTORGX, &lc.lcOutOrgX);
+ m_fpInfo(WTI_DEFSYSCTX, CTX_OUTORGY, &lc.lcOutOrgY);
+ m_fpInfo(WTI_DEFSYSCTX, CTX_OUTEXTX, &lc.lcOutExtX);
+ m_fpInfo(WTI_DEFSYSCTX, CTX_OUTEXTY, &lc.lcOutExtY);
+ m_fpInfo(WTI_DEFSYSCTX, CTX_SYSORGX, &lc.lcSysOrgX);
+ m_fpInfo(WTI_DEFSYSCTX, CTX_SYSORGY, &lc.lcSysOrgY);
+ m_fpInfo(WTI_DEFSYSCTX, CTX_SYSEXTX, &lc.lcSysExtX);
+ m_fpInfo(WTI_DEFSYSCTX, CTX_SYSEXTY, &lc.lcSysExtY);
+ printf("WTI_DEFSYSCTX CTX_*\n");
+ printContextRanges(lc);
+
+ for (unsigned int i = 0; i < m_numDevices; i++) {
+ /* Print individual device system context. */
+ m_fpInfo(WTI_DSCTXS + i, 0, &lc);
+ printf("WTI_DSCTXS %u\n", i);
+ printContextRanges(lc);
+
+ /* Print individual device system context, manually populated. */
+ m_fpInfo(WTI_DSCTXS + i, CTX_INORGX, &lc.lcInOrgX);
+ m_fpInfo(WTI_DSCTXS + i, CTX_INORGY, &lc.lcInOrgY);
+ m_fpInfo(WTI_DSCTXS + i, CTX_INEXTX, &lc.lcInExtX);
+ m_fpInfo(WTI_DSCTXS + i, CTX_INEXTY, &lc.lcInExtY);
+ m_fpInfo(WTI_DSCTXS + i, CTX_OUTORGX, &lc.lcOutOrgX);
+ m_fpInfo(WTI_DSCTXS + i, CTX_OUTORGY, &lc.lcOutOrgY);
+ m_fpInfo(WTI_DSCTXS + i, CTX_OUTEXTX, &lc.lcOutExtX);
+ m_fpInfo(WTI_DSCTXS + i, CTX_OUTEXTY, &lc.lcOutExtY);
+ m_fpInfo(WTI_DSCTXS + i, CTX_SYSORGX, &lc.lcSysOrgX);
+ m_fpInfo(WTI_DSCTXS + i, CTX_SYSORGY, &lc.lcSysOrgY);
+ m_fpInfo(WTI_DSCTXS + i, CTX_SYSEXTX, &lc.lcSysExtX);
+ m_fpInfo(WTI_DSCTXS + i, CTX_SYSEXTY, &lc.lcSysExtY);
+ printf("WTI_DSCTX %u CTX_*\n", i);
+ printContextRanges(lc);
+
+ /* Print device axis. */
+ AXIS axis_x, axis_y;
+ m_fpInfo(WTI_DEVICES + i, DVC_X, &axis_x);
+ m_fpInfo(WTI_DEVICES + i, DVC_Y, &axis_y);
+ printf("WTI_DEVICES %u axis_x org: %d, axis_y org: %d axis_x ext: %d, axis_y ext: %d\n",
+ i,
+ axis_x.axMin,
+ axis_y.axMin,
+ axis_x.axMax - axis_x.axMin + 1,
+ axis_y.axMax - axis_y.axMin + 1);
+ }
+
+ /* Other stuff while we have a logcontext. */
+ printf("sysmode %d\n", lc.lcSysMode);
+} \ No newline at end of file
diff --git a/intern/ghost/intern/GHOST_Wintab.h b/intern/ghost/intern/GHOST_Wintab.h
index a793d2d8f63..86a0143ecc0 100644
--- a/intern/ghost/intern/GHOST_Wintab.h
+++ b/intern/ghost/intern/GHOST_Wintab.h
@@ -13,6 +13,7 @@
#pragma once
#include <memory>
+#include <stdio.h>
#include <vector>
#include <wtypes.h>
@@ -25,6 +26,14 @@
#define PACKETMODE 0
#include <pktdef.h>
+#define WINTAB_PRINTF(x, ...) \
+ { \
+ if (GHOST_Wintab::getDebug()) { \
+ printf(x, __VA_ARGS__); \
+ } \
+ } \
+ (void)0
+
/* Typedefs for Wintab functions to allow dynamic loading. */
typedef UINT(API *GHOST_WIN32_WTInfo)(UINT, UINT, LPVOID);
typedef BOOL(API *GHOST_WIN32_WTGet)(HCTX, LPLOGCONTEXTA);
@@ -55,9 +64,12 @@ class GHOST_Wintab {
/**
* Loads Wintab if available.
* \param hwnd: Window to attach Wintab context to.
+ * \return Pointer to the initialized GHOST_Wintab object, or null if initialization failed.
*/
static GHOST_Wintab *loadWintab(HWND hwnd);
+ ~GHOST_Wintab();
+
/**
* Enables Wintab context.
*/
@@ -146,6 +158,16 @@ class GHOST_Wintab {
*/
GHOST_TabletData getLastTabletData();
+ /* Sets Wintab debugging.
+ * \param debug: True to enable Wintab debugging.
+ */
+ static void setDebug(bool debug);
+
+ /* Returns whether Wintab logging should occur.
+ * \return True if Wintab logging should occur.
+ */
+ static bool getDebug();
+
private:
/** Wintab DLL handle. */
unique_hmodule m_handle;
@@ -200,6 +222,9 @@ class GHOST_Wintab {
/** Most recently received tablet data, or none if pen is not in range. */
GHOST_TabletData m_lastTabletData = GHOST_TABLET_DATA_NONE;
+ /** Whether Wintab logging is enabled. */
+ static bool m_debug;
+
GHOST_Wintab(unique_hmodule handle,
GHOST_WIN32_WTInfo info,
GHOST_WIN32_WTGet get,
@@ -233,4 +258,7 @@ class GHOST_Wintab {
* \param system: System coordinates.
*/
static void extractCoordinates(LOGCONTEXT &lc, Coord &tablet, Coord &system);
+
+ /* Prints Wintab Context information. */
+ void printContextDebugInfo();
};
diff --git a/intern/opencolorio/fallback_impl.cc b/intern/opencolorio/fallback_impl.cc
index aaab8b4e6b9..d78b34d3c92 100644
--- a/intern/opencolorio/fallback_impl.cc
+++ b/intern/opencolorio/fallback_impl.cc
@@ -461,12 +461,13 @@ OCIO_ConstProcessorRcPtr *FallbackImpl::createDisplayProcessor(OCIO_ConstConfigR
const char * /*display*/,
const char * /*look*/,
const float scale,
- const float exponent)
+ const float exponent,
+ const bool inverse)
{
FallbackTransform transform;
- transform.type = TRANSFORM_LINEAR_TO_SRGB;
- transform.scale = scale;
- transform.exponent = exponent;
+ transform.type = (inverse) ? TRANSFORM_SRGB_TO_LINEAR : TRANSFORM_LINEAR_TO_SRGB;
+ transform.scale = (inverse && scale != 0.0f) ? 1.0f / scale : scale;
+ transform.exponent = (inverse && exponent != 0.0f) ? 1.0f / exponent : exponent;
return (OCIO_ConstProcessorRcPtr *)new FallbackProcessor(transform);
}
diff --git a/intern/opencolorio/ocio_capi.cc b/intern/opencolorio/ocio_capi.cc
index 91784a288c8..5e4c2a87a0b 100644
--- a/intern/opencolorio/ocio_capi.cc
+++ b/intern/opencolorio/ocio_capi.cc
@@ -250,9 +250,11 @@ OCIO_ConstProcessorRcPtr *OCIO_createDisplayProcessor(OCIO_ConstConfigRcPtr *con
const char *display,
const char *look,
const float scale,
- const float exponent)
+ const float exponent,
+ const bool inverse)
{
- return impl->createDisplayProcessor(config, input, view, display, look, scale, exponent);
+ return impl->createDisplayProcessor(
+ config, input, view, display, look, scale, exponent, inverse);
}
OCIO_PackedImageDesc *OCIO_createOCIO_PackedImageDesc(float *data,
diff --git a/intern/opencolorio/ocio_capi.h b/intern/opencolorio/ocio_capi.h
index 5c036ec263a..9bd4ec374e2 100644
--- a/intern/opencolorio/ocio_capi.h
+++ b/intern/opencolorio/ocio_capi.h
@@ -166,7 +166,8 @@ OCIO_ConstProcessorRcPtr *OCIO_createDisplayProcessor(OCIO_ConstConfigRcPtr *con
const char *display,
const char *look,
const float scale,
- const float exponent);
+ const float exponent,
+ const bool inverse);
OCIO_PackedImageDesc *OCIO_createOCIO_PackedImageDesc(float *data,
long width,
diff --git a/intern/opencolorio/ocio_impl.cc b/intern/opencolorio/ocio_impl.cc
index ca1b7cc42e1..8d9c5dd2d49 100644
--- a/intern/opencolorio/ocio_impl.cc
+++ b/intern/opencolorio/ocio_impl.cc
@@ -254,7 +254,12 @@ const char *OCIOImpl::configGetDisplayColorSpaceName(OCIO_ConstConfigRcPtr *conf
const char *view)
{
try {
- return (*(ConstConfigRcPtr *)config)->getDisplayViewColorSpaceName(display, view);
+ const char *name = (*(ConstConfigRcPtr *)config)->getDisplayViewColorSpaceName(display, view);
+ /* OpenColorIO does not resolve this token for us, so do it ourselves. */
+ if (strcasecmp(name, "<USE_DISPLAY_NAME>") == 0) {
+ return display;
+ }
+ return name;
}
catch (Exception &exception) {
OCIO_reportException(exception);
@@ -655,7 +660,8 @@ OCIO_ConstProcessorRcPtr *OCIOImpl::createDisplayProcessor(OCIO_ConstConfigRcPtr
const char *display,
const char *look,
const float scale,
- const float exponent)
+ const float exponent,
+ const bool inverse)
{
ConstConfigRcPtr config = *(ConstConfigRcPtr *)config_;
@@ -718,6 +724,10 @@ OCIO_ConstProcessorRcPtr *OCIOImpl::createDisplayProcessor(OCIO_ConstConfigRcPtr
group->appendTransform(et);
}
+ if (inverse) {
+ group->setDirection(TRANSFORM_DIR_INVERSE);
+ }
+
/* Create processor from transform. This is the moment were OCIO validates
* the entire transform, no need to check for the validity of inputs above. */
ConstProcessorRcPtr *p = MEM_new<ConstProcessorRcPtr>(__func__);
diff --git a/intern/opencolorio/ocio_impl.h b/intern/opencolorio/ocio_impl.h
index d42fa58121f..f8397c62e52 100644
--- a/intern/opencolorio/ocio_impl.h
+++ b/intern/opencolorio/ocio_impl.h
@@ -85,7 +85,8 @@ class IOCIOImpl {
const char *display,
const char *look,
const float scale,
- const float exponent) = 0;
+ const float exponent,
+ const bool inverse) = 0;
virtual OCIO_PackedImageDesc *createOCIO_PackedImageDesc(float *data,
long width,
@@ -201,7 +202,8 @@ class FallbackImpl : public IOCIOImpl {
const char *display,
const char *look,
const float scale,
- const float exponent);
+ const float exponent,
+ const bool inverse);
OCIO_PackedImageDesc *createOCIO_PackedImageDesc(float *data,
long width,
@@ -290,7 +292,8 @@ class OCIOImpl : public IOCIOImpl {
const char *display,
const char *look,
const float scale,
- const float exponent);
+ const float exponent,
+ const bool inverse);
OCIO_PackedImageDesc *createOCIO_PackedImageDesc(float *data,
long width,
diff --git a/intern/opencolorio/ocio_impl_glsl.cc b/intern/opencolorio/ocio_impl_glsl.cc
index 87f9d8837c7..150ed1a58bb 100644
--- a/intern/opencolorio/ocio_impl_glsl.cc
+++ b/intern/opencolorio/ocio_impl_glsl.cc
@@ -603,7 +603,7 @@ static OCIO_GPUDisplayShader &getGPUDisplayShader(
OCIO_ConstProcessorRcPtr *processor_to_scene_linear = OCIO_configGetProcessorWithNames(
config, input, ROLE_SCENE_LINEAR);
OCIO_ConstProcessorRcPtr *processor_to_display = OCIO_createDisplayProcessor(
- config, ROLE_SCENE_LINEAR, view, display, look, 1.0f, 1.0f);
+ config, ROLE_SCENE_LINEAR, view, display, look, 1.0f, 1.0f, false);
/* Create shader descriptions. */
if (processor_to_scene_linear && processor_to_display) {
diff --git a/intern/utfconv/utfconv.c b/intern/utfconv/utfconv.c
index a9625309a55..4cc72ae110f 100644
--- a/intern/utfconv/utfconv.c
+++ b/intern/utfconv/utfconv.c
@@ -40,7 +40,7 @@ size_t count_utf_8_from_16(const wchar_t *string16)
}
else {
if (u < 0xE000) {
- /*illegal*/;
+ /* Illegal. */
}
else {
count += 3;
diff --git a/release/datafiles/colormanagement/config.ocio b/release/datafiles/colormanagement/config.ocio
index bdb04cbf9ce..55e52de7002 100644
--- a/release/datafiles/colormanagement/config.ocio
+++ b/release/datafiles/colormanagement/config.ocio
@@ -173,7 +173,7 @@ colorspaces:
name: Non-Color
family: raw
description: |
- Color space used for images which contains non-color data (i,e, normal maps)
+ Color space used for images which contains non-color data (i.e. normal maps)
equalitygroup:
bitdepth: 32f
isdata: true
diff --git a/release/datafiles/icons/brush.sculpt.displacement_smear.dat b/release/datafiles/icons/brush.sculpt.displacement_smear.dat
index 5d422130ea3..9e4df45b2d2 100644
--- a/release/datafiles/icons/brush.sculpt.displacement_smear.dat
+++ b/release/datafiles/icons/brush.sculpt.displacement_smear.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 1877c0ae4d4..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.multiplane_scrape.dat b/release/datafiles/icons/brush.sculpt.multiplane_scrape.dat
index b785bb51431..6e17f520282 100644
--- a/release/datafiles/icons/brush.sculpt.multiplane_scrape.dat
+++ b/release/datafiles/icons/brush.sculpt.multiplane_scrape.dat
Binary files differ
diff --git a/release/datafiles/icons/ops.curves.sculpt_grow_shrink.dat b/release/datafiles/icons/ops.curves.sculpt_grow_shrink.dat
index 9b3453085e4..13f19185030 100644
--- a/release/datafiles/icons/ops.curves.sculpt_grow_shrink.dat
+++ b/release/datafiles/icons/ops.curves.sculpt_grow_shrink.dat
Binary files differ
diff --git a/release/datafiles/icons/ops.curves.sculpt_snake_hook.dat b/release/datafiles/icons/ops.curves.sculpt_snake_hook.dat
new file mode 100644
index 00000000000..15128701d0a
--- /dev/null
+++ b/release/datafiles/icons/ops.curves.sculpt_snake_hook.dat
Binary files differ
diff --git a/release/datafiles/icons/ops.sculpt.color_filter.dat b/release/datafiles/icons/ops.sculpt.color_filter.dat
index d589b15a124..8a65043eb5f 100644
--- a/release/datafiles/icons/ops.sculpt.color_filter.dat
+++ b/release/datafiles/icons/ops.sculpt.color_filter.dat
Binary files differ
diff --git a/release/datafiles/icons/ops.sculpt.mask_by_color.dat b/release/datafiles/icons/ops.sculpt.mask_by_color.dat
index 637c47d2d84..6194ce172d5 100644
--- a/release/datafiles/icons/ops.sculpt.mask_by_color.dat
+++ b/release/datafiles/icons/ops.sculpt.mask_by_color.dat
Binary files differ
diff --git a/release/datafiles/locale b/release/datafiles/locale
-Subproject 716dc02ec30c0810513f7b4adc4ae865ae50c4e
+Subproject 63699f968344db7dc853d2c5972325beea44900
diff --git a/release/scripts/addons b/release/scripts/addons
-Subproject 787ea78f7fa6f0373d80ba1247768402df93f8a
+Subproject baa581415c7ed23d7c45ef87363174813567268
diff --git a/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py b/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py
index 58461309720..87d54213d1b 100644
--- a/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py
+++ b/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py
@@ -424,6 +424,13 @@ def dump_rna_messages(msgs, reports, settings, verbose=False):
# Recursively process subclasses.
process_cls_list(cls.__subclasses__())
+ # FIXME Workaround weird new (blender 3.2) issue where some classes (like `bpy.types.Modifier`)
+ # are not listed by `bpy.types.ID.__base__.__subclasses__()` until they are accessed from
+ # `bpy.types` (eg just executing `bpy.types.Modifier`).
+ cls_dir = dir(bpy.types)
+ for cls_name in cls_dir:
+ getattr(bpy.types, cls_name)
+
# Parse everything (recursively parsing from bpy_struct "class"...).
process_cls_list(bpy.types.ID.__base__.__subclasses__())
diff --git a/release/scripts/modules/rna_manual_reference.py b/release/scripts/modules/rna_manual_reference.py
index af6634a791a..f9bb49cf48f 100644
--- a/release/scripts/modules/rna_manual_reference.py
+++ b/release/scripts/modules/rna_manual_reference.py
@@ -47,7 +47,8 @@ url_manual_mapping = (
("bpy.types.lineartgpencilmodifier.use_offset_towards_custom_camera*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-offset-towards-custom-camera"),
("bpy.types.movietrackingsettings.refine_intrinsics_principal_point*", "movie_clip/tracking/clip/toolbar/solve.html#bpy-types-movietrackingsettings-refine-intrinsics-principal-point"),
("bpy.types.cyclesobjectsettings.shadow_terminator_geometry_offset*", "render/cycles/object_settings/object_data.html#bpy-types-cyclesobjectsettings-shadow-terminator-geometry-offset"),
- ("bpy.types.sequencertoolsettings.use_snap_current_frame_to_strips*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-use-snap-current-frame-to-strips"),
+ ("bpy.types.sequencertoolsettings.use_snap_current_frame_to_strips*", "video_editing/edit/montage/editing.html#bpy-types-sequencertoolsettings-use-snap-current-frame-to-strips"),
+ ("bpy.types.cycleslightsettings.use_multiple_importance_sampling*", "render/cycles/light_settings.html#bpy-types-cycleslightsettings-use-multiple-importance-sampling"),
("bpy.types.fluiddomainsettings.sndparticle_potential_max_energy*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-max-energy"),
("bpy.types.fluiddomainsettings.sndparticle_potential_min_energy*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-min-energy"),
("bpy.types.lineartgpencilmodifier.use_overlap_edge_type_support*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-overlap-edge-type-support"),
@@ -62,7 +63,8 @@ url_manual_mapping = (
("bpy.types.toolsettings.use_transform_correct_face_attributes*", "modeling/meshes/tools/tool_settings.html#bpy-types-toolsettings-use-transform-correct-face-attributes"),
("bpy.types.cyclesrendersettings.adaptive_scrambling_distance*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-adaptive-scrambling-distance"),
("bpy.types.cyclesrendersettings.preview_adaptive_min_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-preview-adaptive-min-samples"),
- ("bpy.types.rendersettings.use_sequencer_override_scene_strip*", "video_editing/preview/sidebar.html#bpy-types-rendersettings-use-sequencer-override-scene-strip"),
+ ("bpy.types.lineartgpencilmodifier.use_face_mark_keep_contour*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-face-mark-keep-contour"),
+ ("bpy.types.rendersettings.use_sequencer_override_scene_strip*", "editors/video_sequencer/preview/sidebar.html#bpy-types-rendersettings-use-sequencer-override-scene-strip"),
("bpy.types.toolsettings.use_transform_correct_keep_connected*", "modeling/meshes/tools/tool_settings.html#bpy-types-toolsettings-use-transform-correct-keep-connected"),
("bpy.types.cyclesrendersettings.preview_denoising_prefilter*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-preview-denoising-prefilter"),
("bpy.types.cyclesrendersettings.preview_scrambling_distance*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-preview-scrambling-distance"),
@@ -109,6 +111,7 @@ url_manual_mapping = (
("bpy.types.fluiddomainsettings.use_collision_border_top*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-top"),
("bpy.types.gpencilsculptsettings.intersection_threshold*", "grease_pencil/modes/draw/tools/cutter.html#bpy-types-gpencilsculptsettings-intersection-threshold"),
("bpy.types.gpencilsculptsettings.use_multiframe_falloff*", "grease_pencil/multiframe.html#bpy-types-gpencilsculptsettings-use-multiframe-falloff"),
+ ("bpy.types.lineartgpencilmodifier.use_back_face_culling*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-back-face-culling"),
("bpy.types.lineartgpencilmodifier.use_intersection_mask*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-intersection-mask"),
("bpy.types.lineartgpencilmodifier.use_invert_collection*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-invert-collection"),
("bpy.types.movietrackingsettings.use_keyframe_selection*", "movie_clip/tracking/clip/toolbar/solve.html#bpy-types-movietrackingsettings-use-keyframe-selection"),
@@ -128,7 +131,7 @@ url_manual_mapping = (
("bpy.types.lineartgpencilmodifier.use_face_mark_invert*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-face-mark-invert"),
("bpy.types.linestylegeometrymodifier_backbonestretcher*", "render/freestyle/view_layer/line_style/modifiers/geometry/backbone_stretcher.html#bpy-types-linestylegeometrymodifier-backbonestretcher"),
("bpy.types.linestylegeometrymodifier_sinusdisplacement*", "render/freestyle/view_layer/line_style/modifiers/geometry/sinus_displacement.html#bpy-types-linestylegeometrymodifier-sinusdisplacement"),
- ("bpy.types.sequencertoolsettings.snap_to_current_frame*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-snap-to-current-frame"),
+ ("bpy.types.sequencertoolsettings.snap_to_current_frame*", "video_editing/edit/montage/editing.html#bpy-types-sequencertoolsettings-snap-to-current-frame"),
("bpy.ops.object.geometry_nodes_input_attribute_toggle*", "modeling/modifiers/generate/geometry_nodes.html#bpy-ops-object-geometry-nodes-input-attribute-toggle"),
("bpy.types.animvizmotionpaths.show_keyframe_highlight*", "animation/motion_paths.html#bpy-types-animvizmotionpaths-show-keyframe-highlight"),
("bpy.types.brushgpencilsettings.pen_subdivision_steps*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-pen-subdivision-steps"),
@@ -163,6 +166,7 @@ url_manual_mapping = (
("bpy.types.brushgpencilsettings.use_random_press_val*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-use-random-press-val"),
("bpy.types.brushgpencilsettings.use_stroke_random_uv*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-use-stroke-random-uv"),
("bpy.types.cyclesmaterialsettings.homogeneous_volume*", "render/cycles/material_settings.html#bpy-types-cyclesmaterialsettings-homogeneous-volume"),
+ ("bpy.types.cyclesobjectsettings.is_caustics_receiver*", "render/cycles/object_settings/object_data.html#bpy-types-cyclesobjectsettings-is-caustics-receiver"),
("bpy.types.cyclesrendersettings.adaptive_min_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-adaptive-min-samples"),
("bpy.types.cyclesrendersettings.debug_bvh_time_steps*", "render/cycles/render_settings/performance.html#bpy-types-cyclesrendersettings-debug-bvh-time-steps"),
("bpy.types.cyclesrendersettings.distance_cull_margin*", "render/cycles/render_settings/simplify.html#bpy-types-cyclesrendersettings-distance-cull-margin"),
@@ -182,7 +186,7 @@ url_manual_mapping = (
("bpy.types.materialgpencilstyle.use_fill_texture_mix*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-use-fill-texture-mix"),
("bpy.types.rendersettings_simplify_gpencil_shader_fx*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-shader-fx"),
("bpy.types.rendersettings_simplify_gpencil_view_fill*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-view-fill"),
- ("bpy.types.sequencertoolsettings.snap_to_hold_offset*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-snap-to-hold-offset"),
+ ("bpy.types.sequencertoolsettings.snap_to_hold_offset*", "video_editing/edit/montage/editing.html#bpy-types-sequencertoolsettings-snap-to-hold-offset"),
("bpy.types.toolsettings.use_mesh_automerge_and_split*", "modeling/meshes/tools/tool_settings.html#bpy-types-toolsettings-use-mesh-automerge-and-split"),
("bpy.types.animvizmotionpaths.show_keyframe_numbers*", "animation/motion_paths.html#bpy-types-animvizmotionpaths-show-keyframe-numbers"),
("bpy.types.brush.cloth_constraint_softbody_strength*", "sculpt_paint/sculpting/tools/cloth.html#bpy-types-brush-cloth-constraint-softbody-strength"),
@@ -223,6 +227,7 @@ url_manual_mapping = (
("bpy.types.colormanagedsequencercolorspacesettings*", "render/color_management.html#bpy-types-colormanagedsequencercolorspacesettings"),
("bpy.types.colormanagedviewsettings.view_transform*", "render/color_management.html#bpy-types-colormanagedviewsettings-view-transform"),
("bpy.types.cyclesmaterialsettings.volume_step_rate*", "render/cycles/material_settings.html#bpy-types-cyclesmaterialsettings-volume-step-rate"),
+ ("bpy.types.cyclesobjectsettings.is_caustics_caster*", "render/cycles/object_settings/object_data.html#bpy-types-cyclesobjectsettings-is-caustics-caster"),
("bpy.types.cyclesrendersettings.adaptive_threshold*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-adaptive-threshold"),
("bpy.types.cyclesrendersettings.camera_cull_margin*", "render/cycles/render_settings/simplify.html#bpy-types-cyclesrendersettings-camera-cull-margin"),
("bpy.types.cyclesrendersettings.debug_use_hair_bvh*", "render/cycles/render_settings/performance.html#bpy-types-cyclesrendersettings-debug-use-hair-bvh"),
@@ -238,6 +243,7 @@ url_manual_mapping = (
("bpy.types.freestylelineset.select_by_image_border*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-select-by-image-border"),
("bpy.types.freestylesettings.kr_derivative_epsilon*", "render/freestyle/view_layer/freestyle.html#bpy-types-freestylesettings-kr-derivative-epsilon"),
("bpy.types.geometrynodecurveprimitivebeziersegment*", "modeling/geometry_nodes/curve_primitives/bezier_segment.html#bpy-types-geometrynodecurveprimitivebeziersegment"),
+ ("bpy.types.geometrynodecurveprimitivequadrilateral*", "modeling/geometry_nodes/curve_primitives/quadrilateral.html#bpy-types-geometrynodecurveprimitivequadrilateral"),
("bpy.types.lineartgpencilmodifier.smooth_tolerance*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-smooth-tolerance"),
("bpy.types.linestylegeometrymodifier_perlinnoise1d*", "render/freestyle/view_layer/line_style/modifiers/geometry/perlin_noise_1d.html#bpy-types-linestylegeometrymodifier-perlinnoise1d"),
("bpy.types.linestylegeometrymodifier_perlinnoise2d*", "render/freestyle/view_layer/line_style/modifiers/geometry/perlin_noise_2d.html#bpy-types-linestylegeometrymodifier-perlinnoise2d"),
@@ -247,8 +253,8 @@ url_manual_mapping = (
("bpy.types.rendersettings.use_high_quality_normals*", "render/eevee/render_settings/performance.html#bpy-types-rendersettings-use-high-quality-normals"),
("bpy.types.sequencerpreviewoverlay.show_annotation*", "editors/video_sequencer/preview/display/overlays.html#bpy-types-sequencerpreviewoverlay-show-annotation"),
("bpy.types.sequencerpreviewoverlay.show_safe_areas*", "editors/video_sequencer/preview/display/overlays.html#bpy-types-sequencerpreviewoverlay-show-safe-areas"),
- ("bpy.types.sequencertoolsettings.snap_ignore_muted*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-snap-ignore-muted"),
- ("bpy.types.sequencertoolsettings.snap_ignore_sound*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-snap-ignore-sound"),
+ ("bpy.types.sequencertoolsettings.snap_ignore_muted*", "video_editing/edit/montage/editing.html#bpy-types-sequencertoolsettings-snap-ignore-muted"),
+ ("bpy.types.sequencertoolsettings.snap_ignore_sound*", "video_editing/edit/montage/editing.html#bpy-types-sequencertoolsettings-snap-ignore-sound"),
("bpy.types.spaceoutliner.use_filter_case_sensitive*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-case-sensitive"),
("bpy.types.spaceoutliner.use_filter_object_content*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-content"),
("bpy.types.spacesequenceeditor.show_gizmo_navigate*", "editors/video_sequencer/preview/display/gizmos.html#bpy-types-spacesequenceeditor-show-gizmo-navigate"),
@@ -297,6 +303,7 @@ url_manual_mapping = (
("bpy.types.spacedopesheeteditor.show_pose_markers*", "animation/markers.html#bpy-types-spacedopesheeteditor-show-pose-markers"),
("bpy.types.spaceoutliner.use_filter_object_camera*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-camera"),
("bpy.types.spaceoutliner.use_filter_object_others*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-others"),
+ ("bpy.types.spacesequenceeditor.overlay_frame_type*", "editors/video_sequencer/preview/sidebar.html#bpy-types-spacesequenceeditor-overlay-frame-type"),
("bpy.types.spacesequenceeditor.show_strip_overlay*", "editors/video_sequencer/sequencer/display.html#bpy-types-spacesequenceeditor-show-strip-overlay"),
("bpy.types.spaceuveditor.custom_grid_subdivisions*", "editors/uv/sidebar.html#bpy-types-spaceuveditor-custom-grid-subdivisions"),
("bpy.types.toolsettings.proportional_edit_falloff*", "editors/3dview/controls/proportional_editing.html#bpy-types-toolsettings-proportional-edit-falloff"),
@@ -311,11 +318,13 @@ url_manual_mapping = (
("bpy.types.animvizmotionpaths.show_frame_numbers*", "animation/motion_paths.html#bpy-types-animvizmotionpaths-show-frame-numbers"),
("bpy.types.brushgpencilsettings.pen_smooth_steps*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-pen-smooth-steps"),
("bpy.types.brushgpencilsettings.show_fill_extend*", "grease_pencil/modes/draw/tools/fill.html#bpy-types-brushgpencilsettings-show-fill-extend"),
+ ("bpy.types.cycleslightsettings.is_caustics_light*", "render/cycles/light_settings.html#bpy-types-cycleslightsettings-is-caustics-light"),
("bpy.types.cyclesrendersettings.max_subdivisions*", "render/cycles/render_settings/subdivision.html#bpy-types-cyclesrendersettings-max-subdivisions"),
("bpy.types.cyclesrendersettings.preview_denoiser*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-preview-denoiser"),
("bpy.types.cyclesrendersettings.sampling_pattern*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-sampling-pattern"),
("bpy.types.cyclesrendersettings.volume_max_steps*", "render/cycles/render_settings/volumes.html#bpy-types-cyclesrendersettings-volume-max-steps"),
("bpy.types.cyclesrendersettings.volume_step_rate*", "render/cycles/render_settings/volumes.html#bpy-types-cyclesrendersettings-volume-step-rate"),
+ ("bpy.types.cyclesworldsettings.is_caustics_light*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings-is-caustics-light"),
("bpy.types.editbone.bbone_handle_use_scale_start*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-handle-use-scale-start"),
("bpy.types.fluiddomainsettings.cache_data_format*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-cache-data-format"),
("bpy.types.fluiddomainsettings.cache_frame_start*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-cache-frame-start"),
@@ -341,12 +350,13 @@ url_manual_mapping = (
("bpy.types.materialgpencilstyle.use_fill_holdout*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-use-fill-holdout"),
("bpy.types.particlesettings.use_parent_particles*", "physics/particles/emitter/render.html#bpy-types-particlesettings-use-parent-particles"),
("bpy.types.rigidbodyconstraint.solver_iterations*", "physics/rigid_body/constraints/introduction.html#bpy-types-rigidbodyconstraint-solver-iterations"),
+ ("bpy.types.sequenceeditor.use_overlay_frame_lock*", "editors/video_sequencer/preview/sidebar.html#bpy-types-sequenceeditor-use-overlay-frame-lock"),
("bpy.types.sequencerpreviewoverlay.show_metadata*", "editors/video_sequencer/preview/display/overlays.html#bpy-types-sequencerpreviewoverlay-show-metadata"),
("bpy.types.sequencertimelineoverlay.show_fcurves*", "editors/video_sequencer/sequencer/display.html#bpy-types-sequencertimelineoverlay-show-fcurves"),
("bpy.types.spaceclipeditor.use_grayscale_preview*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-use-grayscale-preview"),
("bpy.types.spaceoutliner.use_filter_object_empty*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-empty"),
("bpy.types.spaceoutliner.use_filter_object_light*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-light"),
- ("bpy.types.spacesequenceeditor.proxy_render_size*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-proxy-render-size"),
+ ("bpy.types.spacesequenceeditor.proxy_render_size*", "editors/video_sequencer/preview/sidebar.html#bpy-types-spacesequenceeditor-proxy-render-size"),
("bpy.types.spacespreadsheetrowfilter.column_name*", "editors/spreadsheet.html#bpy-types-spacespreadsheetrowfilter-column-name"),
("bpy.types.toolsettings.gpencil_stroke_placement*", "grease_pencil/modes/draw/stroke_placement.html#bpy-types-toolsettings-gpencil-stroke-placement"),
("bpy.types.toolsettings.use_keyframe_cycle_aware*", "editors/timeline.html#bpy-types-toolsettings-use-keyframe-cycle-aware"),
@@ -384,15 +394,16 @@ url_manual_mapping = (
("bpy.types.movietrackingcamera.distortion_model*", "movie_clip/tracking/clip/sidebar/track/camera.html#bpy-types-movietrackingcamera-distortion-model"),
("bpy.types.rendersettings.resolution_percentage*", "render/output/properties/format.html#bpy-types-rendersettings-resolution-percentage"),
("bpy.types.rendersettings_simplify_gpencil_tint*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-tint"),
+ ("bpy.types.spaceoutliner.lib_override_view_mode*", "editors/outliner/interface.html#bpy-types-spaceoutliner-lib-override-view-mode"),
("bpy.types.spaceoutliner.use_filter_object_mesh*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-mesh"),
("bpy.types.spaceoutliner.use_filter_view_layers*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-view-layers"),
- ("bpy.types.spacesequenceeditor.show_overexposed*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-show-overexposed"),
+ ("bpy.types.spacesequenceeditor.show_overexposed*", "editors/video_sequencer/preview/sidebar.html#bpy-types-spacesequenceeditor-show-overexposed"),
("bpy.types.toolsettings.use_gpencil_draw_onback*", "grease_pencil/modes/draw/introduction.html#bpy-types-toolsettings-use-gpencil-draw-onback"),
("bpy.types.toolsettings.use_snap_align_rotation*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-align-rotation"),
("bpy.types.viewlayer.use_pass_cryptomatte_asset*", "render/layers/passes.html#bpy-types-viewlayer-use-pass-cryptomatte-asset"),
("bpy.ops.outliner.collection_indirect_only_set*", "render/layers/introduction.html#bpy-ops-outliner-collection-indirect-only-set"),
("bpy.ops.scene.freestyle_geometry_modifier_add*", "render/freestyle/view_layer/line_style/geometry.html#bpy-ops-scene-freestyle-geometry-modifier-add"),
- ("bpy.ops.sequencer.deinterlace_selected_movies*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-deinterlace-selected-movies"),
+ ("bpy.ops.sequencer.deinterlace_selected_movies*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-deinterlace-selected-movies"),
("bpy.types.bakesettings.use_selected_to_active*", "render/cycles/baking.html#bpy-types-bakesettings-use-selected-to-active"),
("bpy.types.brush.surface_smooth_current_vertex*", "sculpt_paint/sculpting/tools/smooth.html#bpy-types-brush-surface-smooth-current-vertex"),
("bpy.types.brush.use_multiplane_scrape_dynamic*", "sculpt_paint/sculpting/tools/multiplane_scrape.html#bpy-types-brush-use-multiplane-scrape-dynamic"),
@@ -426,18 +437,19 @@ url_manual_mapping = (
("bpy.types.freestylelinestyle.use_split_length*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-split-length"),
("bpy.types.geometrynodedistributepointsonfaces*", "modeling/geometry_nodes/point/distribute_points_on_faces.html#bpy-types-geometrynodedistributepointsonfaces"),
("bpy.types.geometrynodesetcurvehandlepositions*", "modeling/geometry_nodes/curve/set_handle_positions.html#bpy-types-geometrynodesetcurvehandlepositions"),
+ ("bpy.types.greasepencil.stroke_thickness_space*", "grease_pencil/properties/strokes.html#bpy-types-greasepencil-stroke-thickness-space"),
("bpy.types.linestylegeometrymodifier_blueprint*", "render/freestyle/view_layer/line_style/modifiers/geometry/blueprint.html#bpy-types-linestylegeometrymodifier-blueprint"),
("bpy.types.materialgpencilstyle.alignment_mode*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-alignment-mode"),
("bpy.types.particlesettings.use_modifier_stack*", "physics/particles/emitter/emission.html#bpy-types-particlesettings-use-modifier-stack"),
- ("bpy.types.rendersettings.sequencer_gl_preview*", "video_editing/preview/sidebar.html#bpy-types-rendersettings-sequencer-gl-preview"),
+ ("bpy.types.rendersettings.sequencer_gl_preview*", "editors/video_sequencer/preview/sidebar.html#bpy-types-rendersettings-sequencer-gl-preview"),
("bpy.types.rendersettings.simplify_subdivision*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-subdivision"),
("bpy.types.sequencerpreviewoverlay.show_cursor*", "editors/video_sequencer/preview/display/overlays.html#bpy-types-sequencerpreviewoverlay-show-cursor"),
("bpy.types.spacegrapheditor.show_extrapolation*", "editors/graph_editor/introduction.html#bpy-types-spacegrapheditor-show-extrapolation"),
("bpy.types.spaceoutliner.use_filter_collection*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-collection"),
- ("bpy.types.spacesequenceeditor.cursor_location*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-cursor-location"),
- ("bpy.types.spacesequenceeditor.display_channel*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-display-channel"),
+ ("bpy.types.spacesequenceeditor.cursor_location*", "editors/video_sequencer/preview/sidebar.html#bpy-types-spacesequenceeditor-cursor-location"),
+ ("bpy.types.spacesequenceeditor.display_channel*", "editors/video_sequencer/preview/sidebar.html#bpy-types-spacesequenceeditor-display-channel"),
("bpy.types.spacesequenceeditor.show_gizmo_tool*", "editors/video_sequencer/preview/display/gizmos.html#bpy-types-spacesequenceeditor-show-gizmo-tool"),
- ("bpy.types.spacesequenceeditor.show_region_hud*", "video_editing/sequencer/navigating.html#bpy-types-spacesequenceeditor-show-region-hud"),
+ ("bpy.types.spacesequenceeditor.show_region_hud*", "editors/video_sequencer/sequencer/navigating.html#bpy-types-spacesequenceeditor-show-region-hud"),
("bpy.types.spacespreadsheet.show_only_selected*", "editors/spreadsheet.html#bpy-types-spacespreadsheet-show-only-selected"),
("bpy.types.spacespreadsheetrowfilter.operation*", "editors/spreadsheet.html#bpy-types-spacespreadsheetrowfilter-operation"),
("bpy.types.spacespreadsheetrowfilter.threshold*", "editors/spreadsheet.html#bpy-types-spacespreadsheetrowfilter-threshold"),
@@ -480,6 +492,7 @@ url_manual_mapping = (
("bpy.types.freestylelinestyle.use_dashed_line*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-dashed-line"),
("bpy.types.freestylelinestyle.use_same_object*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-same-object"),
("bpy.types.functionnodeinputspecialcharacters*", "modeling/geometry_nodes/text/special_characters.html#bpy-types-functionnodeinputspecialcharacters"),
+ ("bpy.types.geometrynodecurveendpointselection*", "modeling/geometry_nodes/curve/endpoint_selection.html#bpy-types-geometrynodecurveendpointselection"),
("bpy.types.geometrynodeinputmeshedgeneighbors*", "modeling/geometry_nodes/mesh/edge_neighbors.html#bpy-types-geometrynodeinputmeshedgeneighbors"),
("bpy.types.geometrynodeinputmeshfaceneighbors*", "modeling/geometry_nodes/mesh/face_neighbors.html#bpy-types-geometrynodeinputmeshfaceneighbors"),
("bpy.types.gpencilsculptguide.reference_point*", "grease_pencil/modes/draw/guides.html#bpy-types-gpencilsculptguide-reference-point"),
@@ -492,7 +505,7 @@ url_manual_mapping = (
("bpy.types.rendersettings.motion_blur_shutter*", "render/cycles/render_settings/motion_blur.html#bpy-types-rendersettings-motion-blur-shutter"),
("bpy.types.rendersettings.use_persistent_data*", "render/cycles/render_settings/performance.html#bpy-types-rendersettings-use-persistent-data"),
("bpy.types.sequencertimelineoverlay.show_grid*", "editors/video_sequencer/sequencer/display.html#bpy-types-sequencertimelineoverlay-show-grid"),
- ("bpy.types.sequencertoolsettings.overlap_mode*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-overlap-mode"),
+ ("bpy.types.sequencertoolsettings.overlap_mode*", "video_editing/edit/montage/editing.html#bpy-types-sequencertoolsettings-overlap-mode"),
("bpy.types.spaceclipeditor.show_green_channel*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-show-green-channel"),
("bpy.types.spacenodeoverlay.show_context_path*", "interface/controls/nodes/introduction.html#bpy-types-spacenodeoverlay-show-context-path"),
("bpy.types.spaceoutliner.show_restrict_column*", "editors/outliner/interface.html#bpy-types-spaceoutliner-show-restrict-column"),
@@ -500,6 +513,7 @@ url_manual_mapping = (
("bpy.types.spaceuveditor.display_stretch_type*", "editors/uv/overlays.html#bpy-types-spaceuveditor-display-stretch-type"),
("bpy.types.toolsettings.transform_pivot_point*", "editors/3dview/controls/pivot_point/index.html#bpy-types-toolsettings-transform-pivot-point"),
("bpy.types.toolsettings.use_proportional_edit*", "editors/3dview/controls/proportional_editing.html#bpy-types-toolsettings-use-proportional-edit"),
+ ("bpy.types.toolsettings.uv_sticky_select_mode*", "editors/uv/selecting.html#bpy-types-toolsettings-uv-sticky-select-mode"),
("bpy.types.volumedisplay.interpolation_method*", "modeling/volumes/properties.html#bpy-types-volumedisplay-interpolation-method"),
("bpy.types.brushgpencilsettings.angle_factor*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-angle-factor"),
("bpy.types.brushgpencilsettings.pen_strength*", "grease_pencil/modes/draw/tools/erase.html#bpy-types-brushgpencilsettings-pen-strength"),
@@ -582,7 +596,10 @@ url_manual_mapping = (
("bpy.types.freestylelinestyle.use_angle_min*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-angle-min"),
("bpy.types.freestylesettings.as_render_pass*", "render/freestyle/view_layer/freestyle.html#bpy-types-freestylesettings-as-render-pass"),
("bpy.types.freestylesettings.use_smoothness*", "render/freestyle/view_layer/freestyle.html#bpy-types-freestylesettings-use-smoothness"),
+ ("bpy.types.geometrynodecurveprimitivecircle*", "modeling/geometry_nodes/curve_primitives/curve_circle.html#bpy-types-geometrynodecurveprimitivecircle"),
("bpy.types.geometrynodecurvequadraticbezier*", "modeling/geometry_nodes/curve_primitives/quadratic_bezier.html#bpy-types-geometrynodecurvequadraticbezier"),
+ ("bpy.types.gpencillayer.use_viewlayer_masks*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-use-viewlayer-masks"),
+ ("bpy.types.greasepencil.onion_keyframe_type*", "grease_pencil/properties/onion_skinning.html#bpy-types-greasepencil-onion-keyframe-type"),
("bpy.types.lineartgpencilmodifier.use_cache*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-cache"),
("bpy.types.lineartgpencilmodifier.use_loose*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-use-loose"),
("bpy.types.materialgpencilstyle.show_stroke*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-show-stroke"),
@@ -596,9 +613,7 @@ url_manual_mapping = (
("bpy.types.spaceclipeditor.show_red_channel*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-show-red-channel"),
("bpy.types.spaceclipeditor.use_mute_footage*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-use-mute-footage"),
("bpy.types.spacenodeoverlay.show_wire_color*", "interface/controls/nodes/introduction.html#bpy-types-spacenodeoverlay-show-wire-color"),
- ("bpy.types.spacesequenceeditor.display_mode*", "video_editing/preview/display_mode.html#bpy-types-spacesequenceeditor-display-mode"),
- ("bpy.types.spacesequenceeditor.overlay_type*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-overlay-type"),
- ("bpy.types.spaceuveditor.sticky_select_mode*", "editors/uv/selecting.html#bpy-types-spaceuveditor-sticky-select-mode"),
+ ("bpy.types.spacesequenceeditor.display_mode*", "editors/video_sequencer/preview/display/display_mode.html#bpy-types-spacesequenceeditor-display-mode"),
("bpy.types.spaceview3d.show_object_viewport*", "editors/3dview/display/visibility.html#bpy-types-spaceview3d-show-object-viewport"),
("bpy.types.view3doverlay.show_fade_inactive*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-show-fade-inactive"),
("bpy.types.viewlayer.pass_cryptomatte_depth*", "render/layers/passes.html#bpy-types-viewlayer-pass-cryptomatte-depth"),
@@ -620,6 +635,8 @@ url_manual_mapping = (
("bpy.types.brushgpencilsettings.show_lasso*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-show-lasso"),
("bpy.types.compositornodeconvertcolorspace*", "compositing/types/converter/color_space.html#bpy-types-compositornodeconvertcolorspace"),
("bpy.types.cyclescurverendersettings.shape*", "render/cycles/render_settings/hair.html#bpy-types-cyclescurverendersettings-shape"),
+ ("bpy.types.cycleslightsettings.cast_shadow*", "render/cycles/light_settings.html#bpy-types-cycleslightsettings-cast-shadow"),
+ ("bpy.types.cycleslightsettings.max_bounces*", "render/cycles/light_settings.html#bpy-types-cycleslightsettings-max-bounces"),
("bpy.types.cyclesrendersettings.ao_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-ao-bounces"),
("bpy.types.cyclesrendersettings.time_limit*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-time-limit"),
("bpy.types.cyclesvisibilitysettings.camera*", "render/cycles/world_settings.html#bpy-types-cyclesvisibilitysettings-camera"),
@@ -637,11 +654,14 @@ url_manual_mapping = (
("bpy.types.freestylelinestyle.split_length*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-split-length"),
("bpy.types.freestylelinestyle.use_chaining*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-chaining"),
("bpy.types.freestylesettings.sphere_radius*", "render/freestyle/view_layer/freestyle.html#bpy-types-freestylesettings-sphere-radius"),
+ ("bpy.types.geometrynodeattributedomainsize*", "modeling/geometry_nodes/attribute/domain_size.html#bpy-types-geometrynodeattributedomainsize"),
("bpy.types.geometrynodesetsplineresolution*", "modeling/geometry_nodes/curve/set_spline_resolution.html#bpy-types-geometrynodesetsplineresolution"),
("bpy.types.gpencillayer.annotation_opacity*", "interface/annotate_tool.html#bpy-types-gpencillayer-annotation-opacity"),
("bpy.types.gpencillayer.use_onion_skinning*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-use-onion-skinning"),
("bpy.types.gpencilsculptguide.use_snapping*", "grease_pencil/modes/draw/guides.html#bpy-types-gpencilsculptguide-use-snapping"),
("bpy.types.gpencilsculptsettings.lock_axis*", "grease_pencil/modes/draw/drawing_planes.html#bpy-types-gpencilsculptsettings-lock-axis"),
+ ("bpy.types.greasepencil.ghost_before_range*", "grease_pencil/properties/onion_skinning.html#bpy-types-greasepencil-ghost-before-range"),
+ ("bpy.types.greasepencil.stroke_depth_order*", "grease_pencil/properties/strokes.html#bpy-types-greasepencil-stroke-depth-order"),
("bpy.types.imageformatsettings.file_format*", "render/output/properties/output.html#bpy-types-imageformatsettings-file-format"),
("bpy.types.imagepaint.use_backface_culling*", "sculpt_paint/texture_paint/tool_settings/options.html#bpy-types-imagepaint-use-backface-culling"),
("bpy.types.lineartgpencilmodifier.overscan*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier-overscan"),
@@ -658,20 +678,19 @@ url_manual_mapping = (
("bpy.types.rendersettings.use_render_cache*", "render/output/properties/output.html#bpy-types-rendersettings-use-render-cache"),
("bpy.types.rendersettings.use_single_layer*", "render/layers/view_layer.html#bpy-types-rendersettings-use-single-layer"),
("bpy.types.sceneeevee.use_taa_reprojection*", "render/eevee/render_settings/sampling.html#bpy-types-sceneeevee-use-taa-reprojection"),
- ("bpy.types.sequenceeditor.use_overlay_lock*", "video_editing/preview/sidebar.html#bpy-types-sequenceeditor-use-overlay-lock"),
("bpy.types.spaceclipeditor.cursor_location*", "editors/clip/sidebar.html#bpy-types-spaceclipeditor-cursor-location"),
("bpy.types.spacefilebrowser.recent_folders*", "editors/file_browser.html#bpy-types-spacefilebrowser-recent-folders"),
("bpy.types.spacefilebrowser.system_folders*", "editors/file_browser.html#bpy-types-spacefilebrowser-system-folders"),
("bpy.types.spacenodeeditor.show_annotation*", "interface/controls/nodes/introduction.html#bpy-types-spacenodeeditor-show-annotation"),
("bpy.types.spaceoutliner.use_filter_object*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object"),
- ("bpy.types.spacesequenceeditor.use_proxies*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-use-proxies"),
+ ("bpy.types.spacesequenceeditor.use_proxies*", "editors/video_sequencer/preview/sidebar.html#bpy-types-spacesequenceeditor-use-proxies"),
("bpy.types.spaceuveditor.edge_display_type*", "editors/uv/overlays.html#bpy-types-spaceuveditor-edge-display-type"),
("bpy.types.spaceuveditor.show_pixel_coords*", "editors/uv/sidebar.html#bpy-types-spaceuveditor-show-pixel-coords"),
("bpy.types.spaceview3d.show_reconstruction*", "editors/3dview/display/overlays.html#bpy-types-spaceview3d-show-reconstruction"),
("bpy.types.toolsettings.gpencil_selectmode*", "grease_pencil/selecting.html#bpy-types-toolsettings-gpencil-selectmode"),
("bpy.types.toolsettings.use_auto_normalize*", "sculpt_paint/weight_paint/tool_settings/options.html#bpy-types-toolsettings-use-auto-normalize"),
("bpy.types.toolsettings.use_mesh_automerge*", "modeling/meshes/tools/tool_settings.html#bpy-types-toolsettings-use-mesh-automerge"),
- ("bpy.types.toolsettings.use_snap_sequencer*", "video_editing/sequencer/editing.html#bpy-types-toolsettings-use-snap-sequencer"),
+ ("bpy.types.toolsettings.use_snap_sequencer*", "video_editing/edit/montage/editing.html#bpy-types-toolsettings-use-snap-sequencer"),
("bpy.types.toolsettings.use_snap_translate*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-translate"),
("bpy.types.toolsettings.use_uv_select_sync*", "editors/uv/selecting.html#bpy-types-toolsettings-use-uv-select-sync"),
("bpy.types.view3doverlay.wireframe_opacity*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-wireframe-opacity"),
@@ -712,15 +731,17 @@ url_manual_mapping = (
("bpy.types.freestylelinestyle.chain_count*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-chain-count"),
("bpy.types.freestylelinestyle.use_sorting*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-sorting"),
("bpy.types.freestylesettings.crease_angle*", "render/freestyle/view_layer/freestyle.html#bpy-types-freestylesettings-crease-angle"),
- ("bpy.types.geometrynodealigneulertovector*", "modeling/geometry_nodes/utilities/align_euler_to_vector.html#bpy-types-geometrynodealigneulertovector"),
+ ("bpy.types.functionnodealigneulertovector*", "modeling/geometry_nodes/utilities/align_euler_to_vector.html#bpy-types-functionnodealigneulertovector"),
("bpy.types.geometrynodeattributestatistic*", "modeling/geometry_nodes/attribute/attribute_statistic.html#bpy-types-geometrynodeattributestatistic"),
- ("bpy.types.geometrynodecurvequadrilateral*", "modeling/geometry_nodes/curve_primitives/quadrilateral.html#bpy-types-geometrynodecurvequadrilateral"),
+ ("bpy.types.geometrynodecurveprimitiveline*", "modeling/geometry_nodes/curve_primitives/curve_line.html#bpy-types-geometrynodecurveprimitiveline"),
("bpy.types.geometrynodegeometrytoinstance*", "modeling/geometry_nodes/geometry/geometry_to_instance.html#bpy-types-geometrynodegeometrytoinstance"),
("bpy.types.geometrynodeinputmaterialindex*", "modeling/geometry_nodes/material/material_index.html#bpy-types-geometrynodeinputmaterialindex"),
("bpy.types.geometrynodeinputmeshedgeangle*", "modeling/geometry_nodes/mesh/edge_angle.html#bpy-types-geometrynodeinputmeshedgeangle"),
("bpy.types.geometrynodeseparatecomponents*", "modeling/geometry_nodes/geometry/separate_components.html#bpy-types-geometrynodeseparatecomponents"),
("bpy.types.geometrynodesubdivisionsurface*", "modeling/geometry_nodes/mesh/subdivision_surface.html#bpy-types-geometrynodesubdivisionsurface"),
("bpy.types.geometrynodetranslateinstances*", "modeling/geometry_nodes/instances/translate_instances.html#bpy-types-geometrynodetranslateinstances"),
+ ("bpy.types.greasepencil.ghost_after_range*", "grease_pencil/properties/onion_skinning.html#bpy-types-greasepencil-ghost-after-range"),
+ ("bpy.types.greasepencil.use_ghosts_always*", "grease_pencil/properties/onion_skinning.html#bpy-types-greasepencil-use-ghosts-always"),
("bpy.types.imageformatsettings.color_mode*", "render/output/properties/output.html#bpy-types-imageformatsettings-color-mode"),
("bpy.types.linestyle*modifier_alongstroke*", "render/freestyle/view_layer/line_style/modifiers/color/along_stroke.html#bpy-types-linestyle-modifier-alongstroke"),
("bpy.types.linestyle*modifier_creaseangle*", "render/freestyle/view_layer/line_style/modifiers/color/crease_angle.html#bpy-types-linestyle-modifier-creaseangle"),
@@ -761,7 +782,7 @@ url_manual_mapping = (
("bpy.ops.outliner.collection_show_inside*", "editors/outliner/editing.html#bpy-ops-outliner-collection-show-inside"),
("bpy.ops.poselib.restore_previous_action*", "animation/armatures/posing/editing/pose_library.html#bpy-ops-poselib-restore-previous-action"),
("bpy.ops.preferences.reset_default_theme*", "editors/preferences/themes.html#bpy-ops-preferences-reset-default-theme"),
- ("bpy.ops.sequencer.strip_transform_clear*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-strip-transform-clear"),
+ ("bpy.ops.sequencer.strip_transform_clear*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-strip-transform-clear"),
("bpy.ops.spreadsheet.add_row_filter_rule*", "editors/spreadsheet.html#bpy-ops-spreadsheet-add-row-filter-rule"),
("bpy.types.animdata.action_extrapolation*", "editors/nla/sidebar.html#bpy-types-animdata-action-extrapolation"),
("bpy.types.animvizmotionpaths.frame_step*", "animation/motion_paths.html#bpy-types-animvizmotionpaths-frame-step"),
@@ -795,12 +816,11 @@ url_manual_mapping = (
("bpy.types.freestylelinestyle.sort_order*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-sort-order"),
("bpy.types.freestylelinestyle.split_dash*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-split-dash"),
("bpy.types.freestylesettings.use_culling*", "render/freestyle/view_layer/freestyle.html#bpy-types-freestylesettings-use-culling"),
- ("bpy.types.geometrynodeendpointselection*", "modeling/geometry_nodes/curve/endpoint_selection.html#bpy-types-geometrynodeendpointselection"),
- ("bpy.types.geometrynodegeometryproximity*", "modeling/geometry_nodes/geometry/geometry_proximity.html#bpy-types-geometrynodegeometryproximity"),
+ ("bpy.types.geometrynodeattributetransfer*", "modeling/geometry_nodes/attribute/transfer_attribute.html#bpy-types-geometrynodeattributetransfer"),
("bpy.types.geometrynodeinputmeshfacearea*", "modeling/geometry_nodes/mesh/face_area.html#bpy-types-geometrynodeinputmeshfacearea"),
("bpy.types.geometrynodeinputsplinecyclic*", "modeling/geometry_nodes/curve/is_spline_cyclic.html#bpy-types-geometrynodeinputsplinecyclic"),
("bpy.types.geometrynodeinstancestopoints*", "modeling/geometry_nodes/instances/instances_to_points.html#bpy-types-geometrynodeinstancestopoints"),
- ("bpy.types.geometrynodetransferattribute*", "modeling/geometry_nodes/attribute/transfer_attribute.html#bpy-types-geometrynodetransferattribute"),
+ ("bpy.types.gpencillayer.viewlayer_render*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-viewlayer-render"),
("bpy.types.layercollection.hide_viewport*", "editors/outliner/interface.html#bpy-types-layercollection-hide-viewport"),
("bpy.types.layercollection.indirect_only*", "editors/outliner/interface.html#bpy-types-layercollection-indirect-only"),
("bpy.types.material.use_sss_translucency*", "render/eevee/materials/settings.html#bpy-types-material-use-sss-translucency"),
@@ -835,7 +855,7 @@ url_manual_mapping = (
("bpy.ops.gpencil.image_to_grease_pencil*", "editors/image/editing.html#bpy-ops-gpencil-image-to-grease-pencil"),
("bpy.ops.mesh.vertices_smooth_laplacian*", "modeling/meshes/editing/vertex/laplacian_smooth.html#bpy-ops-mesh-vertices-smooth-laplacian"),
("bpy.ops.object.multires_rebuild_subdiv*", "modeling/modifiers/generate/multiresolution.html#bpy-ops-object-multires-rebuild-subdiv"),
- ("bpy.ops.sequencer.select_side_of_frame*", "video_editing/sequencer/selecting.html#bpy-ops-sequencer-select-side-of-frame"),
+ ("bpy.ops.sequencer.select_side_of_frame*", "video_editing/edit/montage/selecting.html#bpy-ops-sequencer-select-side-of-frame"),
("bpy.types.animvizmotionpaths.frame_end*", "animation/motion_paths.html#bpy-types-animvizmotionpaths-frame-end"),
("bpy.types.armature.rigify_colors_index*", "addons/rigging/rigify/metarigs.html#bpy-types-armature-rigify-colors-index"),
("bpy.types.armature.rigify_theme_to_add*", "addons/rigging/rigify/metarigs.html#bpy-types-armature-rigify-theme-to-add"),
@@ -848,6 +868,7 @@ url_manual_mapping = (
("bpy.types.compositornodebrightcontrast*", "compositing/types/color/bright_contrast.html#bpy-types-compositornodebrightcontrast"),
("bpy.types.compositornodedoubleedgemask*", "compositing/types/matte/double_edge_mask.html#bpy-types-compositornodedoubleedgemask"),
("bpy.types.cyclesrendersettings.samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-samples"),
+ ("bpy.types.dopesheet.show_only_selected*", "editors/dope_sheet/introduction.html#bpy-types-dopesheet-show-only-selected"),
("bpy.types.fileselectparams.show_hidden*", "editors/file_browser.html#bpy-types-fileselectparams-show-hidden"),
("bpy.types.fileselectparams.sort_method*", "editors/file_browser.html#bpy-types-fileselectparams-sort-method"),
("bpy.types.fluiddomainsettings.clipping*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-clipping"),
@@ -857,11 +878,13 @@ url_manual_mapping = (
("bpy.types.freestylelinestyle.split_gap*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-split-gap"),
("bpy.types.freestylelinestyle.use_nodes*", "render/freestyle/view_layer/line_style/texture.html#bpy-types-freestylelinestyle-use-nodes"),
("bpy.types.geometrynodecaptureattribute*", "modeling/geometry_nodes/attribute/capture_attribute.html#bpy-types-geometrynodecaptureattribute"),
+ ("bpy.types.geometrynodeinputshadesmooth*", "modeling/geometry_nodes/mesh/is_shade_smooth.html#bpy-types-geometrynodeinputshadesmooth"),
("bpy.types.geometrynodeinstanceonpoints*", "modeling/geometry_nodes/instances/instance_on_points.html#bpy-types-geometrynodeinstanceonpoints"),
("bpy.types.geometrynodepointstovertices*", "modeling/geometry_nodes/point/points_to_vertices.html#bpy-types-geometrynodepointstovertices"),
("bpy.types.geometrynoderealizeinstances*", "modeling/geometry_nodes/instances/realize_instances.html#bpy-types-geometrynoderealizeinstances"),
("bpy.types.geometrynodeseparategeometry*", "modeling/geometry_nodes/geometry/separate_geometry.html#bpy-types-geometrynodeseparategeometry"),
("bpy.types.geometrynodesetmaterialindex*", "modeling/geometry_nodes/material/set_material_index.html#bpy-types-geometrynodesetmaterialindex"),
+ ("bpy.types.greasepencil.edit_line_color*", "grease_pencil/properties/display.html#bpy-types-greasepencil-edit-line-color"),
("bpy.types.material.preview_render_type*", "render/materials/preview.html#bpy-types-material-preview-render-type"),
("bpy.types.materialgpencilstyle.pattern*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-pattern"),
("bpy.types.materialgpencilstyle.texture*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-texture"),
@@ -873,7 +896,7 @@ url_manual_mapping = (
("bpy.types.rendersettings.use_overwrite*", "render/output/properties/output.html#bpy-types-rendersettings-use-overwrite"),
("bpy.types.rendersettings.use_sequencer*", "render/output/properties/post_processing.html#bpy-types-rendersettings-use-sequencer"),
("bpy.types.sceneeevee.volumetric_shadow*", "render/eevee/render_settings/volumetrics.html#bpy-types-sceneeevee-volumetric-shadow"),
- ("bpy.types.sequenceeditor.overlay_frame*", "video_editing/preview/sidebar.html#bpy-types-sequenceeditor-overlay-frame"),
+ ("bpy.types.sequenceeditor.overlay_frame*", "editors/video_sequencer/preview/sidebar.html#bpy-types-sequenceeditor-overlay-frame"),
("bpy.types.shadernodebsdfhairprincipled*", "render/shader_nodes/shader/hair_principled.html#bpy-types-shadernodebsdfhairprincipled"),
("bpy.types.shadernodevectordisplacement*", "render/shader_nodes/vector/vector_displacement.html#bpy-types-shadernodevectordisplacement"),
("bpy.types.spacegrapheditor.show_cursor*", "editors/graph_editor/introduction.html#bpy-types-spacegrapheditor-show-cursor"),
@@ -903,9 +926,9 @@ url_manual_mapping = (
("bpy.ops.object.visual_transform_apply*", "scene_layout/object/editing/apply.html#bpy-ops-object-visual-transform-apply"),
("bpy.ops.outliner.collection_duplicate*", "editors/outliner/editing.html#bpy-ops-outliner-collection-duplicate"),
("bpy.ops.pose.select_constraint_target*", "animation/armatures/posing/selecting.html#bpy-ops-pose-select-constraint-target"),
- ("bpy.ops.sequencer.change_effect_input*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-change-effect-input"),
- ("bpy.ops.sequencer.strip_color_tag_set*", "video_editing/sequencer/sidebar/strip.html#bpy-ops-sequencer-strip-color-tag-set"),
- ("bpy.ops.sequencer.strip_transform_fit*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-strip-transform-fit"),
+ ("bpy.ops.sequencer.change_effect_input*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-change-effect-input"),
+ ("bpy.ops.sequencer.strip_color_tag_set*", "editors/video_sequencer/sequencer/sidebar/strip.html#bpy-ops-sequencer-strip-color-tag-set"),
+ ("bpy.ops.sequencer.strip_transform_fit*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-strip-transform-fit"),
("bpy.types.armature.rigify_colors_lock*", "addons/rigging/rigify/metarigs.html#bpy-types-armature-rigify-colors-lock"),
("bpy.types.bakesettings.cage_extrusion*", "render/cycles/baking.html#bpy-types-bakesettings-cage-extrusion"),
("bpy.types.bakesettings.use_pass_color*", "render/cycles/baking.html#bpy-types-bakesettings-use-pass-color"),
@@ -918,6 +941,7 @@ url_manual_mapping = (
("bpy.types.colormanageddisplaysettings*", "render/color_management.html#bpy-types-colormanageddisplaysettings"),
("bpy.types.colorramp.hue_interpolation*", "interface/controls/templates/color_ramp.html#bpy-types-colorramp-hue-interpolation"),
("bpy.types.compositornodebilateralblur*", "compositing/types/filter/bilateral_blur.html#bpy-types-compositornodebilateralblur"),
+ ("bpy.types.compositornodecryptomattev2*", "compositing/types/matte/cryptomatte.html#bpy-types-compositornodecryptomattev2"),
("bpy.types.compositornodedistancematte*", "compositing/types/matte/distance_key.html#bpy-types-compositornodedistancematte"),
("bpy.types.compositornodesetalpha.mode*", "compositing/types/converter/set_alpha.html#bpy-types-compositornodesetalpha-mode"),
("bpy.types.dopesheet.use_filter_invert*", "editors/graph_editor/channels.html#bpy-types-dopesheet-use-filter-invert"),
@@ -941,6 +965,8 @@ url_manual_mapping = (
("bpy.types.geometrynodesplineparameter*", "modeling/geometry_nodes/curve/spline_parameter.html#bpy-types-geometrynodesplineparameter"),
("bpy.types.gpencillayer.use_mask_layer*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-use-mask-layer"),
("bpy.types.greasepencil.use_curve_edit*", "grease_pencil/modes/edit/curve_editing.html#bpy-types-greasepencil-use-curve-edit"),
+ ("bpy.types.greasepencil.use_onion_fade*", "grease_pencil/properties/onion_skinning.html#bpy-types-greasepencil-use-onion-fade"),
+ ("bpy.types.greasepencil.use_onion_loop*", "grease_pencil/properties/onion_skinning.html#bpy-types-greasepencil-use-onion-loop"),
("bpy.types.imagepaint.screen_grab_size*", "sculpt_paint/texture_paint/tool_settings/options.html#bpy-types-imagepaint-screen-grab-size"),
("bpy.types.linestyle*modifier_material*", "render/freestyle/view_layer/line_style/modifiers/color/material.html#bpy-types-linestyle-modifier-material"),
("bpy.types.motionpath.use_custom_color*", "animation/motion_paths.html#bpy-types-motionpath-use-custom-color"),
@@ -959,9 +985,9 @@ url_manual_mapping = (
("bpy.types.sceneeevee.volumetric_light*", "render/eevee/render_settings/volumetrics.html#bpy-types-sceneeevee-volumetric-light"),
("bpy.types.sculpt.detail_refine_method*", "sculpt_paint/sculpting/tool_settings/dyntopo.html#bpy-types-sculpt-detail-refine-method"),
("bpy.types.sculpt.symmetrize_direction*", "sculpt_paint/sculpting/tool_settings/symmetry.html#bpy-types-sculpt-symmetrize-direction"),
- ("bpy.types.sequenceeditor.show_overlay*", "video_editing/preview/sidebar.html#bpy-types-sequenceeditor-show-overlay"),
- ("bpy.types.sequenceeditor.use_prefetch*", "video_editing/preview/sidebar.html#bpy-types-sequenceeditor-use-prefetch"),
- ("bpy.types.soundsequence.show_waveform*", "video_editing/sequencer/sidebar/strip.html#bpy-types-soundsequence-show-waveform"),
+ ("bpy.types.sequenceeditor.show_overlay*", "editors/video_sequencer/preview/sidebar.html#bpy-types-sequenceeditor-show-overlay"),
+ ("bpy.types.sequenceeditor.use_prefetch*", "editors/video_sequencer/preview/sidebar.html#bpy-types-sequenceeditor-use-prefetch"),
+ ("bpy.types.soundsequence.show_waveform*", "editors/video_sequencer/sequencer/sidebar/strip.html#bpy-types-soundsequence-show-waveform"),
("bpy.types.spaceclipeditor.show_stable*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-show-stable"),
("bpy.types.spaceoutliner.filter_invert*", "editors/outliner/interface.html#bpy-types-spaceoutliner-filter-invert"),
("bpy.types.spacetexteditor.show_margin*", "editors/text_editor.html#bpy-types-spacetexteditor-show-margin"),
@@ -983,7 +1009,7 @@ url_manual_mapping = (
("bpy.ops.object.parent_no_inverse_set*", "scene_layout/object/editing/parent.html#bpy-ops-object-parent-no-inverse-set"),
("bpy.ops.object.vertex_group_quantize*", "sculpt_paint/weight_paint/editing.html#bpy-ops-object-vertex-group-quantize"),
("bpy.ops.outliner.collection_instance*", "editors/outliner/editing.html#bpy-ops-outliner-collection-instance"),
- ("bpy.ops.sequencer.change_effect_type*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-change-effect-type"),
+ ("bpy.ops.sequencer.change_effect_type*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-change-effect-type"),
("bpy.ops.transform.create_orientation*", "editors/3dview/controls/orientation.html#bpy-ops-transform-create-orientation"),
("bpy.ops.transform.delete_orientation*", "editors/3dview/controls/orientation.html#bpy-ops-transform-delete-orientation"),
("bpy.ops.view3d.localview_remove_from*", "editors/3dview/navigate/local_view.html#bpy-ops-view3d-localview-remove-from"),
@@ -1018,6 +1044,8 @@ url_manual_mapping = (
("bpy.types.geometrynodesetshadesmooth*", "modeling/geometry_nodes/mesh/set_shade_smooth.html#bpy-types-geometrynodesetshadesmooth"),
("bpy.types.geometrynodestringtocurves*", "modeling/geometry_nodes/text/string_to_curves.html#bpy-types-geometrynodestringtocurves"),
("bpy.types.geometrynodesubdividecurve*", "modeling/geometry_nodes/curve/subdivide_curve.html#bpy-types-geometrynodesubdividecurve"),
+ ("bpy.types.gpencillayer.channel_color*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-channel-color"),
+ ("bpy.types.gpencillayer.use_solo_mode*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-use-solo-mode"),
("bpy.types.greasepencil.use_multiedit*", "grease_pencil/multiframe.html#bpy-types-greasepencil-use-multiedit"),
("bpy.types.keyframe.handle_right_type*", "editors/graph_editor/fcurves/properties.html#bpy-types-keyframe-handle-right-type"),
("bpy.types.materialgpencilstyle.color*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-color"),
@@ -1025,9 +1053,10 @@ url_manual_mapping = (
("bpy.types.movietrackingstabilization*", "movie_clip/tracking/clip/sidebar/stabilization/index.html#bpy-types-movietrackingstabilization"),
("bpy.types.object.display_bounds_type*", "scene_layout/object/properties/display.html#bpy-types-object-display-bounds-type"),
("bpy.types.regionview3d.lock_rotation*", "editors/3dview/navigate/views.html#bpy-types-regionview3d-lock-rotation"),
+ ("bpy.types.rendersettings.hair_subdiv*", "render/cycles/render_settings/hair.html#bpy-types-rendersettings-hair-subdiv"),
("bpy.types.scene.audio_distance_model*", "scene_layout/scene/properties.html#bpy-types-scene-audio-distance-model"),
("bpy.types.scene.audio_doppler_factor*", "scene_layout/scene/properties.html#bpy-types-scene-audio-doppler-factor"),
- ("bpy.types.sequencetransform.rotation*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sequencetransform-rotation"),
+ ("bpy.types.sequencetransform.rotation*", "editors/video_sequencer/sequencer/sidebar/strip.html#bpy-types-sequencetransform-rotation"),
("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"),
@@ -1063,7 +1092,7 @@ url_manual_mapping = (
("bpy.ops.pose.visual_transform_apply*", "animation/armatures/posing/editing/apply.html#bpy-ops-pose-visual-transform-apply"),
("bpy.ops.poselib.convert_old_poselib*", "animation/armatures/posing/editing/pose_library.html#bpy-ops-poselib-convert-old-poselib"),
("bpy.ops.render.shutter_curve_preset*", "render/cycles/render_settings/motion_blur.html#bpy-ops-render-shutter-curve-preset"),
- ("bpy.ops.sequencer.view_ghost_border*", "video_editing/preview/sidebar.html#bpy-ops-sequencer-view-ghost-border"),
+ ("bpy.ops.sequencer.view_ghost_border*", "editors/video_sequencer/preview/sidebar.html#bpy-ops-sequencer-view-ghost-border"),
("bpy.ops.ui.override_type_set_button*", "files/linked_libraries/library_overrides.html#bpy-ops-ui-override-type-set-button"),
("bpy.types.animdata.action_influence*", "editors/nla/sidebar.html#bpy-types-animdata-action-influence"),
("bpy.types.armature.layers_protected*", "animation/armatures/properties/skeleton.html#bpy-types-armature-layers-protected"),
@@ -1078,6 +1107,7 @@ url_manual_mapping = (
("bpy.types.camera.passepartout_alpha*", "render/cameras.html#bpy-types-camera-passepartout-alpha"),
("bpy.types.colorrampelement.position*", "interface/controls/templates/color_ramp.html#bpy-types-colorrampelement-position"),
("bpy.types.compositornodechromamatte*", "compositing/types/matte/chroma_key.html#bpy-types-compositornodechromamatte"),
+ ("bpy.types.compositornodecryptomatte*", "compositing/types/matte/cryptomatte_legacy.html#bpy-types-compositornodecryptomatte"),
("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"),
@@ -1090,20 +1120,23 @@ url_manual_mapping = (
("bpy.types.fluidflowsettings.density*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-density"),
("bpy.types.freestylelineset.qi_start*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-qi-start"),
("bpy.types.freestylelinestyle.rounds*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-rounds"),
+ ("bpy.types.functionnodereplacestring*", "modeling/geometry_nodes/text/replace_string.html#bpy-types-functionnodereplacestring"),
+ ("bpy.types.functionnodevaluetostring*", "modeling/geometry_nodes/text/value_to_string.html#bpy-types-functionnodevaluetostring"),
("bpy.types.geometrynodecurvetopoints*", "modeling/geometry_nodes/curve/curve_to_points.html#bpy-types-geometrynodecurvetopoints"),
("bpy.types.geometrynodeinputmaterial*", "modeling/geometry_nodes/input/material.html#bpy-types-geometrynodeinputmaterial"),
("bpy.types.geometrynodeinputposition*", "modeling/geometry_nodes/input/position.html#bpy-types-geometrynodeinputposition"),
- ("bpy.types.geometrynodeisshadesmooth*", "modeling/geometry_nodes/mesh/is_shade_smooth.html#bpy-types-geometrynodeisshadesmooth"),
("bpy.types.geometrynodemeshicosphere*", "modeling/geometry_nodes/mesh_primitives/icosphere.html#bpy-types-geometrynodemeshicosphere"),
- ("bpy.types.geometrynodereplacestring*", "modeling/geometry_nodes/text/replace_string.html#bpy-types-geometrynodereplacestring"),
("bpy.types.geometrynoderesamplecurve*", "modeling/geometry_nodes/curve/resample_curve.html#bpy-types-geometrynoderesamplecurve"),
("bpy.types.geometrynodescaleelements*", "modeling/geometry_nodes/mesh/scale_elements.html#bpy-types-geometrynodescaleelements"),
("bpy.types.geometrynodesubdividemesh*", "modeling/geometry_nodes/mesh/subdivide_mesh.html#bpy-types-geometrynodesubdividemesh"),
- ("bpy.types.geometrynodevaluetostring*", "modeling/geometry_nodes/text/value_to_string.html#bpy-types-geometrynodevaluetostring"),
+ ("bpy.types.greasepencil.before_color*", "grease_pencil/properties/onion_skinning.html#bpy-types-greasepencil-before-color"),
+ ("bpy.types.greasepencil.onion_factor*", "grease_pencil/properties/onion_skinning.html#bpy-types-greasepencil-onion-factor"),
+ ("bpy.types.greasepencil.pixel_factor*", "grease_pencil/properties/strokes.html#bpy-types-greasepencil-pixel-factor"),
("bpy.types.keyframe.handle_left_type*", "editors/graph_editor/fcurves/properties.html#bpy-types-keyframe-handle-left-type"),
("bpy.types.light.use_custom_distance*", "render/eevee/lighting.html#bpy-types-light-use-custom-distance"),
("bpy.types.materialgpencilstyle.flip*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-flip"),
("bpy.types.materialgpencilstyle.mode*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-mode"),
+ ("bpy.types.meshsequencecachemodifier*", "modeling/modifiers/modify/mesh_sequence_cache.html#bpy-types-meshsequencecachemodifier"),
("bpy.types.modifier.show_in_editmode*", "modeling/modifiers/introduction.html#bpy-types-modifier-show-in-editmode"),
("bpy.types.motionpath.line_thickness*", "animation/motion_paths.html#bpy-types-motionpath-line-thickness"),
("bpy.types.object.empty_display_size*", "modeling/empties.html#bpy-types-object-empty-display-size"),
@@ -1116,10 +1149,11 @@ url_manual_mapping = (
("bpy.types.sceneeevee.bokeh_max_size*", "render/eevee/render_settings/depth_of_field.html#bpy-types-sceneeevee-bokeh-max-size"),
("bpy.types.sculpt.detail_type_method*", "sculpt_paint/sculpting/tool_settings/dyntopo.html#bpy-types-sculpt-detail-type-method"),
("bpy.types.sculpt.use_smooth_shading*", "sculpt_paint/sculpting/tool_settings/dyntopo.html#bpy-types-sculpt-use-smooth-shading"),
- ("bpy.types.sequenceeditor.show_cache*", "video_editing/sequencer/navigating.html#bpy-types-sequenceeditor-show-cache"),
+ ("bpy.types.sequenceeditor.show_cache*", "editors/video_sequencer/sequencer/navigating.html#bpy-types-sequenceeditor-show-cache"),
("bpy.types.shadernodebsdfanisotropic*", "render/shader_nodes/shader/anisotropic.html#bpy-types-shadernodebsdfanisotropic"),
("bpy.types.shadernodebsdftranslucent*", "render/shader_nodes/shader/translucent.html#bpy-types-shadernodebsdftranslucent"),
("bpy.types.shadernodebsdftransparent*", "render/shader_nodes/shader/transparent.html#bpy-types-shadernodebsdftransparent"),
+ ("bpy.types.shadernodetexpointdensity*", "render/shader_nodes/textures/point_density.html#bpy-types-shadernodetexpointdensity"),
("bpy.types.shadernodevectortransform*", "render/shader_nodes/vector/transform.html#bpy-types-shadernodevectortransform"),
("bpy.types.shrinkwrapgpencilmodifier*", "grease_pencil/modifiers/deform/shrinkwrap.html#bpy-types-shrinkwrapgpencilmodifier"),
("bpy.types.spaceclipeditor.show_grid*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-show-grid"),
@@ -1158,8 +1192,8 @@ url_manual_mapping = (
("bpy.ops.pose.user_transforms_clear*", "animation/armatures/posing/editing/clear.html#bpy-ops-pose-user-transforms-clear"),
("bpy.ops.poselib.browse_interactive*", "animation/armatures/posing/editing/pose_library.html#bpy-ops-poselib-browse-interactive"),
("bpy.ops.sculpt.set_persistent_base*", "sculpt_paint/sculpting/tools/layer.html#bpy-ops-sculpt-set-persistent-base"),
- ("bpy.ops.sequencer.crossfade_sounds*", "video_editing/sequencer/strips/transitions/sound_crossfade.html#bpy-ops-sequencer-crossfade-sounds"),
- ("bpy.ops.sequencer.export_subtitles*", "video_editing/preview/introduction.html#bpy-ops-sequencer-export-subtitles"),
+ ("bpy.ops.sequencer.crossfade_sounds*", "video_editing/edit/montage/strips/transitions/sound_crossfade.html#bpy-ops-sequencer-crossfade-sounds"),
+ ("bpy.ops.sequencer.export_subtitles*", "editors/video_sequencer/preview/header.html#bpy-ops-sequencer-export-subtitles"),
("bpy.ops.transform.edge_bevelweight*", "modeling/meshes/editing/edge/edge_data.html#bpy-ops-transform-edge-bevelweight"),
("bpy.ops.wm.previews_batch_generate*", "files/blend/previews.html#bpy-ops-wm-previews-batch-generate"),
("bpy.types.assetmetadata.active_tag*", "editors/asset_browser.html#bpy-types-assetmetadata-active-tag"),
@@ -1187,7 +1221,9 @@ url_manual_mapping = (
("bpy.types.freestylelineset.exclude*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-exclude"),
("bpy.types.freestylelinestyle.alpha*", "render/freestyle/view_layer/line_style/alpha.html#bpy-types-freestylelinestyle-alpha"),
("bpy.types.freestylelinestyle.color*", "render/freestyle/view_layer/line_style/color.html#bpy-types-freestylelinestyle-color"),
+ ("bpy.types.functionnodestringlength*", "modeling/geometry_nodes/text/string_length.html#bpy-types-functionnodestringlength"),
("bpy.types.geometrynodefieldatindex*", "modeling/geometry_nodes/utilities/field_at_index.html#bpy-types-geometrynodefieldatindex"),
+ ("bpy.types.geometrynodeimagetexture*", "modeling/geometry_nodes/texture/image.html#bpy-types-geometrynodeimagetexture"),
("bpy.types.geometrynodeinputtangent*", "modeling/geometry_nodes/curve/curve_tangent.html#bpy-types-geometrynodeinputtangent"),
("bpy.types.geometrynodejoingeometry*", "modeling/geometry_nodes/geometry/join_geometry.html#bpy-types-geometrynodejoingeometry"),
("bpy.types.geometrynodemeshcylinder*", "modeling/geometry_nodes/mesh_primitives/cylinder.html#bpy-types-geometrynodemeshcylinder"),
@@ -1196,8 +1232,11 @@ url_manual_mapping = (
("bpy.types.geometrynodereversecurve*", "modeling/geometry_nodes/curve/reverse_curve.html#bpy-types-geometrynodereversecurve"),
("bpy.types.geometrynodesetcurvetilt*", "modeling/geometry_nodes/curve/set_curve_tilt.html#bpy-types-geometrynodesetcurvetilt"),
("bpy.types.geometrynodesplinelength*", "modeling/geometry_nodes/curve/spline_length.html#bpy-types-geometrynodesplinelength"),
- ("bpy.types.geometrynodestringlength*", "modeling/geometry_nodes/text/string_length.html#bpy-types-geometrynodestringlength"),
("bpy.types.geometrynodevolumetomesh*", "modeling/geometry_nodes/volume/volume_to_mesh.html#bpy-types-geometrynodevolumetomesh"),
+ ("bpy.types.gpencillayer.line_change*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-line-change"),
+ ("bpy.types.gpencillayer.parent_type*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-parent-type"),
+ ("bpy.types.gpencillayer.tint_factor*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-tint-factor"),
+ ("bpy.types.greasepencil.after_color*", "grease_pencil/properties/onion_skinning.html#bpy-types-greasepencil-after-color"),
("bpy.types.image.use_half_precision*", "editors/image/image_settings.html#bpy-types-image-use-half-precision"),
("bpy.types.image.use_view_as_render*", "editors/image/image_settings.html#bpy-types-image-use-view-as-render"),
("bpy.types.imagepaint.interpolation*", "sculpt_paint/texture_paint/tool_settings/texture_slots.html#bpy-types-imagepaint-interpolation"),
@@ -1208,15 +1247,16 @@ url_manual_mapping = (
("bpy.types.nodesocketinterface.name*", "interface/controls/nodes/groups.html#bpy-types-nodesocketinterface-name"),
("bpy.types.object.is_shadow_catcher*", "render/cycles/object_settings/object_data.html#bpy-types-object-is-shadow-catcher"),
("bpy.types.particleinstancemodifier*", "modeling/modifiers/physics/particle_instance.html#bpy-types-particleinstancemodifier"),
+ ("bpy.types.rendersettings.hair_type*", "render/eevee/render_settings/hair.html#bpy-types-rendersettings-hair-type"),
("bpy.types.rendersettings.tile_size*", "render/cycles/render_settings/performance.html#bpy-types-rendersettings-tile-size"),
("bpy.types.sequencertimelineoverlay*", "editors/video_sequencer/sequencer/display.html#bpy-types-sequencertimelineoverlay"),
- ("bpy.types.sequencetransform.offset*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sequencetransform-offset"),
+ ("bpy.types.sequencetransform.offset*", "editors/video_sequencer/sequencer/sidebar/strip.html#bpy-types-sequencetransform-offset"),
("bpy.types.shadernodebrightcontrast*", "render/shader_nodes/color/bright_contrast.html#bpy-types-shadernodebrightcontrast"),
("bpy.types.shadernodebsdfprincipled*", "render/shader_nodes/shader/principled.html#bpy-types-shadernodebsdfprincipled"),
("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.spacesequenceeditor.show*", "video_editing/preview/introduction.html#bpy-types-spacesequenceeditor-show"),
+ ("bpy.types.spacesequenceeditor.show*", "editors/video_sequencer/preview/header.html#bpy-types-spacesequenceeditor-show"),
("bpy.types.spaceuveditor.show_faces*", "editors/uv/overlays.html#bpy-types-spaceuveditor-show-faces"),
("bpy.types.spaceuveditor.uv_opacity*", "editors/uv/overlays.html#bpy-types-spaceuveditor-uv-opacity"),
("bpy.types.subdividegpencilmodifier*", "grease_pencil/modifiers/generate/subdivide.html#bpy-types-subdividegpencilmodifier"),
@@ -1226,6 +1266,7 @@ url_manual_mapping = (
("bpy.types.unitsettings.length_unit*", "scene_layout/scene/properties.html#bpy-types-unitsettings-length-unit"),
("bpy.types.vertexweighteditmodifier*", "modeling/modifiers/modify/weight_edit.html#bpy-types-vertexweighteditmodifier"),
("bpy.types.volumedisplay.slice_axis*", "modeling/volumes/properties.html#bpy-types-volumedisplay-slice-axis"),
+ ("bpy.ops.action.markers_make_local*", "animation/markers.html#bpy-ops-action-markers-make-local"),
("bpy.ops.anim.channels_clean_empty*", "editors/nla/editing.html#bpy-ops-anim-channels-clean-empty"),
("bpy.ops.armature.select_hierarchy*", "animation/armatures/bones/selecting.html#bpy-ops-armature-select-hierarchy"),
("bpy.ops.armature.switch_direction*", "animation/armatures/bones/editing/switch_direction.html#bpy-ops-armature-switch-direction"),
@@ -1234,6 +1275,7 @@ url_manual_mapping = (
("bpy.ops.clip.setup_tracking_scene*", "movie_clip/tracking/clip/editing/clip.html#bpy-ops-clip-setup-tracking-scene"),
("bpy.ops.curve.match_texture_space*", "modeling/meshes/uv/uv_texture_spaces.html#bpy-ops-curve-match-texture-space"),
("bpy.ops.font.text_paste_from_file*", "modeling/texts/editing.html#bpy-ops-font-text-paste-from-file"),
+ ("bpy.ops.geometry.attribute_remove*", "modeling/geometry_nodes/attributes_reference.html#bpy-ops-geometry-attribute-remove"),
("bpy.ops.gpencil.frame_clean_loose*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-frame-clean-loose"),
("bpy.ops.mask.primitive_circle_add*", "movie_clip/masking/scurve.html#bpy-ops-mask-primitive-circle-add"),
("bpy.ops.mask.primitive_square_add*", "movie_clip/masking/scurve.html#bpy-ops-mask-primitive-square-add"),
@@ -1248,10 +1290,10 @@ url_manual_mapping = (
("bpy.ops.preferences.theme_install*", "editors/preferences/themes.html#bpy-ops-preferences-theme-install"),
("bpy.ops.render.play_rendered_anim*", "render/output/animation_player.html#bpy-ops-render-play-rendered-anim"),
("bpy.ops.sculpt.set_pivot_position*", "sculpt_paint/sculpting/editing/sculpt.html#bpy-ops-sculpt-set-pivot-position"),
- ("bpy.ops.sequencer.image_strip_add*", "video_editing/sequencer/strips/image.html#bpy-ops-sequencer-image-strip-add"),
- ("bpy.ops.sequencer.movie_strip_add*", "video_editing/sequencer/strips/movie.html#bpy-ops-sequencer-movie-strip-add"),
- ("bpy.ops.sequencer.reassign_inputs*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-reassign-inputs"),
- ("bpy.ops.sequencer.sound_strip_add*", "video_editing/sequencer/strips/sound.html#bpy-ops-sequencer-sound-strip-add"),
+ ("bpy.ops.sequencer.image_strip_add*", "video_editing/edit/montage/strips/image.html#bpy-ops-sequencer-image-strip-add"),
+ ("bpy.ops.sequencer.movie_strip_add*", "video_editing/edit/montage/strips/movie.html#bpy-ops-sequencer-movie-strip-add"),
+ ("bpy.ops.sequencer.reassign_inputs*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-reassign-inputs"),
+ ("bpy.ops.sequencer.sound_strip_add*", "video_editing/edit/montage/strips/sound.html#bpy-ops-sequencer-sound-strip-add"),
("bpy.ops.ui.remove_override_button*", "files/linked_libraries/library_overrides.html#bpy-ops-ui-remove-override-button"),
("bpy.ops.view3d.view_center_camera*", "editors/3dview/navigate/camera_view.html#bpy-ops-view3d-view-center-camera"),
("bpy.types.animvizmotionpaths.type*", "animation/motion_paths.html#bpy-types-animvizmotionpaths-type"),
@@ -1269,6 +1311,7 @@ url_manual_mapping = (
("bpy.types.compositornodelumamatte*", "compositing/types/matte/luminance_key.html#bpy-types-compositornodelumamatte"),
("bpy.types.compositornodemovieclip*", "compositing/types/input/movie_clip.html#bpy-types-compositornodemovieclip"),
("bpy.types.compositornodenormalize*", "compositing/types/vector/normalize.html#bpy-types-compositornodenormalize"),
+ ("bpy.types.compositornodeposterize*", "compositing/types/color/posterize.html#bpy-types-compositornodeposterize"),
("bpy.types.compositornodepremulkey*", "compositing/types/converter/alpha_convert.html#bpy-types-compositornodepremulkey"),
("bpy.types.compositornodescenetime*", "compositing/types/input/scene_time.html#bpy-types-compositornodescenetime"),
("bpy.types.compositornodestabilize*", "compositing/types/distort/stabilize_2d.html#bpy-types-compositornodestabilize"),
@@ -1280,6 +1323,7 @@ url_manual_mapping = (
("bpy.types.editbone.bbone_curveinz*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-curveinz"),
("bpy.types.editbone.bbone_scaleout*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-scaleout"),
("bpy.types.editbone.bbone_segments*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-segments"),
+ ("bpy.types.envelopegpencilmodifier*", "grease_pencil/modifiers/generate/envelope.html#bpy-types-envelopegpencilmodifier"),
("bpy.types.freestylelineset.qi_end*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-qi-end"),
("bpy.types.freestylelinestyle.caps*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-caps"),
("bpy.types.freestylelinestyle.dash*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-dash"),
@@ -1287,7 +1331,9 @@ url_manual_mapping = (
("bpy.types.functionnodebooleanmath*", "modeling/geometry_nodes/utilities/boolean_math.html#bpy-types-functionnodebooleanmath"),
("bpy.types.functionnodeinputstring*", "modeling/geometry_nodes/input/string.html#bpy-types-functionnodeinputstring"),
("bpy.types.functionnodeinputvector*", "modeling/geometry_nodes/input/vector.html#bpy-types-functionnodeinputvector"),
- ("bpy.types.geometrynodecurvecircle*", "modeling/geometry_nodes/curve_primitives/curve_circle.html#bpy-types-geometrynodecurvecircle"),
+ ("bpy.types.functionnoderandomvalue*", "modeling/geometry_nodes/utilities/random_value.html#bpy-types-functionnoderandomvalue"),
+ ("bpy.types.functionnoderotateeuler*", "modeling/geometry_nodes/utilities/rotate_euler.html#bpy-types-functionnoderotateeuler"),
+ ("bpy.types.functionnodeslicestring*", "modeling/geometry_nodes/text/slice_string.html#bpy-types-functionnodeslicestring"),
("bpy.types.geometrynodecurvelength*", "modeling/geometry_nodes/curve/curve_length.html#bpy-types-geometrynodecurvelength"),
("bpy.types.geometrynodecurvespiral*", "modeling/geometry_nodes/curve_primitives/curve_spiral.html#bpy-types-geometrynodecurvespiral"),
("bpy.types.geometrynodecurvetomesh*", "modeling/geometry_nodes/curve/curve_to_mesh.html#bpy-types-geometrynodecurvetomesh"),
@@ -1295,19 +1341,19 @@ url_manual_mapping = (
("bpy.types.geometrynodefilletcurve*", "modeling/geometry_nodes/curve/fillet_curve.html#bpy-types-geometrynodefilletcurve"),
("bpy.types.geometrynodeinputnormal*", "modeling/geometry_nodes/input/normal.html#bpy-types-geometrynodeinputnormal"),
("bpy.types.geometrynodeinputradius*", "modeling/geometry_nodes/input/radius.html#bpy-types-geometrynodeinputradius"),
- ("bpy.types.geometrynodejoinstrings*", "modeling/geometry_nodes/text/join_strings.html#bpy-types-geometrynodejoinstrings"),
("bpy.types.geometrynodemeshboolean*", "modeling/geometry_nodes/mesh/mesh_boolean.html#bpy-types-geometrynodemeshboolean"),
("bpy.types.geometrynodemeshtocurve*", "modeling/geometry_nodes/mesh/mesh_to_curve.html#bpy-types-geometrynodemeshtocurve"),
- ("bpy.types.geometrynoderandomvalue*", "modeling/geometry_nodes/utilities/random_value.html#bpy-types-geometrynoderandomvalue"),
- ("bpy.types.geometrynoderotateeuler*", "modeling/geometry_nodes/utilities/rotate_euler.html#bpy-types-geometrynoderotateeuler"),
("bpy.types.geometrynodesamplecurve*", "modeling/geometry_nodes/curve/sample_curve.html#bpy-types-geometrynodesamplecurve"),
("bpy.types.geometrynodesetmaterial*", "modeling/geometry_nodes/material/set_material.html#bpy-types-geometrynodesetmaterial"),
("bpy.types.geometrynodesetposition*", "modeling/geometry_nodes/geometry/set_position.html#bpy-types-geometrynodesetposition"),
- ("bpy.types.geometrynodeslicestring*", "modeling/geometry_nodes/text/slice_string.html#bpy-types-geometrynodeslicestring"),
("bpy.types.geometrynodetriangulate*", "modeling/geometry_nodes/mesh/triangulate.html#bpy-types-geometrynodetriangulate"),
("bpy.types.gpencillayer.blend_mode*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-blend-mode"),
+ ("bpy.types.gpencillayer.pass_index*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-pass-index"),
+ ("bpy.types.gpencillayer.tint_color*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-tint-color"),
("bpy.types.gpencillayer.use_lights*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-use-lights"),
("bpy.types.gpencilsculptguide.type*", "grease_pencil/modes/draw/guides.html#bpy-types-gpencilsculptguide-type"),
+ ("bpy.types.greasepencil.onion_mode*", "grease_pencil/properties/onion_skinning.html#bpy-types-greasepencil-onion-mode"),
+ ("bpy.types.greasepencilgrid.offset*", "grease_pencil/properties/display.html#bpy-types-greasepencilgrid-offset"),
("bpy.types.laplaciandeformmodifier*", "modeling/modifiers/deform/laplacian_deform.html#bpy-types-laplaciandeformmodifier"),
("bpy.types.laplaciansmoothmodifier*", "modeling/modifiers/deform/laplacian_smooth.html#bpy-types-laplaciansmoothmodifier"),
("bpy.types.layercollection.exclude*", "editors/outliner/interface.html#bpy-types-layercollection-exclude"),
@@ -1321,13 +1367,14 @@ url_manual_mapping = (
("bpy.types.rigidbodyobject.enabled*", "physics/rigid_body/properties/settings.html#bpy-types-rigidbodyobject-enabled"),
("bpy.types.sceneeevee.use_overscan*", "render/eevee/render_settings/film.html#bpy-types-sceneeevee-use-overscan"),
("bpy.types.sequencerpreviewoverlay*", "editors/video_sequencer/preview/display/overlays.html#bpy-types-sequencerpreviewoverlay"),
- ("bpy.types.sequencetransform.scale*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sequencetransform-scale"),
+ ("bpy.types.sequencetransform.scale*", "editors/video_sequencer/sequencer/sidebar/strip.html#bpy-types-sequencetransform-scale"),
("bpy.types.shadernodeeeveespecular*", "render/shader_nodes/shader/specular_bsdf.html#bpy-types-shadernodeeeveespecular"),
("bpy.types.shadernodehuesaturation*", "render/shader_nodes/color/hue_saturation.html#bpy-types-shadernodehuesaturation"),
("bpy.types.shadernodetexwhitenoise*", "render/shader_nodes/textures/white_noise.html#bpy-types-shadernodetexwhitenoise"),
("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.texturenodetexdistnoise*", "editors/texture_node/types/textures/distorted_noise.html#bpy-types-texturenodetexdistnoise"),
("bpy.types.vertexweightmixmodifier*", "modeling/modifiers/modify/weight_mix.html#bpy-types-vertexweightmixmodifier"),
("bpy.types.viewlayer.use_freestyle*", "render/freestyle/view_layer/freestyle.html#bpy-types-viewlayer-use-freestyle"),
("bpy.types.volumedisplay.use_slice*", "modeling/volumes/properties.html#bpy-types-volumedisplay-use-slice"),
@@ -1365,15 +1412,15 @@ url_manual_mapping = (
("bpy.ops.rigidbody.mass_calculate*", "scene_layout/object/editing/rigid_body.html#bpy-ops-rigidbody-mass-calculate"),
("bpy.ops.screen.spacedata_cleanup*", "advanced/operators.html#bpy-ops-screen-spacedata-cleanup"),
("bpy.ops.sculpt.detail_flood_fill*", "sculpt_paint/sculpting/tool_settings/dyntopo.html#bpy-ops-sculpt-detail-flood-fill"),
- ("bpy.ops.sequencer.duplicate_move*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-duplicate-move"),
- ("bpy.ops.sequencer.select_grouped*", "video_editing/sequencer/selecting.html#bpy-ops-sequencer-select-grouped"),
- ("bpy.ops.sequencer.select_handles*", "video_editing/sequencer/selecting.html#bpy-ops-sequencer-select-handles"),
+ ("bpy.ops.sequencer.duplicate_move*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-duplicate-move"),
+ ("bpy.ops.sequencer.select_grouped*", "video_editing/edit/montage/selecting.html#bpy-ops-sequencer-select-grouped"),
+ ("bpy.ops.sequencer.select_handles*", "video_editing/edit/montage/selecting.html#bpy-ops-sequencer-select-handles"),
("bpy.ops.uv.average_islands_scale*", "modeling/meshes/uv/editing.html#bpy-ops-uv-average-islands-scale"),
("bpy.types.armature.axes_position*", "animation/armatures/properties/display.html#bpy-types-armature-axes-position"),
("bpy.types.armature.pose_position*", "animation/armatures/properties/skeleton.html#bpy-types-armature-pose-position"),
("bpy.types.bakesettings.use_clear*", "render/cycles/baking.html#bpy-types-bakesettings-use-clear"),
("bpy.types.bone.envelope_distance*", "animation/armatures/bones/properties/deform.html#bpy-types-bone-envelope-distance"),
- ("bpy.types.brightcontrastmodifier*", "video_editing/sequencer/sidebar/modifiers.html#bpy-types-brightcontrastmodifier"),
+ ("bpy.types.brightcontrastmodifier*", "editors/video_sequencer/sequencer/sidebar/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"),
@@ -1407,16 +1454,20 @@ url_manual_mapping = (
("bpy.types.editbone.inherit_scale*", "animation/armatures/bones/properties/relations.html#bpy-types-editbone-inherit-scale"),
("bpy.types.freestylelinestyle.gap*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-gap"),
("bpy.types.freestylesettings.mode*", "render/freestyle/view_layer/freestyle.html#bpy-types-freestylesettings-mode"),
+ ("bpy.types.functionnodefloattoint*", "modeling/geometry_nodes/utilities/float_to_integer.html#bpy-types-functionnodefloattoint"),
+ ("bpy.types.functionnodeinputcolor*", "modeling/geometry_nodes/input/color.html#bpy-types-functionnodeinputcolor"),
("bpy.types.geometrynodeconvexhull*", "modeling/geometry_nodes/geometry/convex_hull.html#bpy-types-geometrynodeconvexhull"),
- ("bpy.types.geometrynodedomainsize*", "modeling/geometry_nodes/attribute/domain_size.html#bpy-types-geometrynodedomainsize"),
- ("bpy.types.geometrynodefloattoint*", "modeling/geometry_nodes/utilities/float_to_integer.html#bpy-types-geometrynodefloattoint"),
- ("bpy.types.geometrynodeinputcolor*", "modeling/geometry_nodes/input/color.html#bpy-types-geometrynodeinputcolor"),
("bpy.types.geometrynodeinputindex*", "modeling/geometry_nodes/input/input_index.html#bpy-types-geometrynodeinputindex"),
("bpy.types.geometrynodeisviewport*", "modeling/geometry_nodes/input/is_viewport.html#bpy-types-geometrynodeisviewport"),
("bpy.types.geometrynodemeshcircle*", "modeling/geometry_nodes/mesh_primitives/mesh_circle.html#bpy-types-geometrynodemeshcircle"),
("bpy.types.geometrynodeobjectinfo*", "modeling/geometry_nodes/input/object_info.html#bpy-types-geometrynodeobjectinfo"),
+ ("bpy.types.geometrynodesplitedges*", "modeling/geometry_nodes/mesh/split_edges.html#bpy-types-geometrynodesplitedges"),
+ ("bpy.types.geometrynodestringjoin*", "modeling/geometry_nodes/text/join_strings.html#bpy-types-geometrynodestringjoin"),
+ ("bpy.types.greasepencilgrid.color*", "grease_pencil/properties/display.html#bpy-types-greasepencilgrid-color"),
+ ("bpy.types.greasepencilgrid.lines*", "grease_pencil/properties/display.html#bpy-types-greasepencilgrid-lines"),
+ ("bpy.types.greasepencilgrid.scale*", "grease_pencil/properties/display.html#bpy-types-greasepencilgrid-scale"),
("bpy.types.imagepaint.use_occlude*", "sculpt_paint/texture_paint/tool_settings/options.html#bpy-types-imagepaint-use-occlude"),
- ("bpy.types.imagesequence.use_flip*", "video_editing/sequencer/sidebar/strip.html#bpy-types-imagesequence-use-flip"),
+ ("bpy.types.imagesequence.use_flip*", "editors/video_sequencer/sequencer/sidebar/strip.html#bpy-types-imagesequence-use-flip"),
("bpy.types.keyframe.interpolation*", "editors/graph_editor/fcurves/properties.html#bpy-types-keyframe-interpolation"),
("bpy.types.latticegpencilmodifier*", "grease_pencil/modifiers/deform/lattice.html#bpy-types-latticegpencilmodifier"),
("bpy.types.lineartgpencilmodifier*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier"),
@@ -1436,9 +1487,11 @@ url_manual_mapping = (
("bpy.types.shadernodelightfalloff*", "render/shader_nodes/color/light_falloff.html#bpy-types-shadernodelightfalloff"),
("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.sound.use_memory_cache*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sound-use-memory-cache"),
+ ("bpy.types.sound.use_memory_cache*", "editors/video_sequencer/sequencer/sidebar/strip.html#bpy-types-sound-use-memory-cache"),
("bpy.types.spaceview3d.show_gizmo*", "editors/3dview/display/gizmo.html#bpy-types-spaceview3d-show-gizmo"),
("bpy.types.texturegpencilmodifier*", "grease_pencil/modifiers/modify/texture_mapping.html#bpy-types-texturegpencilmodifier"),
+ ("bpy.types.texturenodecoordinates*", "editors/texture_node/types/input/coordinates.html#bpy-types-texturenodecoordinates"),
+ ("bpy.types.texturenodetexmusgrave*", "editors/texture_node/types/textures/musgrave.html#bpy-types-texturenodetexmusgrave"),
("bpy.types.unitsettings.mass_unit*", "scene_layout/scene/properties.html#bpy-types-unitsettings-mass-unit"),
("bpy.types.unitsettings.time_unit*", "scene_layout/scene/properties.html#bpy-types-unitsettings-time-unit"),
("bpy.types.volumedisplacemodifier*", "modeling/modifiers/deform/volume_displace.html#bpy-types-volumedisplacemodifier"),
@@ -1461,6 +1514,7 @@ url_manual_mapping = (
("bpy.ops.graph.blend_to_neighbor*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-blend-to-neighbor"),
("bpy.ops.graph.snap_cursor_value*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-snap-cursor-value"),
("bpy.ops.image.save_all_modified*", "editors/image/editing.html#bpy-ops-image-save-all-modified"),
+ ("bpy.ops.marker.make_links_scene*", "animation/markers.html#bpy-ops-marker-make-links-scene"),
("bpy.ops.mesh.extrude_edges_move*", "modeling/meshes/editing/edge/extrude_edges.html#bpy-ops-mesh-extrude-edges-move"),
("bpy.ops.mesh.extrude_faces_move*", "modeling/meshes/editing/face/extrude_individual_faces.html#bpy-ops-mesh-extrude-faces-move"),
("bpy.ops.mesh.faces_shade_smooth*", "modeling/meshes/editing/face/shading.html#bpy-ops-mesh-faces-shade-smooth"),
@@ -1492,7 +1546,7 @@ url_manual_mapping = (
("bpy.ops.scene.view_layer_remove*", "render/layers/introduction.html#bpy-ops-scene-view-layer-remove"),
("bpy.ops.screen.screen_full_area*", "interface/window_system/areas.html#bpy-ops-screen-screen-full-area"),
("bpy.ops.sculpt.face_sets_create*", "sculpt_paint/sculpting/editing/face_sets.html#bpy-ops-sculpt-face-sets-create"),
- ("bpy.ops.sequencer.select_linked*", "video_editing/sequencer/selecting.html#bpy-ops-sequencer-select-linked"),
+ ("bpy.ops.sequencer.select_linked*", "video_editing/edit/montage/selecting.html#bpy-ops-sequencer-select-linked"),
("bpy.ops.transform.rotate_normal*", "modeling/meshes/editing/mesh/normals.html#bpy-ops-transform-rotate-normal"),
("bpy.ops.transform.shrink_fatten*", "modeling/meshes/editing/mesh/transform/shrink-fatten.html#bpy-ops-transform-shrink-fatten"),
("bpy.ops.transform.vertex_random*", "modeling/meshes/editing/mesh/transform/randomize.html#bpy-ops-transform-vertex-random"),
@@ -1530,13 +1584,15 @@ url_manual_mapping = (
("bpy.types.fcurve.auto_smoothing*", "editors/graph_editor/fcurves/properties.html#bpy-types-fcurve-auto-smoothing"),
("bpy.types.fluideffectorsettings*", "physics/fluid/type/effector.html#bpy-types-fluideffectorsettings"),
("bpy.types.followtrackconstraint*", "animation/constraints/motion_tracking/follow_track.html#bpy-types-followtrackconstraint"),
- ("bpy.types.geometrynodecurveline*", "modeling/geometry_nodes/curve_primitives/curve_line.html#bpy-types-geometrynodecurveline"),
+ ("bpy.types.functionnodeinputbool*", "modeling/geometry_nodes/input/boolean.html#bpy-types-functionnodeinputbool"),
("bpy.types.geometrynodecurvestar*", "modeling/geometry_nodes/curve_primitives/star.html#bpy-types-geometrynodecurvestar"),
- ("bpy.types.geometrynodeedgesplit*", "modeling/geometry_nodes/mesh/split_edges.html#bpy-types-geometrynodeedgesplit"),
("bpy.types.geometrynodefillcurve*", "modeling/geometry_nodes/curve/fill_curve.html#bpy-types-geometrynodefillcurve"),
("bpy.types.geometrynodeflipfaces*", "modeling/geometry_nodes/mesh/flip_faces.html#bpy-types-geometrynodeflipfaces"),
+ ("bpy.types.geometrynodeproximity*", "modeling/geometry_nodes/geometry/geometry_proximity.html#bpy-types-geometrynodeproximity"),
("bpy.types.geometrynodetransform*", "modeling/geometry_nodes/geometry/transform.html#bpy-types-geometrynodetransform"),
("bpy.types.geometrynodetrimcurve*", "modeling/geometry_nodes/curve/trim_curve.html#bpy-types-geometrynodetrimcurve"),
+ ("bpy.types.gpencillayer.location*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-location"),
+ ("bpy.types.gpencillayer.rotation*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-rotation"),
("bpy.types.gpencilsculptsettings*", "grease_pencil/properties/index.html#bpy-types-gpencilsculptsettings"),
("bpy.types.keyframe.handle_right*", "editors/graph_editor/fcurves/properties.html#bpy-types-keyframe-handle-right"),
("bpy.types.lengthgpencilmodifier*", "grease_pencil/modifiers/generate/length.html#bpy-types-lengthgpencilmodifier"),
@@ -1559,14 +1615,17 @@ url_manual_mapping = (
("bpy.types.sculpt.gravity_object*", "sculpt_paint/sculpting/tool_settings/options.html#bpy-types-sculpt-gravity-object"),
("bpy.types.shadernodebsdfdiffuse*", "render/shader_nodes/shader/diffuse.html#bpy-types-shadernodebsdfdiffuse"),
("bpy.types.shadernodelayerweight*", "render/shader_nodes/input/layer_weight.html#bpy-types-shadernodelayerweight"),
+ ("bpy.types.shadernodenewgeometry*", "render/shader_nodes/input/geometry.html#bpy-types-shadernodenewgeometry"),
("bpy.types.shadernodeoutputlight*", "render/shader_nodes/output/light.html#bpy-types-shadernodeoutputlight"),
("bpy.types.shadernodeoutputworld*", "render/shader_nodes/output/world.html#bpy-types-shadernodeoutputworld"),
+ ("bpy.types.shadernodeshadertorgb*", "render/shader_nodes/converter/shader_to_rgb.html#bpy-types-shadernodeshadertorgb"),
("bpy.types.shadernodetexgradient*", "render/shader_nodes/textures/gradient.html#bpy-types-shadernodetexgradient"),
("bpy.types.shadernodevectorcurve*", "render/shader_nodes/vector/curves.html#bpy-types-shadernodevectorcurve"),
("bpy.types.shadernodevertexcolor*", "render/shader_nodes/input/vertex_color.html#bpy-types-shadernodevertexcolor"),
("bpy.types.smoothgpencilmodifier*", "grease_pencil/modifiers/deform/smooth.html#bpy-types-smoothgpencilmodifier"),
("bpy.types.spline.use_endpoint_u*", "modeling/curves/properties/active_spline.html#bpy-types-spline-use-endpoint-u"),
("bpy.types.surfacedeformmodifier*", "modeling/modifiers/deform/surface_deform.html#bpy-types-surfacedeformmodifier"),
+ ("bpy.types.texturenodetexvoronoi*", "editors/texture_node/types/textures/voronoi.html#bpy-types-texturenodetexvoronoi"),
("bpy.types.toolsettings.use_snap*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap"),
("bpy.types.viewlayer.use_volumes*", "render/layers/introduction.html#bpy-types-viewlayer-use-volumes"),
("bpy.types.volume.frame_duration*", "modeling/volumes/properties.html#bpy-types-volume-frame-duration"),
@@ -1576,6 +1635,7 @@ url_manual_mapping = (
("bpy.ops.anim.channels_collapse*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-collapse"),
("bpy.ops.armature.select_mirror*", "animation/armatures/bones/selecting.html#bpy-ops-armature-select-mirror"),
("bpy.ops.curve.switch_direction*", "modeling/curves/editing/segments.html#bpy-ops-curve-switch-direction"),
+ ("bpy.ops.geometry.attribute_add*", "modeling/geometry_nodes/attributes_reference.html#bpy-ops-geometry-attribute-add"),
("bpy.ops.gpencil.duplicate_move*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-duplicate-move"),
("bpy.ops.gpencil.stroke_arrange*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-arrange"),
("bpy.ops.graph.equalize_handles*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-equalize-handles"),
@@ -1593,7 +1653,7 @@ url_manual_mapping = (
("bpy.ops.paint.mask_box_gesture*", "sculpt_paint/sculpting/editing/mask.html#bpy-ops-paint-mask-box-gesture"),
("bpy.ops.screen.region_quadview*", "editors/3dview/navigate/views.html#bpy-ops-screen-region-quadview"),
("bpy.ops.screen.screenshot_area*", "interface/window_system/topbar.html#bpy-ops-screen-screenshot-area"),
- ("bpy.ops.sequencer.offset_clear*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-offset-clear"),
+ ("bpy.ops.sequencer.offset_clear*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-offset-clear"),
("bpy.ops.spreadsheet.toggle_pin*", "editors/spreadsheet.html#bpy-ops-spreadsheet-toggle-pin"),
("bpy.ops.uv.follow_active_quads*", "modeling/meshes/editing/uv.html#bpy-ops-uv-follow-active-quads"),
("bpy.types.arraygpencilmodifier*", "grease_pencil/modifiers/generate/array.html#bpy-types-arraygpencilmodifier"),
@@ -1603,11 +1663,11 @@ url_manual_mapping = (
("bpy.types.brushgpencilsettings*", "grease_pencil/modes/draw/tools/index.html#bpy-types-brushgpencilsettings"),
("bpy.types.buildgpencilmodifier*", "grease_pencil/modifiers/generate/build.html#bpy-types-buildgpencilmodifier"),
("bpy.types.camera.sensor_height*", "render/cameras.html#bpy-types-camera-sensor-height"),
- ("bpy.types.colorbalancemodifier*", "video_editing/sequencer/sidebar/modifiers.html#bpy-types-colorbalancemodifier"),
+ ("bpy.types.colorbalancemodifier*", "editors/video_sequencer/sequencer/sidebar/modifiers.html#bpy-types-colorbalancemodifier"),
("bpy.types.colorgpencilmodifier*", "grease_pencil/modifiers/color/hue_saturation.html#bpy-types-colorgpencilmodifier"),
("bpy.types.colorramp.color_mode*", "interface/controls/templates/color_ramp.html#bpy-types-colorramp-color-mode"),
("bpy.types.compositornodefilter*", "compositing/types/filter/filter_node.html#bpy-types-compositornodefilter"),
- ("bpy.types.compositornodehuesat*", "compositing/types/color/posterize.html#bpy-types-compositornodehuesat"),
+ ("bpy.types.compositornodehuesat*", "compositing/types/color/hue_saturation.html#bpy-types-compositornodehuesat"),
("bpy.types.compositornodeidmask*", "compositing/types/converter/id_mask.html#bpy-types-compositornodeidmask"),
("bpy.types.compositornodeinvert*", "compositing/types/color/invert.html#bpy-types-compositornodeinvert"),
("bpy.types.compositornodekeying*", "compositing/types/matte/keying.html#bpy-types-compositornodekeying"),
@@ -1615,6 +1675,7 @@ url_manual_mapping = (
("bpy.types.compositornodemixrgb*", "compositing/types/color/mix.html#bpy-types-compositornodemixrgb"),
("bpy.types.compositornodenormal*", "compositing/types/vector/normal.html#bpy-types-compositornodenormal"),
("bpy.types.compositornoderotate*", "compositing/types/distort/rotate.html#bpy-types-compositornoderotate"),
+ ("bpy.types.compositornodeswitch*", "compositing/types/layout/switch.html#bpy-types-compositornodeswitch"),
("bpy.types.compositornodeviewer*", "compositing/types/output/viewer.html#bpy-types-compositornodeviewer"),
("bpy.types.constraint.influence*", "animation/constraints/interface/common.html#bpy-types-constraint-influence"),
("bpy.types.curve.use_path_clamp*", "modeling/curves/properties/path_animation.html#bpy-types-curve-use-path-clamp"),
@@ -1623,8 +1684,10 @@ url_manual_mapping = (
("bpy.types.editbone.use_connect*", "animation/armatures/bones/properties/relations.html#bpy-types-editbone-use-connect"),
("bpy.types.ffmpegsettings.audio*", "render/output/properties/output.html#bpy-types-ffmpegsettings-audio"),
("bpy.types.followpathconstraint*", "animation/constraints/relationship/follow_path.html#bpy-types-followpathconstraint"),
- ("bpy.types.gaussianblursequence*", "video_editing/sequencer/strips/effects/blur.html#bpy-types-gaussianblursequence"),
+ ("bpy.types.functionnodeinputint*", "modeling/geometry_nodes/input/integer.html#bpy-types-functionnodeinputint"),
+ ("bpy.types.gaussianblursequence*", "video_editing/edit/montage/strips/effects/blur.html#bpy-types-gaussianblursequence"),
("bpy.types.geometrynodeboundbox*", "modeling/geometry_nodes/geometry/bounding_box.html#bpy-types-geometrynodeboundbox"),
+ ("bpy.types.geometrynodecurvearc*", "modeling/geometry_nodes/curve_primitives/arc.html#bpy-types-geometrynodecurvearc"),
("bpy.types.geometrynodedualmesh*", "modeling/geometry_nodes/mesh/dual_mesh.html#bpy-types-geometrynodedualmesh"),
("bpy.types.geometrynodematerial*", "-1"),
("bpy.types.geometrynodemeshcone*", "modeling/geometry_nodes/mesh_primitives/cone.html#bpy-types-geometrynodemeshcone"),
@@ -1649,7 +1712,7 @@ url_manual_mapping = (
("bpy.types.preferencesfilepaths*", "editors/preferences/file_paths.html#bpy-types-preferencesfilepaths"),
("bpy.types.rigidbodyobject.mass*", "physics/rigid_body/properties/settings.html#bpy-types-rigidbodyobject-mass"),
("bpy.types.scene.background_set*", "scene_layout/scene/properties.html#bpy-types-scene-background-set"),
- ("bpy.types.sequence.frame_start*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sequence-frame-start"),
+ ("bpy.types.sequence.frame_start*", "editors/video_sequencer/sequencer/sidebar/strip.html#bpy-types-sequence-frame-start"),
("bpy.types.shadernodebackground*", "render/shader_nodes/shader/background.html#bpy-types-shadernodebackground"),
("bpy.types.shadernodebsdfglossy*", "render/shader_nodes/shader/glossy.html#bpy-types-shadernodebsdfglossy"),
("bpy.types.shadernodebsdfvelvet*", "render/shader_nodes/shader/velvet.html#bpy-types-shadernodebsdfvelvet"),
@@ -1665,14 +1728,18 @@ url_manual_mapping = (
("bpy.types.simpledeformmodifier*", "modeling/modifiers/deform/simple_deform.html#bpy-types-simpledeformmodifier"),
("bpy.types.spaceclipeditor.show*", "editors/clip/display/index.html#bpy-types-spaceclipeditor-show"),
("bpy.types.spacedopesheeteditor*", "editors/dope_sheet/index.html#bpy-types-spacedopesheeteditor"),
- ("bpy.types.speedcontrolsequence*", "video_editing/sequencer/strips/effects/speed_control.html#bpy-types-speedcontrolsequence"),
+ ("bpy.types.speedcontrolsequence*", "video_editing/edit/montage/strips/effects/speed_control.html#bpy-types-speedcontrolsequence"),
("bpy.types.texturenodecurvetime*", "editors/texture_node/types/input/time.html#bpy-types-texturenodecurvetime"),
+ ("bpy.types.texturenodetexclouds*", "editors/texture_node/types/textures/clouds.html#bpy-types-texturenodetexclouds"),
+ ("bpy.types.texturenodetexmarble*", "editors/texture_node/types/textures/marble.html#bpy-types-texturenodetexmarble"),
+ ("bpy.types.texturenodetexstucci*", "editors/texture_node/types/textures/stucci.html#bpy-types-texturenodetexstucci"),
+ ("bpy.types.texturenodetranslate*", "editors/texture_node/types/distort/translate.html#bpy-types-texturenodetranslate"),
("bpy.types.transformorientation*", "editors/3dview/controls/orientation.html#bpy-types-transformorientation"),
("bpy.types.unifiedpaintsettings*", "sculpt_paint/brush/brush.html#bpy-types-unifiedpaintsettings"),
("bpy.types.viewlayer.use_strand*", "render/layers/introduction.html#bpy-types-viewlayer-use-strand"),
("bpy.types.volume.sequence_mode*", "modeling/volumes/properties.html#bpy-types-volume-sequence-mode"),
("bpy.types.volumetomeshmodifier*", "modeling/modifiers/generate/volume_to_mesh.html#bpy-types-volumetomeshmodifier"),
- ("bpy.types.whitebalancemodifier*", "video_editing/sequencer/sidebar/modifiers.html#bpy-types-whitebalancemodifier"),
+ ("bpy.types.whitebalancemodifier*", "editors/video_sequencer/sequencer/sidebar/modifiers.html#bpy-types-whitebalancemodifier"),
("bpy.ops.anim.channels_ungroup*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-ungroup"),
("bpy.ops.armature.extrude_move*", "animation/armatures/bones/editing/extrude.html#bpy-ops-armature-extrude-move"),
("bpy.ops.armature.parent_clear*", "animation/armatures/bones/editing/parenting.html#bpy-ops-armature-parent-clear"),
@@ -1681,6 +1748,7 @@ url_manual_mapping = (
("bpy.ops.curve.handle_type_set*", "modeling/curves/editing/control_points.html#bpy-ops-curve-handle-type-set"),
("bpy.ops.curve.spline_type_set*", "modeling/curves/editing/curve.html#bpy-ops-curve-spline-type-set"),
("bpy.ops.file.unpack_libraries*", "files/blend/packed_data.html#bpy-ops-file-unpack-libraries"),
+ ("bpy.ops.gpencil.layer_isolate*", "grease_pencil/properties/layers.html#bpy-ops-gpencil-layer-isolate"),
("bpy.ops.gpencil.move_to_layer*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-move-to-layer"),
("bpy.ops.gpencil.stroke_sample*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-sample"),
("bpy.ops.gpencil.stroke_smooth*", "grease_pencil/modes/edit/point_menu.html#bpy-ops-gpencil-stroke-smooth"),
@@ -1713,12 +1781,12 @@ url_manual_mapping = (
("bpy.ops.screen.repeat_history*", "interface/undo_redo.html#bpy-ops-screen-repeat-history"),
("bpy.ops.script.execute_preset*", "interface/window_system/tabs_panels.html#bpy-ops-script-execute-preset"),
("bpy.ops.sculpt.face_sets_init*", "sculpt_paint/sculpting/editing/face_sets.html#bpy-ops-sculpt-face-sets-init"),
- ("bpy.ops.sequencer.change_path*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-change-path"),
- ("bpy.ops.sequencer.refresh_all*", "video_editing/sequencer/navigating.html#bpy-ops-sequencer-refresh-all"),
- ("bpy.ops.sequencer.select_less*", "video_editing/sequencer/selecting.html#bpy-ops-sequencer-select-less"),
- ("bpy.ops.sequencer.select_more*", "video_editing/sequencer/selecting.html#bpy-ops-sequencer-select-more"),
- ("bpy.ops.sequencer.select_side*", "video_editing/sequencer/selecting.html#bpy-ops-sequencer-select-side"),
- ("bpy.ops.sequencer.swap_inputs*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-swap-inputs"),
+ ("bpy.ops.sequencer.change_path*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-change-path"),
+ ("bpy.ops.sequencer.refresh_all*", "editors/video_sequencer/sequencer/navigating.html#bpy-ops-sequencer-refresh-all"),
+ ("bpy.ops.sequencer.select_less*", "video_editing/edit/montage/selecting.html#bpy-ops-sequencer-select-less"),
+ ("bpy.ops.sequencer.select_more*", "video_editing/edit/montage/selecting.html#bpy-ops-sequencer-select-more"),
+ ("bpy.ops.sequencer.select_side*", "video_editing/edit/montage/selecting.html#bpy-ops-sequencer-select-side"),
+ ("bpy.ops.sequencer.swap_inputs*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-swap-inputs"),
("bpy.ops.surface.primitive*add*", "modeling/surfaces/primitives.html#bpy-ops-surface-primitive-add"),
("bpy.ops.text.resolve_conflict*", "editors/text_editor.html#bpy-ops-text-resolve-conflict"),
("bpy.ops.transform.edge_crease*", "modeling/meshes/editing/edge/edge_data.html#bpy-ops-transform-edge-crease"),
@@ -1747,11 +1815,9 @@ url_manual_mapping = (
("bpy.types.dashgpencilmodifier*", "grease_pencil/modifiers/generate/dash.html#bpy-types-dashgpencilmodifier"),
("bpy.types.fluiddomainsettings*", "physics/fluid/type/domain/index.html#bpy-types-fluiddomainsettings"),
("bpy.types.functionnodecompare*", "modeling/geometry_nodes/utilities/compare.html#bpy-types-functionnodecompare"),
- ("bpy.types.geometrynodeboolean*", "modeling/geometry_nodes/input/boolean.html#bpy-types-geometrynodeboolean"),
("bpy.types.geometrynodeinputid*", "modeling/geometry_nodes/input/id.html#bpy-types-geometrynodeinputid"),
- ("bpy.types.geometrynodeinteger*", "modeling/geometry_nodes/input/integer.html#bpy-types-geometrynodeinteger"),
("bpy.types.geometrynoderaycast*", "modeling/geometry_nodes/geometry/raycast.html#bpy-types-geometrynoderaycast"),
- ("bpy.types.geonodeimagetexture*", "modeling/geometry_nodes/texture/image.html#bpy-types-geonodeimagetexture"),
+ ("bpy.types.gpencillayer.parent*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-parent"),
("bpy.types.hookgpencilmodifier*", "grease_pencil/modifiers/deform/hook.html#bpy-types-hookgpencilmodifier"),
("bpy.types.imageformatsettings*", "files/media/image_formats.html#bpy-types-imageformatsettings"),
("bpy.types.kinematicconstraint*", "animation/constraints/tracking/ik_solver.html#bpy-types-kinematicconstraint"),
@@ -1773,14 +1839,20 @@ url_manual_mapping = (
("bpy.types.shadernodelightpath*", "render/shader_nodes/input/light_path.html#bpy-types-shadernodelightpath"),
("bpy.types.shadernodemixshader*", "render/shader_nodes/shader/mix.html#bpy-types-shadernodemixshader"),
("bpy.types.shadernodenormalmap*", "render/shader_nodes/vector/normal_map.html#bpy-types-shadernodenormalmap"),
+ ("bpy.types.shadernodeoutputaov*", "render/shader_nodes/output/aov.html#bpy-types-shadernodeoutputaov"),
("bpy.types.shadernodepointinfo*", "render/shader_nodes/input/point_info.html#bpy-types-shadernodepointinfo"),
("bpy.types.shadernodewireframe*", "render/shader_nodes/input/wireframe.html#bpy-types-shadernodewireframe"),
- ("bpy.types.spacesequenceeditor*", "video_editing/index.html#bpy-types-spacesequenceeditor"),
+ ("bpy.types.spacesequenceeditor*", "editors/video_sequencer/index.html#bpy-types-spacesequenceeditor"),
("bpy.types.spline.resolution_u*", "modeling/curves/properties/active_spline.html#bpy-types-spline-resolution-u"),
("bpy.types.spline.use_bezier_u*", "modeling/curves/properties/active_spline.html#bpy-types-spline-use-bezier-u"),
("bpy.types.spline.use_cyclic_u*", "modeling/curves/properties/active_spline.html#bpy-types-spline-use-cyclic-u"),
("bpy.types.stretchtoconstraint*", "animation/constraints/tracking/stretch_to.html#bpy-types-stretchtoconstraint"),
("bpy.types.texturenodecurvergb*", "editors/texture_node/types/color/rgb_curves.html#bpy-types-texturenodecurvergb"),
+ ("bpy.types.texturenodedistance*", "editors/texture_node/types/converter/distance.html#bpy-types-texturenodedistance"),
+ ("bpy.types.texturenodetexblend*", "editors/texture_node/types/textures/blend.html#bpy-types-texturenodetexblend"),
+ ("bpy.types.texturenodetexmagic*", "editors/texture_node/types/textures/magic.html#bpy-types-texturenodetexmagic"),
+ ("bpy.types.texturenodetexnoise*", "editors/texture_node/types/textures/noise.html#bpy-types-texturenodetexnoise"),
+ ("bpy.types.texturenodevaltonor*", "editors/texture_node/types/converter/value_to_normal.html#bpy-types-texturenodevaltonor"),
("bpy.types.texturenodevaltorgb*", "editors/texture_node/types/converter/rgb_to_bw.html#bpy-types-texturenodevaltorgb"),
("bpy.types.timegpencilmodifier*", "grease_pencil/modifiers/modify/time_offset.html#bpy-types-timegpencilmodifier"),
("bpy.types.tintgpencilmodifier*", "grease_pencil/modifiers/color/tint.html#bpy-types-tintgpencilmodifier"),
@@ -1834,24 +1906,24 @@ url_manual_mapping = (
("bpy.ops.preferences.autoexec*", "editors/preferences/save_load.html#bpy-ops-preferences-autoexec"),
("bpy.ops.scene.view_layer_add*", "render/layers/introduction.html#bpy-ops-scene-view-layer-add"),
("bpy.ops.sculpt.face_set_edit*", "sculpt_paint/sculpting/editing/face_sets.html#bpy-ops-sculpt-face-set-edit"),
- ("bpy.ops.sequencer.gap_insert*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-gap-insert"),
- ("bpy.ops.sequencer.gap_remove*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-gap-remove"),
- ("bpy.ops.sequencer.rendersize*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-rendersize"),
- ("bpy.ops.sequencer.select_all*", "video_editing/sequencer/selecting.html#bpy-ops-sequencer-select-all"),
- ("bpy.ops.sequencer.select_box*", "video_editing/sequencer/selecting.html#bpy-ops-sequencer-select-box"),
+ ("bpy.ops.sequencer.gap_insert*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-gap-insert"),
+ ("bpy.ops.sequencer.gap_remove*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-gap-remove"),
+ ("bpy.ops.sequencer.rendersize*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-rendersize"),
+ ("bpy.ops.sequencer.select_all*", "video_editing/edit/montage/selecting.html#bpy-ops-sequencer-select-all"),
+ ("bpy.ops.sequencer.select_box*", "video_editing/edit/montage/selecting.html#bpy-ops-sequencer-select-box"),
("bpy.ops.sound.bake_animation*", "scene_layout/scene/properties.html#bpy-ops-sound-bake-animation"),
("bpy.ops.transform.edge_slide*", "modeling/meshes/editing/edge/edge_slide.html#bpy-ops-transform-edge-slide"),
("bpy.ops.transform.vert_slide*", "modeling/meshes/editing/vertex/slide_vertices.html#bpy-ops-transform-vert-slide"),
("bpy.ops.uv.project_from_view*", "modeling/meshes/editing/uv.html#bpy-ops-uv-project-from-view"),
("bpy.ops.wm.memory_statistics*", "advanced/operators.html#bpy-ops-wm-memory-statistics"),
("bpy.ops.wm.recover_auto_save*", "files/blend/open_save.html#bpy-ops-wm-recover-auto-save"),
- ("bpy.types.adjustmentsequence*", "video_editing/sequencer/strips/adjustment.html#bpy-types-adjustmentsequence"),
- ("bpy.types.alphaundersequence*", "video_editing/sequencer/strips/effects/alpha_over_under_overdrop.html#bpy-types-alphaundersequence"),
+ ("bpy.types.adjustmentsequence*", "video_editing/edit/montage/strips/adjustment.html#bpy-types-adjustmentsequence"),
+ ("bpy.types.alphaundersequence*", "video_editing/edit/montage/strips/effects/alpha_over_under_overdrop.html#bpy-types-alphaundersequence"),
("bpy.types.animvizmotionpaths*", "animation/motion_paths.html#bpy-types-animvizmotionpaths"),
("bpy.types.armature.show_axes*", "animation/armatures/properties/display.html#bpy-types-armature-show-axes"),
("bpy.types.armatureconstraint*", "animation/constraints/relationship/armature.html#bpy-types-armatureconstraint"),
("bpy.types.compositornodeblur*", "compositing/types/filter/blur_node.html#bpy-types-compositornodeblur"),
- ("bpy.types.compositornodecomb*", "compositing/types/converter/combine_separate.html#bpy-types-compositornodecomb"),
+ ("bpy.types.compositornodecomb*", "editors/texture_node/types/color/combine_separate.html#bpy-types-compositornodecomb"),
("bpy.types.compositornodecrop*", "compositing/types/distort/crop.html#bpy-types-compositornodecrop"),
("bpy.types.compositornodeflip*", "compositing/types/distort/flip.html#bpy-types-compositornodeflip"),
("bpy.types.compositornodemask*", "compositing/types/input/mask.html#bpy-types-compositornodemask"),
@@ -1868,11 +1940,12 @@ url_manual_mapping = (
("bpy.types.fileselectidfilter*", "editors/file_browser.html#bpy-types-fileselectidfilter"),
("bpy.types.fmodifiergenerator*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifiergenerator"),
("bpy.types.freestylelinestyle*", "render/freestyle/view_layer/line_style/index.html#bpy-types-freestylelinestyle"),
- ("bpy.types.gammacrosssequence*", "video_editing/sequencer/strips/transitions/gamma_cross.html#bpy-types-gammacrosssequence"),
+ ("bpy.types.gammacrosssequence*", "video_editing/edit/montage/strips/transitions/gamma_cross.html#bpy-types-gammacrosssequence"),
("bpy.types.geometrynodeswitch*", "modeling/geometry_nodes/utilities/switch.html#bpy-types-geometrynodeswitch"),
("bpy.types.geometrynodeviewer*", "modeling/geometry_nodes/output/viewer.html#bpy-types-geometrynodeviewer"),
+ ("bpy.types.gpencillayer.scale*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-scale"),
("bpy.types.gpencilsculptguide*", "grease_pencil/modes/draw/guides.html#bpy-types-gpencilsculptguide"),
- ("bpy.types.huecorrectmodifier*", "video_editing/sequencer/sidebar/modifiers.html#bpy-types-huecorrectmodifier"),
+ ("bpy.types.huecorrectmodifier*", "editors/video_sequencer/sequencer/sidebar/modifiers.html#bpy-types-huecorrectmodifier"),
("bpy.types.image.is_multiview*", "editors/image/image_settings.html#bpy-types-image-is-multiview"),
("bpy.types.imagepaint.stencil*", "sculpt_paint/texture_paint/tool_settings/mask.html#bpy-types-imagepaint-stencil"),
("bpy.types.meshdeformmodifier*", "modeling/modifiers/deform/mesh_deform.html#bpy-types-meshdeformmodifier"),
@@ -1890,7 +1963,6 @@ url_manual_mapping = (
("bpy.types.shadernodebsdfhair*", "render/shader_nodes/shader/hair.html#bpy-types-shadernodebsdfhair"),
("bpy.types.shadernodebsdftoon*", "render/shader_nodes/shader/toon.html#bpy-types-shadernodebsdftoon"),
("bpy.types.shadernodeemission*", "render/shader_nodes/shader/emission.html#bpy-types-shadernodeemission"),
- ("bpy.types.shadernodegeometry*", "render/shader_nodes/input/geometry.html#bpy-types-shadernodegeometry"),
("bpy.types.shadernodehairinfo*", "render/shader_nodes/input/hair_info.html#bpy-types-shadernodehairinfo"),
("bpy.types.shadernodepointinfo*", "render/shader_nodes/input/point_info.html#bpy-types-shadernodepointinfo"),
("bpy.types.shadernodemaprange*", "render/shader_nodes/converter/map_range.html#bpy-types-shadernodemaprange"),
@@ -1903,7 +1975,9 @@ url_manual_mapping = (
("bpy.types.shadernodetexnoise*", "render/shader_nodes/textures/noise.html#bpy-types-shadernodetexnoise"),
("bpy.types.shrinkwrapmodifier*", "modeling/modifiers/deform/shrinkwrap.html#bpy-types-shrinkwrapmodifier"),
("bpy.types.splineikconstraint*", "animation/constraints/tracking/spline_ik.html#bpy-types-splineikconstraint"),
+ ("bpy.types.texturenodechecker*", "editors/texture_node/types/patterns/checker.html#bpy-types-texturenodechecker"),
("bpy.types.texturenodetexture*", "editors/texture_node/types/input/texture.html#bpy-types-texturenodetexture"),
+ ("bpy.types.texturenodetexwood*", "editors/texture_node/types/textures/wood.html#bpy-types-texturenodetexwood"),
("bpy.types.view3dshading.type*", "editors/3dview/display/shading.html#bpy-types-view3dshading-type"),
("bpy.types.volume.frame_start*", "modeling/volumes/properties.html#bpy-types-volume-frame-start"),
("bpy.types.volume.is_sequence*", "modeling/volumes/properties.html#bpy-types-volume-is-sequence"),
@@ -1960,9 +2034,9 @@ url_manual_mapping = (
("bpy.ops.poselib.pose_remove*", "animation/armatures/posing/editing/pose_library.html#bpy-ops-poselib-pose-remove"),
("bpy.ops.poselib.pose_rename*", "animation/armatures/posing/editing/pose_library.html#bpy-ops-poselib-pose-rename"),
("bpy.ops.preferences.keyitem*", "editors/preferences/keymap.html#bpy-ops-preferences-keyitem"),
- ("bpy.ops.sequencer.swap_data*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-swap-data"),
+ ("bpy.ops.sequencer.swap_data*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-swap-data"),
("bpy.ops.transform.push_pull*", "modeling/meshes/editing/mesh/transform/push_pull.html#bpy-ops-transform-push-pull"),
- ("bpy.ops.transform.seq_slide*", "video_editing/sequencer/editing.html#bpy-ops-transform-seq-slide"),
+ ("bpy.ops.transform.seq_slide*", "video_editing/edit/montage/editing.html#bpy-ops-transform-seq-slide"),
("bpy.ops.transform.trackball*", "scene_layout/object/editing/transform/rotate.html#bpy-ops-transform-trackball"),
("bpy.ops.transform.transform*", "scene_layout/object/editing/transform/align_transform_orientation.html#bpy-ops-transform-transform"),
("bpy.ops.transform.translate*", "scene_layout/object/editing/transform/move.html#bpy-ops-transform-translate"),
@@ -1971,7 +2045,7 @@ url_manual_mapping = (
("bpy.ops.uv.minimize_stretch*", "modeling/meshes/uv/editing.html#bpy-ops-uv-minimize-stretch"),
("bpy.ops.uv.select_edge_ring*", "editors/uv/selecting.html#bpy-ops-uv-select-edge-ring"),
("bpy.ops.wm.save_as_mainfile*", "files/blend/open_save.html#bpy-ops-wm-save-as-mainfile"),
- ("bpy.types.alphaoversequence*", "video_editing/sequencer/strips/effects/alpha_over_under_overdrop.html#bpy-types-alphaoversequence"),
+ ("bpy.types.alphaoversequence*", "video_editing/edit/montage/strips/effects/alpha_over_under_overdrop.html#bpy-types-alphaoversequence"),
("bpy.types.armatureeditbones*", "animation/armatures/bones/editing/index.html#bpy-types-armatureeditbones"),
("bpy.types.brush.pose_offset*", "sculpt_paint/sculpting/tools/pose.html#bpy-types-brush-pose-offset"),
("bpy.types.brush.rake_factor*", "sculpt_paint/sculpting/tools/snake_hook.html#bpy-types-brush-rake-factor"),
@@ -1982,7 +2056,7 @@ url_manual_mapping = (
("bpy.types.collisionmodifier*", "physics/collision.html#bpy-types-collisionmodifier"),
("bpy.types.collisionsettings*", "physics/collision.html#bpy-types-collisionsettings"),
("bpy.types.compositornodergb*", "compositing/types/input/rgb.html#bpy-types-compositornodergb"),
- ("bpy.types.compositornodesep*", "compositing/types/converter/combine_separate.html#bpy-types-compositornodesep"),
+ ("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"),
@@ -2001,7 +2075,7 @@ url_manual_mapping = (
("bpy.types.mesh.use_mirror_y*", "modeling/meshes/tools/tool_settings.html#bpy-types-mesh-use-mirror-y"),
("bpy.types.mesh.use_mirror_z*", "modeling/meshes/tools/tool_settings.html#bpy-types-mesh-use-mirror-z"),
("bpy.types.meshcachemodifier*", "modeling/modifiers/modify/mesh_cache.html#bpy-types-meshcachemodifier"),
- ("bpy.types.movieclipsequence*", "video_editing/sequencer/strips/clip.html#bpy-types-movieclipsequence"),
+ ("bpy.types.movieclipsequence*", "video_editing/edit/montage/strips/clip.html#bpy-types-movieclipsequence"),
("bpy.types.object.dimensions*", "scene_layout/object/properties/transforms.html#bpy-types-object-dimensions"),
("bpy.types.object.is_holdout*", "scene_layout/object/properties/visibility.html#bpy-types-object-is-holdout"),
("bpy.types.object.pass_index*", "scene_layout/object/properties/relations.html#bpy-types-object-pass-index"),
@@ -2012,7 +2086,7 @@ url_manual_mapping = (
("bpy.types.scene.frame_start*", "render/output/properties/frame_range.html#bpy-types-scene-frame-start"),
("bpy.types.sceneeevee.shadow*", "render/eevee/render_settings/shadows.html#bpy-types-sceneeevee-shadow"),
("bpy.types.screen.use_follow*", "editors/timeline.html#bpy-types-screen-use-follow"),
- ("bpy.types.sequencetransform*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sequencetransform"),
+ ("bpy.types.sequencetransform*", "editors/video_sequencer/sequencer/sidebar/strip.html#bpy-types-sequencetransform"),
("bpy.types.shadernodecombine*", "render/shader_nodes/converter/combine_separate.html#bpy-types-shadernodecombine"),
("bpy.types.shadernodefresnel*", "render/shader_nodes/input/fresnel.html#bpy-types-shadernodefresnel"),
("bpy.types.shadernodeholdout*", "render/shader_nodes/shader/holdout.html#bpy-types-shadernodeholdout"),
@@ -2024,8 +2098,10 @@ url_manual_mapping = (
("bpy.types.texturenodebricks*", "editors/texture_node/types/patterns/bricks.html#bpy-types-texturenodebricks"),
("bpy.types.texturenodemixrgb*", "editors/texture_node/types/color/mix_rgb.html#bpy-types-texturenodemixrgb"),
("bpy.types.texturenodeoutput*", "editors/texture_node/types/output/output.html#bpy-types-texturenodeoutput"),
+ ("bpy.types.texturenoderotate*", "editors/texture_node/types/distort/rotate.html#bpy-types-texturenoderotate"),
+ ("bpy.types.texturenodeviewer*", "editors/texture_node/types/output/viewer.html#bpy-types-texturenodeviewer"),
("bpy.types.tracktoconstraint*", "animation/constraints/tracking/track_to.html#bpy-types-tracktoconstraint"),
- ("bpy.types.transformsequence*", "video_editing/sequencer/strips/effects/transform.html#bpy-types-transformsequence"),
+ ("bpy.types.transformsequence*", "video_editing/edit/montage/strips/effects/transform.html#bpy-types-transformsequence"),
("bpy.types.uvprojectmodifier*", "modeling/modifiers/modify/uv_project.html#bpy-types-uvprojectmodifier"),
("bpy.types.viewlayer.samples*", "render/layers/introduction.html#bpy-types-viewlayer-samples"),
("bpy.types.viewlayer.use_sky*", "render/layers/introduction.html#bpy-types-viewlayer-use-sky"),
@@ -2069,6 +2145,7 @@ url_manual_mapping = (
("bpy.ops.pose.select_linked*", "animation/armatures/posing/selecting.html#bpy-ops-pose-select-linked"),
("bpy.ops.pose.select_mirror*", "animation/armatures/posing/selecting.html#bpy-ops-pose-select-mirror"),
("bpy.ops.poselib.apply_pose*", "animation/armatures/properties/pose_library.html#bpy-ops-poselib-apply-pose"),
+ ("bpy.ops.screen.marker_jump*", "animation/markers.html#bpy-ops-screen-marker-jump"),
("bpy.ops.screen.repeat_last*", "interface/undo_redo.html#bpy-ops-screen-repeat-last"),
("bpy.ops.sculpt.mask_expand*", "sculpt_paint/sculpting/editing/mask.html#bpy-ops-sculpt-mask-expand"),
("bpy.ops.sculpt.mask_filter*", "sculpt_paint/sculpting/editing/mask.html#bpy-ops-sculpt-mask-filter"),
@@ -2085,7 +2162,7 @@ url_manual_mapping = (
("bpy.types.bone.tail_radius*", "animation/armatures/bones/properties/deform.html#bpy-types-bone-tail-radius"),
("bpy.types.brush.cloth_mass*", "sculpt_paint/sculpting/tools/cloth.html#bpy-types-brush-cloth-mass"),
("bpy.types.brushtextureslot*", "sculpt_paint/brush/texture.html#bpy-types-brushtextureslot"),
- ("bpy.types.colormixsequence*", "video_editing/sequencer/strips/effects/color_mix.html#bpy-types-colormixsequence"),
+ ("bpy.types.colormixsequence*", "video_editing/edit/montage/strips/effects/color_mix.html#bpy-types-colormixsequence"),
("bpy.types.curve.dimensions*", "modeling/curves/properties/shape.html#bpy-types-curve-dimensions"),
("bpy.types.curve.taper_mode*", "modeling/curves/properties/geometry.html#bpy-types-curve-taper-mode"),
("bpy.types.curve.twist_mode*", "modeling/curves/properties/shape.html#bpy-types-curve-twist-mode"),
@@ -2099,14 +2176,15 @@ url_manual_mapping = (
("bpy.types.fileselectparams*", "editors/file_browser.html#bpy-types-fileselectparams"),
("bpy.types.fmodifierstepped*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifierstepped"),
("bpy.types.freestylelineset*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset"),
+ ("bpy.types.greasepencilgrid*", "grease_pencil/properties/display.html#bpy-types-greasepencilgrid"),
("bpy.types.image.alpha_mode*", "editors/image/image_settings.html#bpy-types-image-alpha-mode"),
("bpy.types.mask.frame_start*", "movie_clip/masking/sidebar.html#bpy-types-mask-frame-start"),
("bpy.types.motionpath.color*", "animation/motion_paths.html#bpy-types-motionpath-color"),
("bpy.types.motionpath.lines*", "animation/motion_paths.html#bpy-types-motionpath-lines"),
- ("bpy.types.multicamsequence*", "video_editing/sequencer/strips/effects/multicam.html#bpy-types-multicamsequence"),
- ("bpy.types.multiplysequence*", "video_editing/sequencer/strips/effects/multiply.html#bpy-types-multiplysequence"),
+ ("bpy.types.multicamsequence*", "video_editing/edit/montage/strips/effects/multicam.html#bpy-types-multicamsequence"),
+ ("bpy.types.multiplysequence*", "video_editing/edit/montage/strips/effects/multiply.html#bpy-types-multiplysequence"),
("bpy.types.multiresmodifier*", "modeling/modifiers/generate/multiresolution.html#bpy-types-multiresmodifier"),
- ("bpy.types.overdropsequence*", "video_editing/sequencer/strips/effects/alpha_over_under_overdrop.html#bpy-types-overdropsequence"),
+ ("bpy.types.overdropsequence*", "video_editing/edit/montage/strips/effects/alpha_over_under_overdrop.html#bpy-types-overdropsequence"),
("bpy.types.paint.show_brush*", "sculpt_paint/brush/cursor.html#bpy-types-paint-show-brush"),
("bpy.types.paint.use_cavity*", "sculpt_paint/texture_paint/tool_settings/mask.html#bpy-types-paint-use-cavity"),
("bpy.types.particlesettings*", "physics/particles/index.html#bpy-types-particlesettings"),
@@ -2116,8 +2194,8 @@ url_manual_mapping = (
("bpy.types.scene.frame_step*", "render/output/properties/frame_range.html#bpy-types-scene-frame-step"),
("bpy.types.sceneeevee.bloom*", "render/eevee/render_settings/bloom.html#bpy-types-sceneeevee-bloom"),
("bpy.types.sculpt.show_mask*", "sculpt_paint/sculpting/editing/mask.html#bpy-types-sculpt-show-mask"),
- ("bpy.types.sequence.channel*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sequence-channel"),
- ("bpy.types.sequencemodifier*", "video_editing/sequencer/sidebar/modifiers.html#bpy-types-sequencemodifier"),
+ ("bpy.types.sequence.channel*", "editors/video_sequencer/sequencer/sidebar/strip.html#bpy-types-sequence-channel"),
+ ("bpy.types.sequencemodifier*", "editors/video_sequencer/sequencer/sidebar/modifiers.html#bpy-types-sequencemodifier"),
("bpy.types.shaderfxcolorize*", "grease_pencil/visual_effects/colorize.html#bpy-types-shaderfxcolorize"),
("bpy.types.shaderfxpixelate*", "grease_pencil/visual_effects/pixelate.html#bpy-types-shaderfxpixelate"),
("bpy.types.shadernodeinvert*", "render/shader_nodes/color/invert.html#bpy-types-shadernodeinvert"),
@@ -2136,11 +2214,12 @@ url_manual_mapping = (
("bpy.types.spaceview3d.lock*", "editors/3dview/sidebar.html#bpy-types-spaceview3d-lock"),
("bpy.types.spaceview3d.show*", "editors/3dview/display/index.html#bpy-types-spaceview3d-show"),
("bpy.types.sphfluidsettings*", "physics/particles/emitter/physics/fluid.html#bpy-types-sphfluidsettings"),
- ("bpy.types.subtractsequence*", "video_editing/sequencer/strips/effects/subtract.html#bpy-types-subtractsequence"),
+ ("bpy.types.subtractsequence*", "video_editing/edit/montage/strips/effects/subtract.html#bpy-types-subtractsequence"),
("bpy.types.text.indentation*", "editors/text_editor.html#bpy-types-text-indentation"),
("bpy.types.texture.contrast*", "render/materials/legacy_textures/colors.html#bpy-types-texture-contrast"),
("bpy.types.texturenodegroup*", "editors/texture_node/types/groups.html#bpy-types-texturenodegroup"),
("bpy.types.texturenodeimage*", "editors/texture_node/types/input/image.html#bpy-types-texturenodeimage"),
+ ("bpy.types.texturenodescale*", "editors/texture_node/types/distort/scale.html#bpy-types-texturenodescale"),
("bpy.types.viewlayer.use_ao*", "render/layers/introduction.html#bpy-types-viewlayer-use-ao"),
("bpy.ops.armature.dissolve*", "animation/armatures/bones/editing/delete.html#bpy-ops-armature-dissolve"),
("bpy.ops.armature.separate*", "animation/armatures/bones/editing/separate_bones.html#bpy-ops-armature-separate"),
@@ -2205,7 +2284,6 @@ url_manual_mapping = (
("bpy.types.floorconstraint*", "animation/constraints/relationship/floor.html#bpy-types-floorconstraint"),
("bpy.types.fmodifiercycles*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifiercycles"),
("bpy.types.fmodifierlimits*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifierlimits"),
- ("bpy.types.geometrynodearc*", "modeling/geometry_nodes/curve_primitives/arc.html#bpy-types-geometrynodearc"),
("bpy.types.imagepaint.mode*", "sculpt_paint/texture_paint/tool_settings/texture_slots.html#bpy-types-imagepaint-mode"),
("bpy.types.latticemodifier*", "modeling/modifiers/deform/lattice.html#bpy-types-latticemodifier"),
("bpy.types.materiallineart*", "render/materials/line_art.html#bpy-types-materiallineart"),
@@ -2244,6 +2322,7 @@ url_manual_mapping = (
("bpy.ops.gpencil.dissolve*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-dissolve"),
("bpy.ops.graph.frame_jump*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-frame-jump"),
("bpy.ops.graph.sound_bake*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-sound-bake"),
+ ("bpy.ops.marker.duplicate*", "animation/markers.html#bpy-ops-marker-duplicate"),
("bpy.ops.mask.select_less*", "movie_clip/masking/selecting.html#bpy-ops-mask-select-less"),
("bpy.ops.mask.select_more*", "movie_clip/masking/selecting.html#bpy-ops-mask-select-more"),
("bpy.ops.mesh.convex_hull*", "modeling/meshes/editing/mesh/convex_hull.html#bpy-ops-mesh-convex-hull"),
@@ -2260,10 +2339,10 @@ url_manual_mapping = (
("bpy.ops.scene.view_layer*", "render/layers/introduction.html#bpy-ops-scene-view-layer"),
("bpy.ops.screen.redo_last*", "interface/undo_redo.html#bpy-ops-screen-redo-last"),
("bpy.ops.sculpt.mask_init*", "sculpt_paint/sculpting/editing/mask.html#bpy-ops-sculpt-mask-init"),
- ("bpy.ops.sequencer.delete*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-delete"),
- ("bpy.ops.sequencer.reload*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-reload"),
- ("bpy.ops.sequencer.unlock*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-unlock"),
- ("bpy.ops.sequencer.unmute*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-unmute"),
+ ("bpy.ops.sequencer.delete*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-delete"),
+ ("bpy.ops.sequencer.reload*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-reload"),
+ ("bpy.ops.sequencer.unlock*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-unlock"),
+ ("bpy.ops.sequencer.unmute*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-unmute"),
("bpy.ops.transform.mirror*", "scene_layout/object/editing/mirror.html#bpy-ops-transform-mirror"),
("bpy.ops.transform.resize*", "scene_layout/object/editing/transform/scale.html#bpy-ops-transform-resize"),
("bpy.ops.transform.rotate*", "scene_layout/object/editing/transform/rotate.html#bpy-ops-transform-rotate"),
@@ -2277,7 +2356,7 @@ url_manual_mapping = (
("bpy.ops.wm.save_mainfile*", "files/blend/open_save.html#bpy-ops-wm-save-mainfile"),
("bpy.types.bone.show_wire*", "animation/armatures/bones/properties/display.html#bpy-types-bone-show-wire"),
("bpy.types.brush.hardness*", "sculpt_paint/sculpting/tool_settings/brush_settings.html#bpy-types-brush-hardness"),
- ("bpy.types.curvesmodifier*", "video_editing/sequencer/sidebar/modifiers.html#bpy-types-curvesmodifier"),
+ ("bpy.types.curvesmodifier*", "editors/video_sequencer/sequencer/sidebar/modifiers.html#bpy-types-curvesmodifier"),
("bpy.types.ffmpegsettings*", "render/output/properties/output.html#bpy-types-ffmpegsettings"),
("bpy.types.fmodifiernoise*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifiernoise"),
("bpy.types.image.filepath*", "editors/image/image_settings.html#bpy-types-image-filepath"),
@@ -2299,7 +2378,7 @@ url_manual_mapping = (
("bpy.types.shadernodebump*", "render/shader_nodes/vector/bump.html#bpy-types-shadernodebump"),
("bpy.types.shadernodemath*", "render/shader_nodes/converter/math.html#bpy-types-shadernodemath"),
("bpy.types.smoothmodifier*", "modeling/modifiers/deform/smooth.html#bpy-types-smoothmodifier"),
- ("bpy.types.sound.use_mono*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sound-use-mono"),
+ ("bpy.types.sound.use_mono*", "editors/video_sequencer/sequencer/sidebar/strip.html#bpy-types-sound-use-mono"),
("bpy.types.spline.order_u*", "modeling/curves/properties/active_spline.html#bpy-types-spline-order-u"),
("bpy.types.timelinemarker*", "animation/markers.html#bpy-types-timelinemarker"),
("bpy.types.usersolidlight*", "editors/preferences/lights.html#bpy-types-usersolidlight"),
@@ -2346,7 +2425,7 @@ url_manual_mapping = (
("bpy.ops.pose.select_all*", "animation/armatures/posing/selecting.html#bpy-ops-pose-select-all"),
("bpy.ops.rigidbody.world*", "physics/rigid_body/world.html#bpy-ops-rigidbody-world"),
("bpy.ops.sculpt.optimize*", "sculpt_paint/sculpting/editing/sculpt.html#bpy-ops-sculpt-optimize"),
- ("bpy.ops.sequencer.split*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-split"),
+ ("bpy.ops.sequencer.split*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-split"),
("bpy.ops.transform.shear*", "modeling/meshes/editing/mesh/transform/shear.html#bpy-ops-transform-shear"),
("bpy.ops.uv.cube_project*", "modeling/meshes/editing/uv.html#bpy-ops-uv-cube-project"),
("bpy.ops.uv.pack_islands*", "modeling/meshes/uv/editing.html#bpy-ops-uv-pack-islands"),
@@ -2366,8 +2445,8 @@ url_manual_mapping = (
("bpy.types.clothmodifier*", "physics/cloth/index.html#bpy-types-clothmodifier"),
("bpy.types.clothsettings*", "physics/cloth/settings/index.html#bpy-types-clothsettings"),
("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.colorsequence*", "video_editing/edit/montage/strips/color.html#bpy-types-colorsequence"),
+ ("bpy.types.crosssequence*", "video_editing/edit/montage/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.editbone.head*", "animation/armatures/bones/properties/transform.html#bpy-types-editbone-head"),
@@ -2375,11 +2454,11 @@ url_manual_mapping = (
("bpy.types.editbone.roll*", "animation/armatures/bones/properties/transform.html#bpy-types-editbone-roll"),
("bpy.types.editbone.tail*", "animation/armatures/bones/properties/transform.html#bpy-types-editbone-tail"),
("bpy.types.fieldsettings*", "physics/forces/force_fields/index.html#bpy-types-fieldsettings"),
- ("bpy.types.imagesequence*", "video_editing/sequencer/strips/image.html#bpy-types-imagesequence"),
+ ("bpy.types.imagesequence*", "video_editing/edit/montage/strips/image.html#bpy-types-imagesequence"),
("bpy.types.marbletexture*", "render/materials/legacy_textures/types/marble.html#bpy-types-marbletexture"),
("bpy.types.modifier.name*", "modeling/modifiers/introduction.html#bpy-types-modifier-name"),
("bpy.types.modifier.show*", "modeling/modifiers/introduction.html#bpy-types-modifier-show"),
- ("bpy.types.moviesequence*", "video_editing/sequencer/strips/movie.html#bpy-types-moviesequence"),
+ ("bpy.types.moviesequence*", "video_editing/edit/montage/strips/movie.html#bpy-types-moviesequence"),
("bpy.types.movietracking*", "movie_clip/tracking/index.html#bpy-types-movietracking"),
("bpy.types.nlastrip.mute*", "editors/nla/sidebar.html#bpy-types-nlastrip-mute"),
("bpy.types.nlastrip.name*", "editors/nla/sidebar.html#bpy-types-nlastrip-name"),
@@ -2390,17 +2469,18 @@ url_manual_mapping = (
("bpy.types.particlebrush*", "physics/particles/mode.html#bpy-types-particlebrush"),
("bpy.types.scene.gravity*", "physics/forces/gravity.html#bpy-types-scene-gravity"),
("bpy.types.sceneeevee.gi*", "render/eevee/render_settings/indirect_lighting.html#bpy-types-sceneeevee-gi"),
- ("bpy.types.scenesequence*", "video_editing/sequencer/strips/scene.html#bpy-types-scenesequence"),
+ ("bpy.types.scenesequence*", "video_editing/edit/montage/strips/scene.html#bpy-types-scenesequence"),
("bpy.types.screwmodifier*", "modeling/modifiers/generate/screw.html#bpy-types-screwmodifier"),
- ("bpy.types.sequence.name*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sequence-name"),
- ("bpy.types.sequenceproxy*", "video_editing/sequencer/sidebar/proxy.html#bpy-types-sequenceproxy"),
+ ("bpy.types.sequence.name*", "editors/video_sequencer/sequencer/sidebar/strip.html#bpy-types-sequence-name"),
+ ("bpy.types.sequenceproxy*", "editors/video_sequencer/sequencer/sidebar/proxy.html#bpy-types-sequenceproxy"),
("bpy.types.shaderfxswirl*", "grease_pencil/visual_effects/swirl.html#bpy-types-shaderfxswirl"),
("bpy.types.shadernodergb*", "render/shader_nodes/input/rgb.html#bpy-types-shadernodergb"),
- ("bpy.types.soundsequence*", "video_editing/sequencer/strips/sound.html#bpy-types-soundsequence"),
+ ("bpy.types.soundsequence*", "video_editing/edit/montage/strips/sound.html#bpy-types-soundsequence"),
("bpy.types.spaceoutliner*", "editors/outliner/index.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.texturenodeat*", "editors/texture_node/types/distort/at.html#bpy-types-texturenodeat"),
("bpy.types.view3doverlay*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay"),
("bpy.types.viewlayer.use*", "render/layers/view_layer.html#bpy-types-viewlayer-use"),
("bpy.types.volumedisplay*", "modeling/volumes/properties.html#bpy-types-volumedisplay"),
@@ -2436,11 +2516,11 @@ url_manual_mapping = (
("bpy.ops.pose.propagate*", "animation/armatures/posing/editing/propagate.html#bpy-ops-pose-propagate"),
("bpy.ops.pose.push_rest*", "animation/armatures/posing/editing/in_betweens.html#bpy-ops-pose-push-rest"),
("bpy.ops.pose.rot_clear*", "animation/armatures/posing/editing/clear.html#bpy-ops-pose-rot-clear"),
- ("bpy.ops.sequencer.lock*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-lock"),
- ("bpy.ops.sequencer.mute*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-mute"),
- ("bpy.ops.sequencer.slip*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-slip"),
- ("bpy.ops.sequencer.snap*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-snap"),
- ("bpy.ops.sequencer.swap*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-swap"),
+ ("bpy.ops.sequencer.lock*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-lock"),
+ ("bpy.ops.sequencer.mute*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-mute"),
+ ("bpy.ops.sequencer.slip*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-slip"),
+ ("bpy.ops.sequencer.snap*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-snap"),
+ ("bpy.ops.sequencer.swap*", "video_editing/edit/montage/editing.html#bpy-ops-sequencer-swap"),
("bpy.ops.transform.bend*", "modeling/meshes/editing/mesh/transform/bend.html#bpy-ops-transform-bend"),
("bpy.ops.transform.tilt*", "modeling/curves/editing/control_points.html#bpy-ops-transform-tilt"),
("bpy.ops.uv.snap_cursor*", "modeling/meshes/uv/editing.html#bpy-ops-uv-snap-cursor"),
@@ -2451,7 +2531,7 @@ url_manual_mapping = (
("bpy.types.castmodifier*", "modeling/modifiers/deform/cast.html#bpy-types-castmodifier"),
("bpy.types.curve.offset*", "modeling/curves/properties/geometry.html#bpy-types-curve-offset"),
("bpy.types.geometrynode*", "modeling/geometry_nodes/index.html#bpy-types-geometrynode"),
- ("bpy.types.glowsequence*", "video_editing/sequencer/strips/effects/glow.html#bpy-types-glowsequence"),
+ ("bpy.types.glowsequence*", "video_editing/edit/montage/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"),
("bpy.types.image.source*", "editors/image/image_settings.html#bpy-types-image-source"),
@@ -2459,23 +2539,23 @@ url_manual_mapping = (
("bpy.types.latticepoint*", "animation/lattice.html#bpy-types-latticepoint"),
("bpy.types.magictexture*", "render/materials/legacy_textures/types/magic.html#bpy-types-magictexture"),
("bpy.types.maskmodifier*", "modeling/modifiers/generate/mask.html#bpy-types-maskmodifier"),
- ("bpy.types.masksequence*", "video_editing/sequencer/strips/mask.html#bpy-types-masksequence"),
+ ("bpy.types.masksequence*", "video_editing/edit/montage/strips/mask.html#bpy-types-masksequence"),
("bpy.types.materialslot*", "render/materials/assignment.html#bpy-types-materialslot"),
- ("bpy.types.metasequence*", "video_editing/sequencer/meta.html#bpy-types-metasequence"),
+ ("bpy.types.metasequence*", "video_editing/edit/montage/meta.html#bpy-types-metasequence"),
("bpy.types.object.color*", "scene_layout/object/properties/display.html#bpy-types-object-color"),
("bpy.types.object.delta*", "scene_layout/object/properties/transforms.html#bpy-types-object-delta"),
("bpy.types.object.empty*", "modeling/empties.html#bpy-types-object-empty"),
("bpy.types.object.scale*", "scene_layout/object/properties/transforms.html#bpy-types-object-scale"),
("bpy.types.particleedit*", "physics/particles/mode.html#bpy-types-particleedit"),
("bpy.types.scene.camera*", "scene_layout/scene/properties.html#bpy-types-scene-camera"),
- ("bpy.types.sequencecrop*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sequencecrop"),
+ ("bpy.types.sequencecrop*", "editors/video_sequencer/sequencer/sidebar/strip.html#bpy-types-sequencecrop"),
("bpy.types.shaderfxblur*", "grease_pencil/visual_effects/blur.html#bpy-types-shaderfxblur"),
("bpy.types.shaderfxflip*", "grease_pencil/visual_effects/flip.html#bpy-types-shaderfxflip"),
("bpy.types.shaderfxglow*", "grease_pencil/visual_effects/glow.html#bpy-types-shaderfxglow"),
("bpy.types.shaderfxwave*", "grease_pencil/visual_effects/wave_distortion.html#bpy-types-shaderfxwave"),
("bpy.types.skinmodifier*", "modeling/modifiers/generate/skin.html#bpy-types-skinmodifier"),
("bpy.types.spaceconsole*", "editors/python_console.html#bpy-types-spaceconsole"),
- ("bpy.types.textsequence*", "video_editing/sequencer/strips/text.html#bpy-types-textsequence"),
+ ("bpy.types.textsequence*", "video_editing/edit/montage/strips/text.html#bpy-types-textsequence"),
("bpy.types.unitsettings*", "scene_layout/scene/properties.html#bpy-types-unitsettings"),
("bpy.types.vertexcolors*", "sculpt_paint/vertex_paint/index.html#bpy-types-vertexcolors"),
("bpy.types.view3dcursor*", "editors/3dview/3d_cursor.html#bpy-types-view3dcursor"),
@@ -2483,7 +2563,7 @@ url_manual_mapping = (
("bpy.types.warpmodifier*", "modeling/modifiers/deform/warp.html#bpy-types-warpmodifier"),
("bpy.types.wavemodifier*", "modeling/modifiers/deform/wave.html#bpy-types-wavemodifier"),
("bpy.types.weldmodifier*", "modeling/modifiers/generate/weld.html#bpy-types-weldmodifier"),
- ("bpy.types.wipesequence*", "video_editing/sequencer/strips/transitions/wipe.html#bpy-types-wipesequence"),
+ ("bpy.types.wipesequence*", "video_editing/edit/montage/strips/transitions/wipe.html#bpy-types-wipesequence"),
("bpy.ops.armature.fill*", "animation/armatures/bones/editing/fill_between_joints.html#bpy-ops-armature-fill"),
("bpy.ops.asset.tag_add*", "editors/asset_browser.html#bpy-ops-asset-tag-add"),
("bpy.ops.clip.prefetch*", "movie_clip/tracking/clip/editing/clip.html#bpy-ops-clip-prefetch"),
@@ -2494,6 +2574,9 @@ url_manual_mapping = (
("bpy.ops.image.project*", "sculpt_paint/texture_paint/tool_settings/options.html#bpy-ops-image-project"),
("bpy.ops.image.replace*", "editors/image/editing.html#bpy-ops-image-replace"),
("bpy.ops.image.save_as*", "editors/image/editing.html#bpy-ops-image-save-as"),
+ ("bpy.ops.marker.delete*", "animation/markers.html#bpy-ops-marker-delete"),
+ ("bpy.ops.marker.rename*", "animation/markers.html#bpy-ops-marker-rename"),
+ ("bpy.ops.marker.select*", "animation/markers.html#bpy-ops-marker-select"),
("bpy.ops.material.copy*", "render/materials/assignment.html#bpy-ops-material-copy"),
("bpy.ops.mesh.decimate*", "modeling/meshes/editing/mesh/cleanup.html#bpy-ops-mesh-decimate"),
("bpy.ops.mesh.dissolve*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-dissolve"),
@@ -2513,7 +2596,7 @@ url_manual_mapping = (
("bpy.ops.wm.obj_export*", "files/import_export/obj.html#bpy-ops-wm-obj-export"),
("bpy.ops.wm.properties*", "files/data_blocks.html#bpy-ops-wm-properties"),
("bpy.ops.wm.usd_export*", "files/import_export/usd.html#bpy-ops-wm-usd-export"),
- ("bpy.types.addsequence*", "video_editing/sequencer/strips/effects/add.html#bpy-types-addsequence"),
+ ("bpy.types.addsequence*", "video_editing/edit/montage/strips/effects/add.html#bpy-types-addsequence"),
("bpy.types.brush.cloth*", "sculpt_paint/sculpting/tools/cloth.html#bpy-types-brush-cloth"),
("bpy.types.camera.show*", "render/cameras.html#bpy-types-camera-show"),
("bpy.types.consoleline*", "editors/python_console.html#bpy-types-consoleline"),
@@ -2588,6 +2671,7 @@ url_manual_mapping = (
("bpy.ops.file.parent*", "editors/file_browser.html#bpy-ops-file-parent"),
("bpy.ops.graph.clean*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-clean"),
("bpy.ops.graph.paste*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-paste"),
+ ("bpy.ops.marker.move*", "animation/markers.html#bpy-ops-marker-move"),
("bpy.ops.mesh.bisect*", "modeling/meshes/editing/mesh/bisect.html#bpy-ops-mesh-bisect"),
("bpy.ops.mesh.delete*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-delete"),
("bpy.ops.nla.move_up*", "editors/nla/editing.html#bpy-ops-nla-move-up"),
@@ -2636,6 +2720,7 @@ url_manual_mapping = (
("bpy.ops.image.pack*", "editors/image/editing.html#bpy-ops-image-pack"),
("bpy.ops.image.save*", "editors/image/editing.html#bpy-ops-image-save"),
("bpy.ops.image.tile*", "modeling/meshes/uv/workflows/udims.html#bpy-ops-image-tile"),
+ ("bpy.ops.marker.add*", "animation/markers.html#bpy-ops-marker-add"),
("bpy.ops.mesh.bevel*", "modeling/meshes/editing/edge/bevel.html#bpy-ops-mesh-bevel"),
("bpy.ops.mesh.inset*", "modeling/meshes/editing/face/inset_faces.html#bpy-ops-mesh-inset"),
("bpy.ops.mesh.knife*", "modeling/meshes/tools/knife.html#bpy-ops-mesh-knife"),
@@ -2689,6 +2774,7 @@ url_manual_mapping = (
("bpy.types.textbox*", "modeling/texts/properties.html#bpy-types-textbox"),
("bpy.types.texture*", "render/materials/legacy_textures/index.html#bpy-types-texture"),
("bpy.ops.armature*", "animation/armatures/index.html#bpy-ops-armature"),
+ ("bpy.ops.geometry*", "modeling/index.html#bpy-ops-geometry"),
("bpy.ops.material*", "render/materials/index.html#bpy-ops-material"),
("bpy.ops.nla.bake*", "animation/actions.html#bpy-ops-nla-bake"),
("bpy.ops.nla.snap*", "editors/nla/editing.html#bpy-ops-nla-snap"),
diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
index a1913945364..6408873f13e 100644
--- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py
+++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
@@ -1026,7 +1026,7 @@ def km_markers(params):
*_template_items_select_actions(params, "marker.select_all"),
("marker.delete", {"type": 'X', "value": 'PRESS'}, None),
("marker.delete", {"type": 'DEL', "value": 'PRESS'}, None),
- ("marker.rename", {"type": 'M', "value": 'PRESS', "ctrl": True}, None),
+ op_panel("TOPBAR_PT_name_marker", {"type": 'F2', "value": 'PRESS'}, [("keep_open", False)]),
("marker.move", {"type": 'G', "value": 'PRESS'}, None),
("marker.camera_bind", {"type": 'B', "value": 'PRESS', "ctrl": True}, None),
])
@@ -1387,7 +1387,8 @@ def km_view3d(params):
("view3d.localview", {"type": 'NUMPAD_SLASH', "value": 'PRESS'}, None),
("view3d.localview", {"type": 'SLASH', "value": 'PRESS'}, None),
("view3d.localview", {"type": 'MOUSESMARTZOOM', "value": 'ANY'}, None),
- ("view3d.localview_remove_from", {"type": 'M', "value": 'PRESS'}, None),
+ ("view3d.localview_remove_from", {"type": 'NUMPAD_SLASH', "value": 'PRESS', "alt": True}, None),
+ ("view3d.localview_remove_from", {"type": 'SLASH', "value": 'PRESS', "alt": True}, None),
# Navigation.
("view3d.rotate", {"type": 'MOUSEROTATE', "value": 'ANY'}, None),
*((
@@ -1832,7 +1833,6 @@ def km_graph_editor(params):
*_template_items_proportional_editing(
params, connected=False, toggle_data_path='tool_settings.use_proportional_fcurve'),
("marker.add", {"type": 'M', "value": 'PRESS'}, None),
- ("marker.rename", {"type": 'M', "value": 'PRESS', "ctrl": True}, None),
*_template_items_context_menu("GRAPH_MT_context_menu", params.context_menu_event),
])
@@ -2486,7 +2486,6 @@ def km_dopesheet(params):
*_template_items_proportional_editing(
params, connected=False, toggle_data_path='tool_settings.use_proportional_action'),
("marker.add", {"type": 'M', "value": 'PRESS'}, None),
- ("marker.rename", {"type": 'M', "value": 'PRESS', "ctrl": True}, None),
("marker.camera_bind", {"type": 'B', "value": 'PRESS', "ctrl": True}, None),
*_template_items_context_menu("DOPESHEET_MT_context_menu", params.context_menu_event),
*_template_items_change_frame(params),
@@ -2510,7 +2509,8 @@ def km_nla_generic(_params):
*_template_space_region_type_toggle(
sidebar_key={"type": 'N', "value": 'PRESS'},
),
- ("nla.tweakmode_enter", {"type": 'TAB', "value": 'PRESS'}, None),
+ ("nla.tweakmode_enter", {"type": 'TAB', "value": 'PRESS'},
+ {"properties": [("use_upper_stack_evaluation", False)]}),
("nla.tweakmode_exit", {"type": 'TAB', "value": 'PRESS'}, None),
("nla.tweakmode_enter", {"type": 'TAB', "value": 'PRESS', "shift": True},
{"properties": [("isolate_action", True)]}),
@@ -2620,7 +2620,6 @@ def km_nla_editor(params):
("transform.transform", {"type": 'S', "value": 'PRESS'},
{"properties": [("mode", 'TIME_SCALE')]}),
("marker.add", {"type": 'M', "value": 'PRESS'}, None),
- ("marker.rename", {"type": 'M', "value": 'PRESS', "ctrl": True}, None),
*_template_items_context_menu("NLA_MT_context_menu", params.context_menu_event),
*_template_items_change_frame(params),
])
@@ -2929,7 +2928,6 @@ def km_sequencer(params):
("transform.transform", {"type": 'E', "value": 'PRESS'},
{"properties": [("mode", 'TIME_EXTEND')]}),
("marker.add", {"type": 'M', "value": 'PRESS'}, None),
- ("marker.rename", {"type": 'M', "value": 'PRESS', "ctrl": True}, None),
("sequencer.select_side_of_frame", {"type": 'LEFT_BRACKET', "value": 'PRESS'},
{"properties": [("side", 'LEFT')]}),
("sequencer.select_side_of_frame", {"type": 'RIGHT_BRACKET', "value": 'PRESS'},
@@ -7372,7 +7370,7 @@ def km_3d_view_tool_sculpt_mask_by_color(params):
"3D View Tool: Sculpt, Mask by Color",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
- ("sculpt.mask_by_color", {"type": params.tool_mouse, "value": 'CLICK'}, None)
+ ("sculpt.mask_by_color", {"type": params.tool_mouse, "value": 'PRESS'}, None)
]},
)
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 4c1b905ac05..64039f200e9 100644
--- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
+++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
@@ -836,7 +836,7 @@ def km_markers(params):
("marker.select_all", {"type": 'I', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'INVERT')]}),
("marker.delete", {"type": 'BACK_SPACE', "value": 'PRESS'}, None),
("marker.delete", {"type": 'DEL', "value": 'PRESS'}, None),
- ("marker.rename", {"type": 'RET', "value": 'PRESS'}, None),
+ op_panel("TOPBAR_PT_name_marker", {"type": 'RET', "value": 'PRESS'}, [("keep_open", False)]),
("marker.move", {"type": 'W', "value": 'PRESS'}, None),
])
@@ -937,7 +937,6 @@ def km_graph_editor(params):
("wm.context_menu_enum", {"type": 'X', "value": 'PRESS'},
{"properties": [("data_path", 'space_data.auto_snap')]}),
("marker.add", {"type": 'M', "value": 'PRESS'}, None),
- ("marker.rename", {"type": 'RET', "value": 'PRESS'}, None),
op_menu_pie("GRAPH_MT_snap_pie", {"type": 'X', "value": 'PRESS', "shift": True}),
])
@@ -1438,7 +1437,6 @@ def km_dopesheet(params):
("wm.context_toggle", {"type": 'B', "value": 'PRESS'},
{"properties": [("data_path", 'tool_settings.use_proportional_action')]}),
("marker.add", {"type": 'M', "value": 'PRESS'}, None),
- ("marker.rename", {"type": 'RET', "value": 'PRESS'}, None),
("anim.start_frame_set", {"type": 'LEFT_ARROW', "value": 'PRESS', "ctrl": True}, None),
("anim.end_frame_set", {"type": 'RIGHT_ARROW', "value": 'PRESS', "ctrl": True}, None),
])
@@ -1548,7 +1546,6 @@ def km_nla_editor(params):
*_template_items_context_menu("NLA_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
op_menu_pie("NLA_MT_snap_pie", {"type": 'X', "value": 'PRESS', "shift": True}),
("marker.add", {"type": 'M', "value": 'PRESS'}, None),
- ("marker.rename", {"type": 'RET', "value": 'PRESS'}, None),
])
return keymap
@@ -1835,7 +1832,6 @@ def km_sequencer(params):
{"properties": [("mode", 'TIME_EXTEND')]}),
*_template_items_context_menu("SEQUENCER_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
("marker.add", {"type": 'M', "value": 'PRESS'}, None),
- ("marker.rename", {"type": 'RET', "value": 'PRESS'}, None),
# Tools
op_tool_cycle("builtin.select_box", {"type": 'Q', "value": 'PRESS'}),
op_tool_cycle("builtin.blade", {"type": 'B', "value": 'PRESS'}),
diff --git a/release/scripts/startup/bl_operators/geometry_nodes.py b/release/scripts/startup/bl_operators/geometry_nodes.py
index c3892e988c5..f435efc76fc 100644
--- a/release/scripts/startup/bl_operators/geometry_nodes.py
+++ b/release/scripts/startup/bl_operators/geometry_nodes.py
@@ -26,8 +26,9 @@ def geometry_node_group_empty_new():
def geometry_modifier_poll(context):
ob = context.object
- # Test object support for geometry node modifier (No curves object support yet)
- if not ob or ob.type not in {'MESH', 'POINTCLOUD', 'VOLUME', 'CURVE', 'FONT'}:
+
+ # Test object support for geometry node modifier
+ if not ob or ob.type not in {'MESH', 'POINTCLOUD', 'VOLUME', 'CURVE', 'FONT', 'CURVES'}:
return False
return True
@@ -82,32 +83,7 @@ class NewGeometryNodeTreeAssign(Operator):
return {'FINISHED'}
-class CopyGeometryNodeTreeAssign(Operator):
- """Copy the active geometry node group and assign it to the active modifier"""
-
- bl_idname = "node.copy_geometry_node_group_assign"
- bl_label = "Copy Geometry Node Group"
- bl_options = {'REGISTER', 'UNDO'}
-
- @classmethod
- def poll(cls, context):
- return geometry_modifier_poll(context)
-
- def execute(self, context):
- modifier = context.object.modifiers.active
- if modifier is None:
- return {'CANCELLED'}
-
- group = modifier.node_group
- if group is None:
- return {'CANCELLED'}
-
- modifier.node_group = group.copy()
- return {'FINISHED'}
-
-
classes = (
NewGeometryNodesModifier,
NewGeometryNodeTreeAssign,
- CopyGeometryNodeTreeAssign,
)
diff --git a/release/scripts/startup/bl_operators/node.py b/release/scripts/startup/bl_operators/node.py
index b8368cd1970..60a684ae5e8 100644
--- a/release/scripts/startup/bl_operators/node.py
+++ b/release/scripts/startup/bl_operators/node.py
@@ -73,7 +73,11 @@ class NodeAddOperator:
for n in tree.nodes:
n.select = False
- node = tree.nodes.new(type=node_type)
+ try:
+ node = tree.nodes.new(type=node_type)
+ except RuntimeError as e:
+ self.report({'ERROR'}, str(e))
+ return None
for setting in self.settings:
# XXX catch exceptions here?
diff --git a/release/scripts/startup/bl_ui/properties_data_mesh.py b/release/scripts/startup/bl_ui/properties_data_mesh.py
index 1bfd82cc790..1ba7a4a5413 100644
--- a/release/scripts/startup/bl_ui/properties_data_mesh.py
+++ b/release/scripts/startup/bl_ui/properties_data_mesh.py
@@ -599,20 +599,21 @@ class MESH_UL_color_attributes(UIList, ColorAttributesListBase):
split.emboss = 'NONE'
split.prop(attribute, "name", text="")
- active_render = _index == data.color_attributes.render_color_index
+ sub = split.row()
+ sub.alignment = 'RIGHT'
+ sub.active = False
+ sub.label(text="%s â–¶ %s" % (domain_name, data_type.name))
- props = split.operator(
+ active_render = _index == data.color_attributes.render_color_index
+
+ row = layout.row()
+ row.emboss = 'NONE'
+ prop = row.operator(
"geometry.color_attribute_render_set",
text="",
icon='RESTRICT_RENDER_OFF' if active_render else 'RESTRICT_RENDER_ON',
)
-
- props.name = attribute.name
-
- sub = split.row()
- sub.alignment = 'RIGHT'
- sub.active = False
- sub.label(text="%s â–¶ %s" % (domain_name, data_type.name))
+ prop.name = attribute.name
class MESH_UL_color_attributes_selector(UIList, ColorAttributesListBase):
diff --git a/release/scripts/startup/bl_ui/space_nla.py b/release/scripts/startup/bl_ui/space_nla.py
index 99324ac5d50..f0e991c768d 100644
--- a/release/scripts/startup/bl_ui/space_nla.py
+++ b/release/scripts/startup/bl_ui/space_nla.py
@@ -150,6 +150,23 @@ class NLA_MT_marker(Menu):
marker_menu_generic(layout, context)
+class NLA_MT_marker_select(Menu):
+ bl_label = 'Select'
+
+ def draw(self, context):
+ layout = self.layout
+
+ layout.operator("marker.select_all", text="All").action = 'SELECT'
+ layout.operator("marker.select_all", text="None").action = 'DESELECT'
+ layout.operator("marker.select_all", text="Invert").action = 'INVERT'
+
+ layout.separator()
+
+ layout.operator("marker.select_leftright", text="Before Current Frame").mode = 'LEFT'
+ layout.operator("marker.select_leftright", text="After Current Frame").mode = 'RIGHT'
+
+
+
class NLA_MT_edit(Menu):
bl_label = "Edit"
@@ -197,7 +214,8 @@ class NLA_MT_edit(Menu):
layout.operator("nla.tweakmode_exit", text="Stop Tweaking Strip Actions")
else:
layout.operator("nla.tweakmode_enter", text="Start Editing Stashed Action").isolate_action = True
- layout.operator("nla.tweakmode_enter", text="Start Tweaking Strip Actions")
+ layout.operator("nla.tweakmode_enter", text="Start Tweaking Strip Actions (Full Stack)").use_upper_stack_evaluation = True
+ layout.operator("nla.tweakmode_enter", text="Start Tweaking Strip Actions (Lower Stack)").use_upper_stack_evaluation = False
class NLA_MT_add(Menu):
@@ -271,7 +289,8 @@ class NLA_MT_context_menu(Menu):
layout.operator("nla.tweakmode_exit", text="Stop Tweaking Strip Actions")
else:
layout.operator("nla.tweakmode_enter", text="Start Editing Stashed Action").isolate_action = True
- layout.operator("nla.tweakmode_enter", text="Start Tweaking Strip Actions")
+ layout.operator("nla.tweakmode_enter", text="Start Tweaking Strip Actions (Full Stack)").use_upper_stack_evaluation = True
+ layout.operator("nla.tweakmode_enter", text="Start Tweaking Strip Actions (Lower Stack)").use_upper_stack_evaluation = False
layout.separator()
@@ -312,6 +331,7 @@ classes = (
NLA_MT_view,
NLA_MT_select,
NLA_MT_marker,
+ NLA_MT_marker_select,
NLA_MT_add,
NLA_MT_edit_transform,
NLA_MT_snap_pie,
diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py
index d7d905cb820..d7c6bf63423 100644
--- a/release/scripts/startup/bl_ui/space_node.py
+++ b/release/scripts/startup/bl_ui/space_node.py
@@ -149,7 +149,7 @@ class NODE_HT_header(Header):
active_modifier = ob.modifiers.active
if active_modifier and active_modifier.type == 'NODES':
if active_modifier.node_group:
- row.template_ID(active_modifier, "node_group", new="node.copy_geometry_node_group_assign")
+ row.template_ID(active_modifier, "node_group", new="object.geometry_node_tree_copy_assign")
else:
row.template_ID(active_modifier, "node_group", new="node.new_geometry_node_group_assign")
else:
@@ -709,6 +709,7 @@ class NODE_PT_overlay(Panel):
if snode.tree_type == 'GeometryNodeTree':
col.separator()
col.prop(overlay, "show_timing", text="Timings")
+ col.prop(overlay, "show_named_attributes", text="Named Attributes")
class NODE_UL_interface_sockets(bpy.types.UIList):
diff --git a/release/scripts/startup/bl_ui/space_time.py b/release/scripts/startup/bl_ui/space_time.py
index 5296900fa30..13ab6e67b00 100644
--- a/release/scripts/startup/bl_ui/space_time.py
+++ b/release/scripts/startup/bl_ui/space_time.py
@@ -187,11 +187,17 @@ def marker_menu_generic(layout, context):
layout.separator()
- layout.operator("marker.rename", text="Rename Marker")
+ props = layout.operator("wm.call_panel", text="Rename Marker")
+ props.name = "TOPBAR_PT_name_marker"
+ props.keep_open = False
layout.operator("marker.move", text="Move Marker")
layout.separator()
+ layout.menu('NLA_MT_marker_select')
+
+ layout.separator()
+
layout.operator("marker.camera_bind")
layout.separator()
diff --git a/release/scripts/startup/bl_ui/space_topbar.py b/release/scripts/startup/bl_ui/space_topbar.py
index 55dcb4a20eb..2cf50bdbf95 100644
--- a/release/scripts/startup/bl_ui/space_topbar.py
+++ b/release/scripts/startup/bl_ui/space_topbar.py
@@ -851,6 +851,64 @@ class TOPBAR_PT_name(Panel):
row.label(text="No active item")
+class TOPBAR_PT_name_marker(Panel):
+ bl_space_type = 'TOPBAR' # dummy
+ bl_region_type = 'HEADER'
+ bl_label = "Rename Marker"
+ bl_ui_units_x = 14
+
+ @staticmethod
+ def is_using_pose_markers(context):
+ sd = context.space_data
+ return (sd.type == 'DOPESHEET_EDITOR' and sd.mode in {'ACTION', 'SHAPEKEY'} and
+ sd.show_pose_markers and sd.action)
+
+ @staticmethod
+ def get_selected_marker(context):
+ if TOPBAR_PT_name_marker.is_using_pose_markers(context):
+ markers = context.space_data.action.pose_markers
+ else:
+ markers = context.scene.timeline_markers
+
+ for marker in markers:
+ if marker.select:
+ return marker
+ return None
+
+ @staticmethod
+ def row_with_icon(layout, icon):
+ row = layout.row()
+ row.activate_init = True
+ row.label(icon=icon)
+ return row
+
+ def draw(self, context):
+ layout = self.layout
+
+ layout.label(text="Marker Name")
+
+ scene = context.scene
+ if scene.tool_settings.lock_markers:
+ row = self.row_with_icon(layout, 'ERROR')
+ label = "Markers are locked"
+ row.label(text=label)
+ return
+
+ marker = self.get_selected_marker(context)
+ if marker is None:
+ row = self.row_with_icon(layout, 'ERROR')
+ row.label(text="No active marker")
+ return
+
+ icon = 'TIME'
+ if marker.camera is not None:
+ icon = 'CAMERA_DATA'
+ elif self.is_using_pose_markers(context):
+ icon = 'ARMATURE_DATA'
+ row = self.row_with_icon(layout, icon)
+ row.prop(marker, "name", text="")
+
+
classes = (
TOPBAR_HT_upper_bar,
TOPBAR_MT_file_context_menu,
@@ -877,6 +935,7 @@ classes = (
TOPBAR_PT_gpencil_layers,
TOPBAR_PT_gpencil_primitive,
TOPBAR_PT_name,
+ TOPBAR_PT_name_marker,
)
if __name__ == "__main__": # only for live edit.
diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py
index 035c4fd1352..b954d726ca3 100644
--- a/release/scripts/startup/bl_ui/space_userpref.py
+++ b/release/scripts/startup/bl_ui/space_userpref.py
@@ -2281,7 +2281,8 @@ class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel):
),
)
-
+# Keep this as tweaks can be useful to restore.
+"""
class USERPREF_PT_experimental_tweaks(ExperimentalPanel, Panel):
bl_label = "Tweaks"
@@ -2292,6 +2293,7 @@ class USERPREF_PT_experimental_tweaks(ExperimentalPanel, Panel):
),
)
+"""
class USERPREF_PT_experimental_debugging(ExperimentalPanel, Panel):
bl_label = "Debugging"
@@ -2413,7 +2415,7 @@ classes = (
USERPREF_PT_experimental_new_features,
USERPREF_PT_experimental_prototypes,
- USERPREF_PT_experimental_tweaks,
+ # USERPREF_PT_experimental_tweaks,
USERPREF_PT_experimental_debugging,
# Add dynamically generated editor theme panels last,
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index 2e924d5b4c9..74f20aca072 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -2722,6 +2722,9 @@ class VIEW3D_MT_object_apply(Menu):
text_ctxt=i18n_contexts.default,
).target = 'MESH'
layout.operator("object.duplicates_make_real")
+ layout.operator("object.parent_inverse_apply",
+ text="Parent Inverse",
+ text_ctxt=i18n_contexts.default)
class VIEW3D_MT_object_parent(Menu):
@@ -6746,8 +6749,10 @@ class VIEW3D_PT_snapping(Panel):
col.prop(tool_settings, "use_snap_backface_culling")
if obj:
- if object_mode == 'EDIT':
- col.prop(tool_settings, "use_snap_self")
+ if object_mode == 'EDIT' and obj.type not in {'LATTICE', 'META', 'FONT'}:
+ sub = col.column()
+ sub.active = not (tool_settings.use_proportional_edit and obj.type == 'MESH')
+ sub.prop(tool_settings, "use_snap_self")
if object_mode in {'OBJECT', 'POSE', 'EDIT', 'WEIGHT_PAINT'}:
col.prop(tool_settings, "use_snap_align_rotation")
diff --git a/source/blender/blenfont/BLF_api.h b/source/blender/blenfont/BLF_api.h
index 058b0f120f7..e5e2b1711b1 100644
--- a/source/blender/blenfont/BLF_api.h
+++ b/source/blender/blenfont/BLF_api.h
@@ -109,7 +109,7 @@ typedef bool (*BLF_GlyphBoundsFn)(const char *str,
size_t str_step_ofs,
const struct rcti *glyph_step_bounds,
int glyph_advance_x,
- const struct rctf *glyph_bounds,
+ const struct rcti *glyph_bounds,
const int glyph_bearing[2],
void *user_data);
@@ -151,9 +151,9 @@ size_t BLF_width_to_rstrlen(
void BLF_boundbox_ex(int fontid,
const char *str,
size_t str_len,
- struct rctf *box,
+ struct rcti *box,
struct ResultBLF *r_info) ATTR_NONNULL(2);
-void BLF_boundbox(int fontid, const char *str, size_t str_len, struct rctf *box) ATTR_NONNULL();
+void BLF_boundbox(int fontid, const char *str, size_t str_len, struct rcti *box) ATTR_NONNULL();
/**
* The next both function return the width and height
@@ -173,9 +173,9 @@ float BLF_height(int fontid, const char *str, size_t str_len) ATTR_WARN_UNUSED_R
* Return dimensions of the font without any sample text.
*/
int BLF_height_max(int fontid) ATTR_WARN_UNUSED_RESULT;
-float BLF_width_max(int fontid) ATTR_WARN_UNUSED_RESULT;
-float BLF_descender(int fontid) ATTR_WARN_UNUSED_RESULT;
-float BLF_ascender(int fontid) ATTR_WARN_UNUSED_RESULT;
+int BLF_width_max(int fontid) ATTR_WARN_UNUSED_RESULT;
+int BLF_descender(int fontid) ATTR_WARN_UNUSED_RESULT;
+int BLF_ascender(int fontid) ATTR_WARN_UNUSED_RESULT;
/**
* The following function return the width and height of the string, but
@@ -195,7 +195,7 @@ float BLF_fixed_width(int fontid) ATTR_WARN_UNUSED_RESULT;
* have to be enable/disable using BLF_enable/disable.
*/
void BLF_rotation(int fontid, float angle);
-void BLF_clipping(int fontid, float xmin, float ymin, float xmax, float ymax);
+void BLF_clipping(int fontid, int xmin, int ymin, int xmax, int ymax);
void BLF_wordwrap(int fontid, int wrap_width);
#if BLF_BLUR_ENABLE
diff --git a/source/blender/blenfont/intern/blf.c b/source/blender/blenfont/intern/blf.c
index 2b5a2cdf606..a944ab332bd 100644
--- a/source/blender/blenfont/intern/blf.c
+++ b/source/blender/blenfont/intern/blf.c
@@ -342,9 +342,9 @@ void BLF_position(int fontid, float x, float y, float z)
}
}
- font->pos[0] = x;
- font->pos[1] = y;
- font->pos[2] = z;
+ font->pos[0] = round_fl_to_int(x);
+ font->pos[1] = round_fl_to_int(y);
+ font->pos[2] = round_fl_to_int(z);
}
}
@@ -488,7 +488,7 @@ static void blf_draw_gl__start(FontBLF *font)
GPU_matrix_mul(font->m);
}
- GPU_matrix_translate_3fv(font->pos);
+ GPU_matrix_translate_3f(font->pos[0], font->pos[1], font->pos[2]);
if (font->flags & BLF_ASPECT) {
GPU_matrix_scale_3fv(font->aspect);
@@ -589,9 +589,10 @@ size_t BLF_width_to_strlen(
if (font) {
const float xa = (font->flags & BLF_ASPECT) ? font->aspect[0] : 1.0f;
size_t ret;
- ret = blf_font_width_to_strlen(font, str, str_len, width / xa, r_width);
+ int width_result;
+ ret = blf_font_width_to_strlen(font, str, str_len, width / xa, &width_result);
if (r_width) {
- *r_width *= xa;
+ *r_width = (float)width_result * xa;
}
return ret;
}
@@ -610,9 +611,10 @@ size_t BLF_width_to_rstrlen(
if (font) {
const float xa = (font->flags & BLF_ASPECT) ? font->aspect[0] : 1.0f;
size_t ret;
- ret = blf_font_width_to_rstrlen(font, str, str_len, width / xa, r_width);
+ int width_result;
+ ret = blf_font_width_to_rstrlen(font, str, str_len, width / xa, &width_result);
if (r_width) {
- *r_width *= xa;
+ *r_width = (float)width_result * xa;
}
return ret;
}
@@ -624,7 +626,7 @@ size_t BLF_width_to_rstrlen(
}
void BLF_boundbox_ex(
- int fontid, const char *str, const size_t str_len, rctf *r_box, struct ResultBLF *r_info)
+ int fontid, const char *str, const size_t str_len, rcti *r_box, struct ResultBLF *r_info)
{
FontBLF *font = blf_get(fontid);
@@ -640,7 +642,7 @@ void BLF_boundbox_ex(
}
}
-void BLF_boundbox(int fontid, const char *str, const size_t str_len, rctf *r_box)
+void BLF_boundbox(int fontid, const char *str, const size_t str_len, rcti *r_box)
{
BLF_boundbox_ex(fontid, str, str_len, r_box, NULL);
}
@@ -716,7 +718,7 @@ int BLF_height_max(int fontid)
return 0;
}
-float BLF_width_max(int fontid)
+int BLF_width_max(int fontid)
{
FontBLF *font = blf_get(fontid);
@@ -724,10 +726,10 @@ float BLF_width_max(int fontid)
return blf_font_width_max(font);
}
- return 0.0f;
+ return 0;
}
-float BLF_descender(int fontid)
+int BLF_descender(int fontid)
{
FontBLF *font = blf_get(fontid);
@@ -735,10 +737,10 @@ float BLF_descender(int fontid)
return blf_font_descender(font);
}
- return 0.0f;
+ return 0;
}
-float BLF_ascender(int fontid)
+int BLF_ascender(int fontid)
{
FontBLF *font = blf_get(fontid);
@@ -758,7 +760,7 @@ void BLF_rotation(int fontid, float angle)
}
}
-void BLF_clipping(int fontid, float xmin, float ymin, float xmax, float ymax)
+void BLF_clipping(int fontid, int xmin, int ymin, int xmax, int ymax)
{
FontBLF *font = blf_get(fontid);
@@ -889,7 +891,7 @@ void BLF_state_print(int fontid)
printf(" name: '%s'\n", font->name);
printf(" size: %f\n", font->size);
printf(" dpi: %u\n", font->dpi);
- printf(" pos: %.6f %.6f %.6f\n", UNPACK3(font->pos));
+ printf(" pos: %d %d %d\n", UNPACK3(font->pos));
printf(" aspect: (%d) %.6f %.6f %.6f\n",
(font->flags & BLF_ROTATION) != 0,
UNPACK3(font->aspect));
diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c
index 60ff5f6470f..f93cb8b2d64 100644
--- a/source/blender/blenfont/intern/blf_font.c
+++ b/source/blender/blenfont/intern/blf_font.c
@@ -57,34 +57,26 @@ static SpinLock blf_glyph_cache_mutex;
/* May be set to #UI_widgetbase_draw_cache_flush. */
static void (*blf_draw_cache_flush)(void) = NULL;
+static ft_pix blf_font_height_max_ft_pix(struct FontBLF *font);
+static ft_pix blf_font_width_max_ft_pix(struct FontBLF *font);
+
/* -------------------------------------------------------------------- */
/** \name FreeType Utilities (Internal)
* \{ */
-/**
- * Convert a FreeType 26.6 value representing an unscaled design size to pixels.
- * This is an exact copy of the scaling done inside FT_Get_Kerning when called
- * with #FT_KERNING_DEFAULT, including arbitrary resizing for small fonts.
- */
-static int blf_unscaled_F26Dot6_to_pixels(FontBLF *font, FT_Pos value)
+/* Convert a FreeType 26.6 value representing an unscaled design size to factional pixels. */
+static ft_pix blf_unscaled_F26Dot6_to_pixels(FontBLF *font, FT_Pos value)
{
/* Scale value by font size using integer-optimized multiplication. */
FT_Long scaled = FT_MulFix(value, font->face->size->metrics.x_scale);
- /* FreeType states that this '25' has been determined heuristically. */
+ /* Copied from FreeType's FT_Get_Kerning (with FT_KERNING_DEFAULT), scaling down */
+ /* kerning distances at small ppem values so that they don't become too big. */
if (font->face->size->metrics.x_ppem < 25) {
scaled = FT_MulDiv(scaled, font->face->size->metrics.x_ppem, 25);
}
- /* Copies of internal FreeType macros needed here. */
-#define FT_PIX_FLOOR(x) ((x) & ~63)
-#define FT_PIX_ROUND(x) FT_PIX_FLOOR((x) + 32)
-
- /* Round to even 64ths, then divide by 64. */
- return (int)FT_PIX_ROUND(scaled) >> 6;
-
-#undef FT_PIX_FLOOR
-#undef FT_PIX_ROUND
+ return (ft_pix)scaled;
}
/** \} */
@@ -146,12 +138,12 @@ void blf_batch_draw_begin(FontBLF *font)
if (simple_shader) {
/* Offset is applied to each glyph. */
- g_batch.ofs[0] = floorf(font->pos[0]);
- g_batch.ofs[1] = floorf(font->pos[1]);
+ g_batch.ofs[0] = font->pos[0];
+ g_batch.ofs[1] = font->pos[1];
}
else {
/* Offset is baked in modelview mat. */
- zero_v2(g_batch.ofs);
+ zero_v2_int(g_batch.ofs);
}
if (g_batch.active) {
@@ -293,36 +285,39 @@ BLI_INLINE GlyphBLF *blf_glyph_from_utf8_and_step(
return blf_glyph_ensure(font, gc, charcode);
}
-BLI_INLINE int blf_kerning(FontBLF *font, const GlyphBLF *g_prev, const GlyphBLF *g)
+BLI_INLINE ft_pix blf_kerning(FontBLF *font, const GlyphBLF *g_prev, const GlyphBLF *g)
{
- if (!FT_HAS_KERNING(font->face) || g_prev == NULL) {
- return 0;
- }
+ ft_pix adjustment = 0;
- FT_Vector delta = {KERNING_ENTRY_UNSET};
+ /* Small adjust if there is hinting. */
+ adjustment += g->lsb_delta - ((g_prev) ? g_prev->rsb_delta : 0);
- /* Get unscaled kerning value from our cache if ASCII. */
- if ((g_prev->c < KERNING_CACHE_TABLE_SIZE) && (g->c < GLYPH_ASCII_TABLE_SIZE)) {
- delta.x = font->kerning_cache->ascii_table[g->c][g_prev->c];
- }
+ if (FT_HAS_KERNING(font->face) && g_prev) {
+ FT_Vector delta = {KERNING_ENTRY_UNSET};
- /* If not ASCII or not found in cache, ask FreeType for kerning. */
- if (UNLIKELY(delta.x == KERNING_ENTRY_UNSET)) {
- /* Note that this function sets delta values to zero on any error. */
- FT_Get_Kerning(font->face, g_prev->idx, g->idx, FT_KERNING_UNSCALED, &delta);
- }
+ /* Get unscaled kerning value from our cache if ASCII. */
+ if ((g_prev->c < KERNING_CACHE_TABLE_SIZE) && (g->c < GLYPH_ASCII_TABLE_SIZE)) {
+ delta.x = font->kerning_cache->ascii_table[g->c][g_prev->c];
+ }
- /* If ASCII we save this value to our cache for quicker access next time. */
- if ((g_prev->c < KERNING_CACHE_TABLE_SIZE) && (g->c < GLYPH_ASCII_TABLE_SIZE)) {
- font->kerning_cache->ascii_table[g->c][g_prev->c] = (int)delta.x;
- }
+ /* If not ASCII or not found in cache, ask FreeType for kerning. */
+ if (UNLIKELY(delta.x == KERNING_ENTRY_UNSET)) {
+ /* Note that this function sets delta values to zero on any error. */
+ FT_Get_Kerning(font->face, g_prev->idx, g->idx, FT_KERNING_UNSCALED, &delta);
+ }
+
+ /* If ASCII we save this value to our cache for quicker access next time. */
+ if ((g_prev->c < KERNING_CACHE_TABLE_SIZE) && (g->c < GLYPH_ASCII_TABLE_SIZE)) {
+ font->kerning_cache->ascii_table[g->c][g_prev->c] = (int)delta.x;
+ }
- if (delta.x != 0) {
- /* Convert unscaled design units to pixels and move pen. */
- return blf_unscaled_F26Dot6_to_pixels(font, delta.x);
+ if (delta.x != 0) {
+ /* Convert unscaled design units to pixels and move pen. */
+ adjustment += blf_unscaled_F26Dot6_to_pixels(font, delta.x);
+ }
}
- return 0;
+ return adjustment;
}
/** \} */
@@ -336,10 +331,10 @@ static void blf_font_draw_ex(FontBLF *font,
const char *str,
const size_t str_len,
struct ResultBLF *r_info,
- int pen_y)
+ ft_pix pen_y)
{
GlyphBLF *g, *g_prev = NULL;
- int pen_x = 0;
+ ft_pix pen_x = 0;
size_t i = 0;
if (str_len == 0) {
@@ -358,9 +353,9 @@ static void blf_font_draw_ex(FontBLF *font,
pen_x += blf_kerning(font, g_prev, g);
/* do not return this loop if clipped, we want every character tested */
- blf_glyph_draw(font, gc, g, (float)pen_x, (float)pen_y);
+ blf_glyph_draw(font, gc, g, ft_pix_to_int_floor(pen_x), ft_pix_to_int_floor(pen_y));
- pen_x += g->advance_i;
+ pen_x = ft_pix_round_advance(pen_x, g->advance_x);
g_prev = g;
}
@@ -368,7 +363,7 @@ static void blf_font_draw_ex(FontBLF *font,
if (r_info) {
r_info->lines = 1;
- r_info->width = pen_x;
+ r_info->width = ft_pix_to_int(pen_x);
}
}
void blf_font_draw(FontBLF *font, const char *str, const size_t str_len, struct ResultBLF *r_info)
@@ -382,7 +377,9 @@ int blf_font_draw_mono(FontBLF *font, const char *str, const size_t str_len, int
{
GlyphBLF *g;
int col, columns = 0;
- int pen_x = 0, pen_y = 0;
+ ft_pix pen_x = 0, pen_y = 0;
+ ft_pix cwidth_fpx = ft_pix_from_int(cwidth);
+
size_t i = 0;
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
@@ -396,7 +393,7 @@ int blf_font_draw_mono(FontBLF *font, const char *str, const size_t str_len, int
continue;
}
/* do not return this loop if clipped, we want every character tested */
- blf_glyph_draw(font, gc, g, (float)pen_x, (float)pen_y);
+ blf_glyph_draw(font, gc, g, ft_pix_to_int_floor(pen_x), ft_pix_to_int_floor(pen_y));
col = BLI_wcwidth((char32_t)g->c);
if (col < 0) {
@@ -404,7 +401,7 @@ int blf_font_draw_mono(FontBLF *font, const char *str, const size_t str_len, int
}
columns += col;
- pen_x += cwidth * col;
+ pen_x += cwidth_fpx * col;
}
blf_batch_draw_end();
@@ -425,11 +422,11 @@ static void blf_font_draw_buffer_ex(FontBLF *font,
const char *str,
const size_t str_len,
struct ResultBLF *r_info,
- int pen_y)
+ ft_pix pen_y)
{
GlyphBLF *g, *g_prev = NULL;
- int pen_x = (int)font->pos[0];
- int pen_y_basis = (int)font->pos[1] + pen_y;
+ ft_pix pen_x = ft_pix_from_int(font->pos[0]);
+ ft_pix pen_y_basis = ft_pix_from_int(font->pos[1]) + pen_y;
size_t i = 0;
/* buffer specific vars */
@@ -449,18 +446,18 @@ static void blf_font_draw_buffer_ex(FontBLF *font,
}
pen_x += blf_kerning(font, g_prev, g);
- chx = pen_x + ((int)g->pos[0]);
- chy = pen_y_basis + g->dims[1];
+ chx = ft_pix_to_int(pen_x + ft_pix_from_int(g->pos[0]));
+ chy = ft_pix_to_int(pen_y_basis + ft_pix_from_int(g->dims[1]));
if (g->pitch < 0) {
- pen_y = pen_y_basis + (g->dims[1] - g->pos[1]);
+ pen_y = pen_y_basis + ft_pix_from_int(g->dims[1] - g->pos[1]);
}
else {
- pen_y = pen_y_basis - (g->dims[1] - g->pos[1]);
+ pen_y = pen_y_basis - ft_pix_from_int(g->dims[1] - g->pos[1]);
}
- if ((chx + g->dims[0]) >= 0 && chx < buf_info->dims[0] && (pen_y + g->dims[1]) >= 0 &&
- pen_y < buf_info->dims[1]) {
+ if ((chx + g->dims[0]) >= 0 && chx < buf_info->dims[0] &&
+ (ft_pix_to_int(pen_y) + g->dims[1]) >= 0 && ft_pix_to_int(pen_y) < buf_info->dims[1]) {
/* don't draw beyond the buffer bounds */
int width_clip = g->dims[0];
int height_clip = g->dims[1];
@@ -469,17 +466,20 @@ static void blf_font_draw_buffer_ex(FontBLF *font,
if (width_clip + chx > buf_info->dims[0]) {
width_clip -= chx + width_clip - buf_info->dims[0];
}
- if (height_clip + pen_y > buf_info->dims[1]) {
- height_clip -= pen_y + height_clip - buf_info->dims[1];
+ if (height_clip + ft_pix_to_int(pen_y) > buf_info->dims[1]) {
+ height_clip -= ft_pix_to_int(pen_y) + height_clip - buf_info->dims[1];
}
/* drawing below the image? */
if (pen_y < 0) {
- yb_start += (g->pitch < 0) ? -pen_y : pen_y;
- height_clip += pen_y;
+ yb_start += (g->pitch < 0) ? -ft_pix_to_int(pen_y) : ft_pix_to_int(pen_y);
+ height_clip += ft_pix_to_int(pen_y);
pen_y = 0;
}
+ /* Avoid conversions in the pixel writing loop. */
+ const int pen_y_px = ft_pix_to_int(pen_y);
+
if (buf_info->fbuf) {
int yb = yb_start;
for (y = ((chy >= 0) ? 0 : -chy); y < height_clip; y++) {
@@ -488,7 +488,7 @@ static void blf_font_draw_buffer_ex(FontBLF *font,
if (a_byte) {
const float a = (a_byte / 255.0f) * b_col_float[3];
const size_t buf_ofs = (((size_t)(chx + x) +
- ((size_t)(pen_y + y) * (size_t)buf_info->dims[0])) *
+ ((size_t)(pen_y_px + y) * (size_t)buf_info->dims[0])) *
(size_t)buf_info->ch);
float *fbuf = buf_info->fbuf + buf_ofs;
@@ -519,7 +519,7 @@ static void blf_font_draw_buffer_ex(FontBLF *font,
if (a_byte) {
const float a = (a_byte / 255.0f) * b_col_float[3];
const size_t buf_ofs = (((size_t)(chx + x) +
- ((size_t)(pen_y + y) * (size_t)buf_info->dims[0])) *
+ ((size_t)(pen_y_px + y) * (size_t)buf_info->dims[0])) *
(size_t)buf_info->ch);
unsigned char *cbuf = buf_info->cbuf + buf_ofs;
@@ -542,13 +542,13 @@ static void blf_font_draw_buffer_ex(FontBLF *font,
}
}
- pen_x += g->advance_i;
+ pen_x = ft_pix_round_advance(pen_x, g->advance_x);
g_prev = g;
}
if (r_info) {
r_info->lines = 1;
- r_info->width = pen_x;
+ r_info->width = ft_pix_to_int(pen_x);
}
}
@@ -573,23 +573,24 @@ void blf_font_draw_buffer(FontBLF *font,
* \{ */
static bool blf_font_width_to_strlen_glyph_process(
- FontBLF *font, GlyphBLF *g_prev, GlyphBLF *g, int *pen_x, const int width_i)
+ FontBLF *font, GlyphBLF *g_prev, GlyphBLF *g, ft_pix *pen_x, const int width_i)
{
if (UNLIKELY(g == NULL)) {
return false; /* continue the calling loop. */
}
*pen_x += blf_kerning(font, g_prev, g);
- *pen_x += g->advance_i;
+ *pen_x = ft_pix_round_advance(*pen_x, g->advance_x);
/* When true, break the calling loop. */
- return (*pen_x >= width_i);
+ return (ft_pix_to_int(*pen_x) >= width_i);
}
size_t blf_font_width_to_strlen(
- FontBLF *font, const char *str, const size_t str_len, float width, float *r_width)
+ FontBLF *font, const char *str, const size_t str_len, int width, int *r_width)
{
GlyphBLF *g, *g_prev;
- int pen_x, width_new;
+ ft_pix pen_x;
+ ft_pix width_new;
size_t i, i_prev;
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
@@ -605,7 +606,7 @@ size_t blf_font_width_to_strlen(
}
if (r_width) {
- *r_width = (float)width_new;
+ *r_width = ft_pix_to_int(width_new);
}
blf_glyph_cache_release(font);
@@ -613,15 +614,14 @@ size_t blf_font_width_to_strlen(
}
size_t blf_font_width_to_rstrlen(
- FontBLF *font, const char *str, const size_t str_len, float width, float *r_width)
+ FontBLF *font, const char *str, const size_t str_len, int width, int *r_width)
{
GlyphBLF *g, *g_prev;
- int pen_x, width_new;
+ ft_pix pen_x, width_new;
size_t i, i_prev, i_tmp;
const char *s, *s_prev;
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
- const int width_i = (int)width;
i = BLI_strnlen(str, str_len);
s = BLI_str_find_prev_char_utf8(&str[i], str);
@@ -642,13 +642,13 @@ size_t blf_font_width_to_rstrlen(
BLI_assert(i_tmp == i);
}
- if (blf_font_width_to_strlen_glyph_process(font, g_prev, g, &pen_x, width_i)) {
+ if (blf_font_width_to_strlen_glyph_process(font, g_prev, g, &pen_x, width)) {
break;
}
}
if (r_width) {
- *r_width = (float)width_new;
+ *r_width = ft_pix_to_int(width_new);
}
blf_glyph_cache_release(font);
@@ -665,19 +665,18 @@ static void blf_font_boundbox_ex(FontBLF *font,
GlyphCacheBLF *gc,
const char *str,
const size_t str_len,
- rctf *box,
+ rcti *box,
struct ResultBLF *r_info,
- int pen_y)
+ ft_pix pen_y)
{
GlyphBLF *g, *g_prev = NULL;
- int pen_x = 0;
+ ft_pix pen_x = 0;
size_t i = 0;
- rctf gbox;
- box->xmin = 32000.0f;
- box->xmax = -32000.0f;
- box->ymin = 32000.0f;
- box->ymax = -32000.0f;
+ ft_pix box_xmin = ft_pix_from_int(32000);
+ ft_pix box_xmax = ft_pix_from_int(-32000);
+ ft_pix box_ymin = ft_pix_from_int(32000);
+ ft_pix box_ymax = ft_pix_from_int(-32000);
while ((i < str_len) && str[i]) {
g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i);
@@ -686,44 +685,50 @@ static void blf_font_boundbox_ex(FontBLF *font,
continue;
}
pen_x += blf_kerning(font, g_prev, g);
+ const ft_pix pen_x_next = ft_pix_round_advance(pen_x, g->advance_x);
- gbox.xmin = (float)pen_x;
- gbox.xmax = (float)pen_x + g->advance;
- gbox.ymin = g->box.ymin + (float)pen_y;
- gbox.ymax = g->box.ymax + (float)pen_y;
+ const ft_pix gbox_xmin = pen_x;
+ const ft_pix gbox_xmax = pen_x_next;
+ const ft_pix gbox_ymin = g->box_ymin + pen_y;
+ const ft_pix gbox_ymax = g->box_ymax + pen_y;
- if (gbox.xmin < box->xmin) {
- box->xmin = gbox.xmin;
+ if (gbox_xmin < box_xmin) {
+ box_xmin = gbox_xmin;
}
- if (gbox.ymin < box->ymin) {
- box->ymin = gbox.ymin;
+ if (gbox_ymin < box_ymin) {
+ box_ymin = gbox_ymin;
}
- if (gbox.xmax > box->xmax) {
- box->xmax = gbox.xmax;
+ if (gbox_xmax > box_xmax) {
+ box_xmax = gbox_xmax;
}
- if (gbox.ymax > box->ymax) {
- box->ymax = gbox.ymax;
+ if (gbox_ymax > box_ymax) {
+ box_ymax = gbox_ymax;
}
- pen_x += g->advance_i;
+ pen_x = pen_x_next;
g_prev = g;
}
- if (box->xmin > box->xmax) {
- box->xmin = 0.0f;
- box->ymin = 0.0f;
- box->xmax = 0.0f;
- box->ymax = 0.0f;
+ if (box_xmin > box_xmax) {
+ box_xmin = 0;
+ box_ymin = 0;
+ box_xmax = 0;
+ box_ymax = 0;
}
+ box->xmin = ft_pix_to_int_floor(box_xmin);
+ box->xmax = ft_pix_to_int_ceil(box_xmax);
+ box->ymin = ft_pix_to_int_floor(box_ymin);
+ box->ymax = ft_pix_to_int_ceil(box_ymax);
+
if (r_info) {
r_info->lines = 1;
- r_info->width = pen_x;
+ r_info->width = ft_pix_to_int(pen_x);
}
}
void blf_font_boundbox(
- FontBLF *font, const char *str, const size_t str_len, rctf *r_box, struct ResultBLF *r_info)
+ FontBLF *font, const char *str, const size_t str_len, rcti *r_box, struct ResultBLF *r_info)
{
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
blf_font_boundbox_ex(font, gc, str, str_len, r_box, r_info, 0);
@@ -738,7 +743,7 @@ void blf_font_width_and_height(FontBLF *font,
struct ResultBLF *r_info)
{
float xa, ya;
- rctf box;
+ rcti box;
if (font->flags & BLF_ASPECT) {
xa = font->aspect[0];
@@ -755,8 +760,8 @@ void blf_font_width_and_height(FontBLF *font,
else {
blf_font_boundbox(font, str, str_len, &box, r_info);
}
- *r_width = (BLI_rctf_size_x(&box) * xa);
- *r_height = (BLI_rctf_size_y(&box) * ya);
+ *r_width = ((float)BLI_rcti_size_x(&box) * xa);
+ *r_height = ((float)BLI_rcti_size_y(&box) * ya);
}
float blf_font_width(FontBLF *font,
@@ -765,7 +770,7 @@ float blf_font_width(FontBLF *font,
struct ResultBLF *r_info)
{
float xa;
- rctf box;
+ rcti box;
if (font->flags & BLF_ASPECT) {
xa = font->aspect[0];
@@ -780,7 +785,7 @@ float blf_font_width(FontBLF *font,
else {
blf_font_boundbox(font, str, str_len, &box, r_info);
}
- return BLI_rctf_size_x(&box) * xa;
+ return (float)BLI_rcti_size_x(&box) * xa;
}
float blf_font_height(FontBLF *font,
@@ -789,7 +794,7 @@ float blf_font_height(FontBLF *font,
struct ResultBLF *r_info)
{
float ya;
- rctf box;
+ rcti box;
if (font->flags & BLF_ASPECT) {
ya = font->aspect[1];
@@ -804,7 +809,7 @@ float blf_font_height(FontBLF *font,
else {
blf_font_boundbox(font, str, str_len, &box, r_info);
}
- return BLI_rctf_size_y(&box) * ya;
+ return (float)BLI_rcti_size_y(&box) * ya;
}
float blf_font_fixed_width(FontBLF *font)
@@ -822,12 +827,12 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font,
BLF_GlyphBoundsFn user_fn,
void *user_data,
struct ResultBLF *r_info,
- int pen_y)
+ ft_pix pen_y)
{
GlyphBLF *g, *g_prev = NULL;
- int pen_x = 0;
+ ft_pix pen_x = 0;
size_t i = 0, i_curr;
- rcti gbox;
+ rcti gbox_px;
if (str_len == 0) {
/* early output. */
@@ -842,15 +847,23 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font,
continue;
}
pen_x += blf_kerning(font, g_prev, g);
+ const ft_pix pen_x_next = ft_pix_round_advance(pen_x, g->advance_x);
+
+ gbox_px.xmin = ft_pix_to_int_floor(pen_x);
+ gbox_px.xmax = ft_pix_to_int_ceil(pen_x_next);
+ gbox_px.ymin = ft_pix_to_int_floor(pen_y);
+ gbox_px.ymax = gbox_px.ymin - g->dims[1];
+ const int advance_x_px = gbox_px.xmax - gbox_px.xmin;
- gbox.xmin = pen_x;
- gbox.xmax = gbox.xmin + MIN2(g->advance_i, g->dims[0]);
- gbox.ymin = pen_y;
- gbox.ymax = gbox.ymin - g->dims[1];
+ pen_x = pen_x_next;
- pen_x += g->advance_i;
+ rcti box_px;
+ box_px.xmin = ft_pix_to_int_floor(g->box_xmin);
+ box_px.xmax = ft_pix_to_int_ceil(g->box_xmax);
+ box_px.ymin = ft_pix_to_int_floor(g->box_ymin);
+ box_px.ymax = ft_pix_to_int_ceil(g->box_ymax);
- if (user_fn(str, i_curr, &gbox, g->advance_i, &g->box, g->pos, user_data) == false) {
+ if (user_fn(str, i_curr, &gbox_px, advance_x_px, &box_px, g->pos, user_data) == false) {
break;
}
@@ -859,7 +872,7 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font,
if (r_info) {
r_info->lines = 1;
- r_info->width = pen_x;
+ r_info->width = ft_pix_to_int(pen_x);
}
}
void blf_font_boundbox_foreach_glyph(FontBLF *font,
@@ -897,22 +910,26 @@ static void blf_font_wrap_apply(FontBLF *font,
GlyphCacheBLF *gc,
const char *str,
const size_t str_len,
- int pen_y,
+ ft_pix pen_y,
void *userdata),
void *userdata)
{
GlyphBLF *g, *g_prev = NULL;
- int pen_x = 0, pen_y = 0;
+ ft_pix pen_x = 0;
+ ft_pix pen_y = 0;
size_t i = 0;
int lines = 0;
- int pen_x_next = 0;
+ ft_pix pen_x_next = 0;
+
+ /* Space between lines needs to be aligned to the pixel grid (T97310). */
+ ft_pix line_height = FT_PIX_FLOOR(blf_font_height_max_ft_pix(font));
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
struct WordWrapVars {
- int wrap_width;
+ ft_pix wrap_width;
size_t start, last[2];
- } wrap = {font->wrap_width != -1 ? font->wrap_width : INT_MAX, 0, {0, 0}};
+ } wrap = {font->wrap_width != -1 ? ft_pix_from_int(font->wrap_width) : INT_MAX, 0, {0, 0}};
// printf("%s wrapping (%d, %d) `%s`:\n", __func__, str_len, strlen(str), str);
while ((i < str_len) && str[i]) {
@@ -936,7 +953,7 @@ static void blf_font_wrap_apply(FontBLF *font,
*
* This is _only_ done when we know for sure the character is ascii (newline or a space).
*/
- pen_x_next = pen_x + g->advance_i;
+ pen_x_next = ft_pix_round_advance(pen_x, g->advance_x);
if (UNLIKELY((pen_x_next >= wrap.wrap_width) && (wrap.start != wrap.last[0]))) {
do_draw = true;
}
@@ -964,7 +981,7 @@ static void blf_font_wrap_apply(FontBLF *font,
wrap.start = wrap.last[0];
i = wrap.last[1];
pen_x = 0;
- pen_y -= blf_font_height_max(font);
+ pen_y -= line_height;
g_prev = NULL;
lines += 1;
continue;
@@ -979,7 +996,7 @@ static void blf_font_wrap_apply(FontBLF *font,
if (r_info) {
r_info->lines = lines;
/* width of last line only (with wrapped lines) */
- r_info->width = pen_x_next;
+ r_info->width = ft_pix_to_int(pen_x_next);
}
blf_glyph_cache_release(font);
@@ -990,7 +1007,7 @@ static void blf_font_draw__wrap_cb(FontBLF *font,
GlyphCacheBLF *gc,
const char *str,
const size_t str_len,
- int pen_y,
+ ft_pix pen_y,
void *UNUSED(userdata))
{
blf_font_draw_ex(font, gc, str, str_len, NULL, pen_y);
@@ -1008,22 +1025,22 @@ static void blf_font_boundbox_wrap_cb(FontBLF *font,
GlyphCacheBLF *gc,
const char *str,
const size_t str_len,
- int pen_y,
+ ft_pix pen_y,
void *userdata)
{
- rctf *box = userdata;
- rctf box_single;
+ rcti *box = userdata;
+ rcti box_single;
blf_font_boundbox_ex(font, gc, str, str_len, &box_single, NULL, pen_y);
- BLI_rctf_union(box, &box_single);
+ BLI_rcti_union(box, &box_single);
}
void blf_font_boundbox__wrap(
- FontBLF *font, const char *str, const size_t str_len, rctf *box, struct ResultBLF *r_info)
+ FontBLF *font, const char *str, const size_t str_len, rcti *box, struct ResultBLF *r_info)
{
- box->xmin = 32000.0f;
- box->xmax = -32000.0f;
- box->ymin = 32000.0f;
- box->ymax = -32000.0f;
+ box->xmin = 32000;
+ box->xmax = -32000;
+ box->ymin = 32000;
+ box->ymax = -32000;
blf_font_wrap_apply(font, str, str_len, r_info, blf_font_boundbox_wrap_cb, box);
}
@@ -1033,7 +1050,7 @@ static void blf_font_draw_buffer__wrap_cb(FontBLF *font,
GlyphCacheBLF *gc,
const char *str,
const size_t str_len,
- int pen_y,
+ ft_pix pen_y,
void *UNUSED(userdata))
{
blf_font_draw_buffer_ex(font, gc, str, str_len, NULL, pen_y);
@@ -1084,44 +1101,56 @@ int blf_font_count_missing_chars(FontBLF *font,
/** \name Font Query: Attributes
* \{ */
-int blf_font_height_max(FontBLF *font)
+static ft_pix blf_font_height_max_ft_pix(FontBLF *font)
{
- int height_max;
- if (FT_IS_SCALABLE(font->face)) {
- height_max = (int)((float)(font->face->ascender - font->face->descender) *
- (((float)font->face->size->metrics.y_ppem) /
- ((float)font->face->units_per_EM)));
+ ft_pix height_max;
+ FT_Face face = font->face;
+ if (FT_IS_SCALABLE(face)) {
+ height_max = ft_pix_from_int((int)(face->ascender - face->descender) *
+ (int)face->size->metrics.y_ppem) /
+ (ft_pix)face->units_per_EM;
}
else {
- height_max = (int)(((float)font->face->size->metrics.height) / 64.0f);
+ height_max = (ft_pix)face->size->metrics.height;
}
/* can happen with size 1 fonts */
- return MAX2(height_max, 1);
+ return MAX2(height_max, ft_pix_from_int(1));
}
-int blf_font_width_max(FontBLF *font)
+int blf_font_height_max(FontBLF *font)
+{
+ return ft_pix_to_int(blf_font_height_max_ft_pix(font));
+}
+
+static ft_pix blf_font_width_max_ft_pix(FontBLF *font)
{
- int width_max;
- if (FT_IS_SCALABLE(font->face)) {
- width_max = (int)((float)(font->face->bbox.xMax - font->face->bbox.xMin) *
- (((float)font->face->size->metrics.x_ppem) /
- ((float)font->face->units_per_EM)));
+ ft_pix width_max;
+ const FT_Face face = font->face;
+ if (FT_IS_SCALABLE(face)) {
+ width_max = ft_pix_from_int((int)(face->bbox.xMax - face->bbox.xMin) *
+ (int)face->size->metrics.x_ppem) /
+ (ft_pix)face->units_per_EM;
}
else {
- width_max = (int)(((float)font->face->size->metrics.max_advance) / 64.0f);
+ width_max = (ft_pix)face->size->metrics.max_advance;
}
/* can happen with size 1 fonts */
- return MAX2(width_max, 1);
+ return MAX2(width_max, ft_pix_from_int(1));
+}
+
+int blf_font_width_max(FontBLF *font)
+{
+ return ft_pix_to_int(blf_font_width_max_ft_pix(font));
}
-float blf_font_descender(FontBLF *font)
+int blf_font_descender(FontBLF *font)
{
- return ((float)font->face->size->metrics.descender) / 64.0f;
+ return ft_pix_to_int((ft_pix)font->face->size->metrics.descender);
}
-float blf_font_ascender(FontBLF *font)
+int blf_font_ascender(FontBLF *font)
{
- return ((float)font->face->size->metrics.ascender) / 64.0f;
+ return ft_pix_to_int((ft_pix)font->face->size->metrics.ascender);
}
char *blf_display_name(FontBLF *font)
@@ -1170,8 +1199,8 @@ static void blf_font_fill(FontBLF *font)
font->aspect[0] = 1.0f;
font->aspect[1] = 1.0f;
font->aspect[2] = 1.0f;
- font->pos[0] = 0.0f;
- font->pos[1] = 0.0f;
+ font->pos[0] = 0;
+ font->pos[1] = 0;
font->angle = 0.0f;
for (int i = 0; i < 16; i++) {
@@ -1184,10 +1213,10 @@ static void blf_font_fill(FontBLF *font)
font->color[2] = 0;
font->color[3] = 255;
- font->clip_rec.xmin = 0.0f;
- font->clip_rec.xmax = 0.0f;
- font->clip_rec.ymin = 0.0f;
- font->clip_rec.ymax = 0.0f;
+ font->clip_rec.xmin = 0;
+ font->clip_rec.xmax = 0;
+ font->clip_rec.ymin = 0;
+ font->clip_rec.ymax = 0;
font->flags = 0;
font->dpi = 0;
font->size = 0;
diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c
index 28531c5afc1..2694b179a11 100644
--- a/source/blender/blenfont/intern/blf_glyph.c
+++ b/source/blender/blenfont/intern/blf_glyph.c
@@ -183,8 +183,7 @@ static GlyphBLF *blf_glyph_cache_add_glyph(
GlyphBLF *g = (GlyphBLF *)MEM_callocN(sizeof(GlyphBLF), "blf_glyph_get");
g->c = charcode;
g->idx = glyph_index;
- g->advance = ((float)glyph->advance.x) / 64.0f;
- g->advance_i = (int)g->advance;
+ g->advance_x = (ft_pix)glyph->advance.x;
g->pos[0] = glyph->bitmap_left;
g->pos[1] = glyph->bitmap_top;
g->dims[0] = (int)glyph->bitmap.width;
@@ -193,10 +192,14 @@ static GlyphBLF *blf_glyph_cache_add_glyph(
FT_BBox bbox;
FT_Outline_Get_CBox(&(glyph->outline), &bbox);
- g->box.xmin = ((float)bbox.xMin) / 64.0f;
- g->box.xmax = ((float)bbox.xMax) / 64.0f;
- g->box.ymin = ((float)bbox.yMin) / 64.0f;
- g->box.ymax = ((float)bbox.yMax) / 64.0f;
+ g->box_xmin = (ft_pix)bbox.xMin;
+ g->box_xmax = (ft_pix)bbox.xMax;
+ g->box_ymin = (ft_pix)bbox.yMin;
+ g->box_ymax = (ft_pix)bbox.yMax;
+
+ /* Used to improve advance when hinting is enabled. */
+ g->lsb_delta = (ft_pix)glyph->lsb_delta;
+ g->rsb_delta = (ft_pix)glyph->rsb_delta;
const int buffer_size = (int)(glyph->bitmap.width * glyph->bitmap.rows);
if (buffer_size != 0) {
@@ -488,28 +491,29 @@ void blf_glyph_free(GlyphBLF *g)
/** \name Glyph Bounds Calculation
* \{ */
-static void blf_glyph_calc_rect(rctf *rect, GlyphBLF *g, float x, float y)
+static void blf_glyph_calc_rect(rcti *rect, GlyphBLF *g, const int x, const int y)
{
- rect->xmin = floorf(x + (float)g->pos[0]);
- rect->xmax = rect->xmin + (float)g->dims[0];
- rect->ymin = floorf(y + (float)g->pos[1]);
- rect->ymax = rect->ymin - (float)g->dims[1];
+ rect->xmin = x + g->pos[0];
+ rect->xmax = rect->xmin + g->dims[0];
+ rect->ymin = y + g->pos[1];
+ rect->ymax = rect->ymin - g->dims[1];
}
-static void blf_glyph_calc_rect_test(rctf *rect, GlyphBLF *g, float x, float y)
+static void blf_glyph_calc_rect_test(rcti *rect, GlyphBLF *g, const int x, const int y)
{
/* Intentionally check with `g->advance`, because this is the
* width used by BLF_width. This allows that the text slightly
* overlaps the clipping border to achieve better alignment. */
- rect->xmin = floorf(x);
- rect->xmax = rect->xmin + MIN2(g->advance, (float)g->dims[0]);
- rect->ymin = floorf(y);
- rect->ymax = rect->ymin - (float)g->dims[1];
+ rect->xmin = x;
+ rect->xmax = rect->xmin + MIN2(ft_pix_to_int(g->advance_x), g->dims[0]);
+ rect->ymin = y;
+ rect->ymax = rect->ymin - g->dims[1];
}
-static void blf_glyph_calc_rect_shadow(rctf *rect, GlyphBLF *g, float x, float y, FontBLF *font)
+static void blf_glyph_calc_rect_shadow(
+ rcti *rect, GlyphBLF *g, const int x, const int y, FontBLF *font)
{
- blf_glyph_calc_rect(rect, g, x + (float)font->shadow_x, y + (float)font->shadow_y);
+ blf_glyph_calc_rect(rect, g, x + font->shadow_x, y + font->shadow_y);
}
/** \} */
@@ -521,18 +525,18 @@ static void blf_glyph_calc_rect_shadow(rctf *rect, GlyphBLF *g, float x, float y
static void blf_texture_draw(const unsigned char color[4],
const int glyph_size[2],
const int offset,
- float x1,
- float y1,
- float x2,
- float y2)
+ const int x1,
+ const int y1,
+ const int x2,
+ const int y2)
{
/* Only one vertex per glyph, geometry shader expand it into a quad. */
/* TODO: Get rid of Geom Shader because it's not optimal AT ALL for the GPU. */
copy_v4_fl4(GPU_vertbuf_raw_step(&g_batch.pos_step),
- x1 + g_batch.ofs[0],
- y1 + g_batch.ofs[1],
- x2 + g_batch.ofs[0],
- y2 + g_batch.ofs[1]);
+ (float)(x1 + g_batch.ofs[0]),
+ (float)(y1 + g_batch.ofs[1]),
+ (float)(x2 + g_batch.ofs[0]),
+ (float)(y2 + g_batch.ofs[1]));
copy_v4_v4_uchar(GPU_vertbuf_raw_step(&g_batch.col_step), color);
copy_v2_v2_int(GPU_vertbuf_raw_step(&g_batch.glyph_size_step), glyph_size);
*((int *)GPU_vertbuf_raw_step(&g_batch.offset_step)) = offset;
@@ -547,10 +551,10 @@ static void blf_texture_draw(const unsigned char color[4],
static void blf_texture5_draw(const unsigned char color_in[4],
const int glyph_size[2],
const int offset,
- float x1,
- float y1,
- float x2,
- float y2)
+ const int x1,
+ const int y1,
+ const int x2,
+ const int y2)
{
int glyph_size_flag[2];
/* flag the x and y component signs for 5x5 blurring */
@@ -563,10 +567,10 @@ static void blf_texture5_draw(const unsigned char color_in[4],
static void blf_texture3_draw(const unsigned char color_in[4],
const int glyph_size[2],
const int offset,
- float x1,
- float y1,
- float x2,
- float y2)
+ const int x1,
+ const int y1,
+ const int x2,
+ const int y2)
{
int glyph_size_flag[2];
/* flag the x component sign for 3x3 blurring */
@@ -576,7 +580,7 @@ static void blf_texture3_draw(const unsigned char color_in[4],
blf_texture_draw(color_in, glyph_size_flag, offset, x1, y1, x2, y2);
}
-void blf_glyph_draw(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, float y)
+void blf_glyph_draw(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, const int x, const int y)
{
if ((!g->dims[0]) || (!g->dims[1])) {
return;
@@ -615,11 +619,11 @@ void blf_glyph_draw(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, floa
}
if (font->flags & BLF_CLIPPING) {
- rctf rect_test;
+ rcti rect_test;
blf_glyph_calc_rect_test(&rect_test, g, x, y);
- BLI_rctf_translate(&rect_test, font->pos[0], font->pos[1]);
+ BLI_rcti_translate(&rect_test, font->pos[0], font->pos[1]);
- if (!BLI_rctf_inside_rctf(&font->clip_rec, &rect_test)) {
+ if (!BLI_rcti_inside_rcti(&font->clip_rec, &rect_test)) {
return;
}
}
@@ -630,7 +634,7 @@ void blf_glyph_draw(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, floa
}
if (font->flags & BLF_SHADOW) {
- rctf rect_ofs;
+ rcti rect_ofs;
blf_glyph_calc_rect_shadow(&rect_ofs, g, x, y, font);
if (font->shadow == 0) {
@@ -662,7 +666,7 @@ void blf_glyph_draw(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, floa
}
}
- rctf rect;
+ rcti rect;
blf_glyph_calc_rect(&rect, g, x, y);
#if BLF_BLUR_ENABLE
diff --git a/source/blender/blenfont/intern/blf_internal.h b/source/blender/blenfont/intern/blf_internal.h
index 1018dc09ff8..7754f960043 100644
--- a/source/blender/blenfont/intern/blf_internal.h
+++ b/source/blender/blenfont/intern/blf_internal.h
@@ -67,18 +67,18 @@ void blf_font_draw_buffer__wrap(struct FontBLF *font,
size_t str_len,
struct ResultBLF *r_info);
size_t blf_font_width_to_strlen(
- struct FontBLF *font, const char *str, size_t str_len, float width, float *r_width);
+ struct FontBLF *font, const char *str, size_t str_len, int width, int *r_width);
size_t blf_font_width_to_rstrlen(
- struct FontBLF *font, const char *str, size_t str_len, float width, float *r_width);
+ struct FontBLF *font, const char *str, size_t str_len, int width, int *r_width);
void blf_font_boundbox(struct FontBLF *font,
const char *str,
size_t str_len,
- struct rctf *r_box,
+ struct rcti *r_box,
struct ResultBLF *r_info);
void blf_font_boundbox__wrap(struct FontBLF *font,
const char *str,
size_t str_len,
- struct rctf *r_box,
+ struct rcti *r_box,
struct ResultBLF *r_info);
void blf_font_width_and_height(struct FontBLF *font,
const char *str,
@@ -97,8 +97,8 @@ float blf_font_height(struct FontBLF *font,
float blf_font_fixed_width(struct FontBLF *font);
int blf_font_height_max(struct FontBLF *font);
int blf_font_width_max(struct FontBLF *font);
-float blf_font_descender(struct FontBLF *font);
-float blf_font_ascender(struct FontBLF *font);
+int blf_font_descender(struct FontBLF *font);
+int blf_font_ascender(struct FontBLF *font);
char *blf_display_name(struct FontBLF *font);
@@ -109,7 +109,7 @@ void blf_font_boundbox_foreach_glyph(struct FontBLF *font,
size_t str_step_ofs,
const struct rcti *glyph_step_bounds,
int glyph_advance_x,
- const struct rctf *glyph_bounds,
+ const struct rcti *glyph_bounds,
const int glyph_bearing[2],
void *user_data),
void *user_data,
@@ -133,7 +133,7 @@ struct GlyphBLF *blf_glyph_ensure(struct FontBLF *font, struct GlyphCacheBLF *gc
void blf_glyph_free(struct GlyphBLF *g);
void blf_glyph_draw(
- struct FontBLF *font, struct GlyphCacheBLF *gc, struct GlyphBLF *g, float x, float y);
+ struct FontBLF *font, struct GlyphCacheBLF *gc, struct GlyphBLF *g, int x, int y);
#ifdef WIN32
/* blf_font_win32_compat.c */
diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h
index 23dc2fda38c..62bce36dda0 100644
--- a/source/blender/blenfont/intern/blf_internal_types.h
+++ b/source/blender/blenfont/intern/blf_internal_types.h
@@ -10,6 +10,78 @@
#include "GPU_texture.h"
#include "GPU_vertex_buffer.h"
+/* -------------------------------------------------------------------- */
+/** \name Sub-Pixel Offset & Utilities
+ *
+ * Free-type uses fixed point precision for sub-pixel offsets.
+ * Utility functions here avoid exposing the details in the BLF API.
+ * \{ */
+
+/**
+ * This is an internal type that represents sub-pixel positioning,
+ * users of this type are to use `ft_pix_*` functions to keep scaling/rounding in one place.
+ */
+typedef int32_t ft_pix;
+
+/* Macros copied from `include/freetype/internal/ftobjs.h`. */
+
+/* FIXME(@campbellbarton): Follow rounding from Blender 3.1x and older.
+ * This is what users will expect and changing this creates wider spaced text.
+ * Use this macro to communicate that rounding should be used, using floor is to avoid
+ * user visible changes, which can be reviewed and handled separately. */
+#define USE_LEGACY_SPACING
+
+#define FT_PIX_FLOOR(x) ((x) & ~63)
+#define FT_PIX_ROUND(x) FT_PIX_FLOOR((x) + 32)
+#define FT_PIX_CEIL(x) ((x) + 63)
+
+#ifdef USE_LEGACY_SPACING
+# define FT_PIX_DEFAULT_ROUNDING(x) FT_PIX_FLOOR(x)
+#else
+# define FT_PIX_DEFAULT_ROUNDING(x) FT_PIX_ROUND(x)
+#endif
+
+BLI_INLINE int ft_pix_to_int(ft_pix v)
+{
+#ifdef USE_LEGACY_SPACING
+ return (int)(v >> 6);
+#else
+ return (int)(FT_PIX_DEFAULT_ROUNDING(v) >> 6);
+#endif
+}
+
+BLI_INLINE int ft_pix_to_int_floor(ft_pix v)
+{
+ return (int)(v >> 6); /* No need for explicit floor as the bits are removed when shifting. */
+}
+
+BLI_INLINE int ft_pix_to_int_ceil(ft_pix v)
+{
+ return (int)(FT_PIX_CEIL(v) >> 6);
+}
+
+BLI_INLINE ft_pix ft_pix_from_int(int v)
+{
+ return v * 64;
+}
+
+BLI_INLINE ft_pix ft_pix_from_float(float v)
+{
+ return lroundf(v * 64.0f);
+}
+
+BLI_INLINE ft_pix ft_pix_round_advance(ft_pix v, ft_pix step)
+{
+ /* See #USE_LEGACY_SPACING, rounding logic could change here. */
+ return FT_PIX_DEFAULT_ROUNDING(v) + FT_PIX_DEFAULT_ROUNDING(step);
+}
+
+#undef FT_PIX_ROUND
+#undef FT_PIX_CEIL
+#undef FT_PIX_DEFAULT_ROUNDING
+
+/** \} */
+
#define BLF_BATCH_DRAW_LEN_MAX 2048 /* in glyph */
/* Number of characters in GlyphCacheBLF.glyph_ascii_table. */
@@ -28,7 +100,7 @@ typedef struct BatchBLF {
struct GPUVertBufRaw pos_step, col_step, offset_step, glyph_size_step;
unsigned int pos_loc, col_loc, offset_loc, glyph_size_loc;
unsigned int glyph_len;
- float ofs[2]; /* copy of font->pos */
+ int ofs[2]; /* copy of font->pos */
float mat[4][4]; /* previous call modelmatrix. */
bool enabled, active, simple_shader;
struct GlyphCacheBLF *glyph_cache;
@@ -86,12 +158,16 @@ typedef struct GlyphBLF {
FT_UInt idx;
/* glyph box. */
- rctf box;
+ ft_pix box_xmin;
+ ft_pix box_xmax;
+ ft_pix box_ymin;
+ ft_pix box_ymax;
+
+ ft_pix advance_x;
- /* advance size. */
- float advance;
- /* avoid conversion to int while drawing */
- int advance_i;
+ /* The difference in bearings when hinting is active, zero otherwise. */
+ ft_pix lsb_delta;
+ ft_pix rsb_delta;
/* position inside the texture where this glyph is store. */
int offset;
@@ -154,7 +230,7 @@ typedef struct FontBLF {
float aspect[3];
/* initial position for draw the text. */
- float pos[3];
+ int pos[3];
/* angle in radians. */
float angle;
@@ -183,7 +259,7 @@ typedef struct FontBLF {
float m[16];
/* clipping rectangle. */
- rctf clip_rec;
+ rcti clip_rec;
/* the width to wrap the text, see BLF_WORD_WRAP */
int wrap_width;
diff --git a/source/blender/blenfont/intern/blf_thumbs.c b/source/blender/blenfont/intern/blf_thumbs.c
index 0e265fb7553..a75072f854f 100644
--- a/source/blender/blenfont/intern/blf_thumbs.c
+++ b/source/blender/blenfont/intern/blf_thumbs.c
@@ -64,7 +64,7 @@ void BLF_thumb_preview(const char *filepath,
/* Always create the image with a white font,
* the caller can theme how it likes */
memcpy(font->buf_info.col_init, font_color, sizeof(font->buf_info.col_init));
- font->pos[1] = (float)h;
+ font->pos[1] = h;
font_size_curr = font_size;
@@ -84,7 +84,7 @@ void BLF_thumb_preview(const char *filepath,
font_size_curr -= (font_size_curr / font_shrink);
font_shrink += 1;
- font->pos[1] -= blf_font_ascender(font) * 1.1f;
+ font->pos[1] -= (int)((float)blf_font_ascender(font) * 1.1f);
/* We fallback to default english strings in case not enough chars are available in current
* font for given translated string (useful in non-latin i18n context, like Chinese,
diff --git a/source/blender/blenkernel/BKE_animsys.h b/source/blender/blenkernel/BKE_animsys.h
index ded64b68f79..91ecfe09f38 100644
--- a/source/blender/blenkernel/BKE_animsys.h
+++ b/source/blender/blenkernel/BKE_animsys.h
@@ -7,6 +7,7 @@
* \ingroup bke
*/
+#include "BLI_bitmap.h"
#include "BLI_sys_types.h" /* for bool */
#ifdef __cplusplus
@@ -258,16 +259,20 @@ struct NlaKeyframingContext *BKE_animsys_get_nla_keyframing_context(
* \param count: Number of values in the array.
* \param index: Index of the element about to be updated, or -1.
* \param[out] r_force_all: Set to true if all channels must be inserted. May be NULL.
- * \return False if correction fails due to a division by zero,
- * or null r_force_all when all channels are required.
+ * \param[out] r_successful_remaps: Bits will be enabled for indices that are both intended to be
+ * remapped and succeeded remapping. With both, it allows caller to check successfully remapped
+ * indices without having to explicitly check whether the index was intended to be remapped.
*/
-bool BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context,
+void BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context,
struct PointerRNA *prop_ptr,
struct PropertyRNA *prop,
float *values,
int count,
int index,
- bool *r_force_all);
+ const struct AnimationEvalContext *anim_eval_context,
+ bool *r_force_all,
+ BLI_bitmap *r_successful_remaps);
+
/**
* Free all cached contexts from the list.
*/
diff --git a/source/blender/blenkernel/BKE_attribute.h b/source/blender/blenkernel/BKE_attribute.h
index 62469a28eb1..f3a29736bc8 100644
--- a/source/blender/blenkernel/BKE_attribute.h
+++ b/source/blender/blenkernel/BKE_attribute.h
@@ -65,7 +65,7 @@ struct CustomDataLayer *BKE_id_attribute_find(const struct ID *id,
int type,
AttributeDomain domain);
-AttributeDomain BKE_id_attribute_domain(struct ID *id, const struct CustomDataLayer *layer);
+AttributeDomain BKE_id_attribute_domain(const struct ID *id, const struct CustomDataLayer *layer);
int BKE_id_attribute_data_length(struct ID *id, struct CustomDataLayer *layer);
bool BKE_id_attribute_required(struct ID *id, struct CustomDataLayer *layer);
bool BKE_id_attribute_rename(struct ID *id,
diff --git a/source/blender/blenkernel/BKE_curve_to_mesh.hh b/source/blender/blenkernel/BKE_curve_to_mesh.hh
index a49cb6eb7f5..6e657542e0f 100644
--- a/source/blender/blenkernel/BKE_curve_to_mesh.hh
+++ b/source/blender/blenkernel/BKE_curve_to_mesh.hh
@@ -2,7 +2,7 @@
#pragma once
-struct CurveEval;
+struct CurvesGeometry;
struct Mesh;
/** \file
@@ -21,11 +21,13 @@ namespace blender::bke {
* changed anyway in a way that affects the normals. So currently this code uses the safer /
* simpler solution of deferring normal calculation to the rest of Blender.
*/
-Mesh *curve_to_mesh_sweep(const CurveEval &curve, const CurveEval &profile, bool fill_caps);
+Mesh *curve_to_mesh_sweep(const CurvesGeometry &main,
+ const CurvesGeometry &profile,
+ bool fill_caps);
/**
* Create a loose-edge mesh based on the evaluated path of the curve's splines.
* Transfer curve attributes to the mesh.
*/
-Mesh *curve_to_wire_mesh(const CurveEval &curve);
+Mesh *curve_to_wire_mesh(const CurvesGeometry &curve);
} // namespace blender::bke
diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh
index 9fd023edcf2..282e2a40bd0 100644
--- a/source/blender/blenkernel/BKE_curves.hh
+++ b/source/blender/blenkernel/BKE_curves.hh
@@ -76,6 +76,11 @@ class CurvesGeometryRuntime {
mutable Vector<float3> evaluated_position_cache;
mutable std::mutex position_cache_mutex;
mutable bool position_cache_dirty = true;
+ /**
+ * The evaluated positions result, using a separate span in case all curves are poly curves,
+ * in which case a separate array of evaluated positions is unnecessary.
+ */
+ mutable Span<float3> evaluated_positions_span;
/**
* Cache of lengths along each evaluated curve for for each evaluated point. If a curve is
@@ -178,6 +183,13 @@ class CurvesGeometry : public ::CurvesGeometry {
MutableSpan<int> resolution_for_write();
/**
+ * The angle used to rotate evaluated normals around the tangents after their calculation.
+ * Call #tag_normals_changed after changes.
+ */
+ VArray<float> tilt() const;
+ MutableSpan<float> tilt_for_write();
+
+ /**
* Which method to use for calculating the normals of evaluated points (#NormalMode).
* Call #tag_normals_changed after changes.
*/
@@ -316,6 +328,10 @@ class CurvesGeometry : public ::CurvesGeometry {
* calculated. That can be ensured with #ensure_evaluated_offsets.
*/
void interpolate_to_evaluated(int curve_index, GSpan src, GMutableSpan dst) const;
+ /**
+ * Evaluate generic data for curve control points to the standard evaluated points of the curves.
+ */
+ void interpolate_to_evaluated(GSpan src, GMutableSpan dst) const;
private:
/**
@@ -377,6 +393,7 @@ namespace curves {
*/
inline int curve_segment_size(const int points_num, const bool cyclic)
{
+ BLI_assert(points_num > 0);
return cyclic ? points_num : points_num - 1;
}
@@ -433,6 +450,13 @@ bool segment_is_vector(Span<int8_t> handle_types_left,
bool last_cylic_segment_is_vector(Span<int8_t> handle_types_left, Span<int8_t> handle_types_right);
/**
+ * Return true if the handle types at the index are free (#BEZIER_HANDLE_FREE) or vector
+ * (#BEZIER_HANDLE_VECTOR). In these cases, directional continuitity from the previous and next
+ * evaluated segments is assumed not to be desired.
+ */
+bool point_is_sharp(Span<int8_t> handle_types_left, Span<int8_t> handle_types_right, int index);
+
+/**
* Calculate offsets into the curve's evaluated points for each control point. While most control
* point edges generate the number of edges specified by the resolution, vector segments only
* generate one edge.
@@ -681,8 +705,7 @@ inline IndexRange CurvesGeometry::lengths_range_for_curve(const int curve_index,
BLI_assert(cyclic == this->cyclic()[curve_index]);
const IndexRange points = this->evaluated_points_for_curve(curve_index);
const int start = points.start() + curve_index;
- const int size = curves::curve_segment_size(points.size(), cyclic);
- return {start, size};
+ return {start, points.is_empty() ? 0 : curves::curve_segment_size(points.size(), cyclic)};
}
inline Span<float> CurvesGeometry::evaluated_lengths_for_curve(const int curve_index,
@@ -703,4 +726,22 @@ inline float CurvesGeometry::evaluated_length_total_for_curve(const int curve_in
/** \} */
+/* -------------------------------------------------------------------- */
+/** \name Bezier Inline Methods
+ * \{ */
+
+namespace curves::bezier {
+
+inline bool point_is_sharp(const Span<int8_t> handle_types_left,
+ const Span<int8_t> handle_types_right,
+ const int index)
+{
+ return ELEM(handle_types_left[index], BEZIER_HANDLE_VECTOR, BEZIER_HANDLE_FREE) ||
+ ELEM(handle_types_right[index], BEZIER_HANDLE_VECTOR, BEZIER_HANDLE_FREE);
+}
+
+} // namespace curves::bezier
+
+/** \} */
+
} // namespace blender::bke
diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h
index d82e7460071..2070584a8a0 100644
--- a/source/blender/blenkernel/BKE_global.h
+++ b/source/blender/blenkernel/BKE_global.h
@@ -195,12 +195,13 @@ enum {
G_DEBUG_XR = (1 << 19), /* XR/OpenXR messages */
G_DEBUG_XR_TIME = (1 << 20), /* XR/OpenXR timing messages */
- G_DEBUG_GHOST = (1 << 21), /* Debug GHOST module. */
+ G_DEBUG_GHOST = (1 << 21), /* Debug GHOST module. */
+ G_DEBUG_WINTAB = (1 << 22), /* Debug Wintab. */
};
#define G_DEBUG_ALL \
(G_DEBUG | G_DEBUG_FFMPEG | G_DEBUG_PYTHON | G_DEBUG_EVENTS | G_DEBUG_WM | G_DEBUG_JOBS | \
- G_DEBUG_FREESTYLE | G_DEBUG_DEPSGRAPH | G_DEBUG_IO | G_DEBUG_GHOST)
+ G_DEBUG_FREESTYLE | G_DEBUG_DEPSGRAPH | G_DEBUG_IO | G_DEBUG_GHOST | G_DEBUG_WINTAB)
/** #Global.fileflags */
enum {
diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h
index fba2512fa49..fff3b2a8f89 100644
--- a/source/blender/blenkernel/BKE_image.h
+++ b/source/blender/blenkernel/BKE_image.h
@@ -329,7 +329,7 @@ void BKE_image_merge(struct Main *bmain, struct Image *dest, struct Image *sourc
bool BKE_image_scale(struct Image *image, int width, int height);
/**
- * Check if texture has alpha (planes == 32 || planes == 16).
+ * Check if texture has alpha `planes == 32 || planes == 16`.
*/
bool BKE_image_has_alpha(struct Image *image);
@@ -350,13 +350,13 @@ void BKE_image_get_tile_label(struct Image *ima,
* Checks whether the given filepath refers to a UDIM tiled texture.
* If yes, the range from the lowest to the highest tile is returned.
*
- * `filepath` may be modified to ensure a UDIM token is present.
- * `tiles` may be filled even if the result ultimately is false!
+ * \param filepath: may be modified to ensure a UDIM token is present.
+ * \param tiles: may be filled even if the result ultimately is false!
*/
bool BKE_image_get_tile_info(char *filepath,
struct ListBase *tiles,
- int *tile_start,
- int *tile_range);
+ int *r_tile_start,
+ int *r_tile_range);
struct ImageTile *BKE_image_add_tile(struct Image *ima, int tile_number, const char *label);
bool BKE_image_remove_tile(struct Image *ima, struct ImageTile *tile);
@@ -385,7 +385,7 @@ typedef enum {
void BKE_image_ensure_tile_token(char *filename);
/**
- * When provided with an absolute virtual filepath, check to see if at least
+ * When provided with an absolute virtual `filepath`, check to see if at least
* one concrete file exists.
* Note: This function requires directory traversal and may be inefficient in time-critical,
* or iterative, code paths.
@@ -394,8 +394,8 @@ bool BKE_image_tile_filepath_exists(const char *filepath);
/**
* Retrieves the UDIM token format and returns the pattern from the provided `filepath`.
- * The returned pattern is typically passed to either `BKE_image_get_tile_number_from_filepath` or
- * `BKE_image_set_filepath_from_tile_number`.
+ * The returned pattern is typically passed to either #BKE_image_get_tile_number_from_filepath or
+ * #BKE_image_set_filepath_from_tile_number.
*/
char *BKE_image_get_tile_strformat(const char *filepath, eUDIM_TILE_FORMAT *r_tile_format);
bool BKE_image_get_tile_number_from_filepath(const char *filepath,
diff --git a/source/blender/blenkernel/BKE_layer.h b/source/blender/blenkernel/BKE_layer.h
index cad6f6d6645..3e4f2fe154e 100644
--- a/source/blender/blenkernel/BKE_layer.h
+++ b/source/blender/blenkernel/BKE_layer.h
@@ -594,9 +594,9 @@ void BKE_view_layer_rename_lightgroup(ViewLayer *view_layer,
ViewLayerLightgroup *lightgroup,
const char *name);
-void BKE_lightgroup_membership_get(struct LightgroupMembership *lgm, char *value);
+void BKE_lightgroup_membership_get(struct LightgroupMembership *lgm, char *name);
int BKE_lightgroup_membership_length(struct LightgroupMembership *lgm);
-void BKE_lightgroup_membership_set(struct LightgroupMembership **lgm, const char *value);
+void BKE_lightgroup_membership_set(struct LightgroupMembership **lgm, const char *name);
#ifdef __cplusplus
}
diff --git a/source/blender/blenkernel/BKE_mesh_tangent.h b/source/blender/blenkernel/BKE_mesh_tangent.h
index 5d9f850dd24..27061a5766e 100644
--- a/source/blender/blenkernel/BKE_mesh_tangent.h
+++ b/source/blender/blenkernel/BKE_mesh_tangent.h
@@ -28,7 +28,7 @@ void BKE_mesh_calc_loop_tangent_single_ex(const struct MVert *mverts,
int numPolys,
struct ReportList *reports);
/**
- * Wrapper around BKE_mesh_calc_loop_tangent_single_ex, which takes care of most boiling code.
+ * Wrapper around BKE_mesh_calc_loop_tangent_single_ex, which takes care of most boilerplate code.
* \note
* - There must be a valid loop's CD_NORMALS available.
* - The mesh should be made of only tris and quads!
diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h
index eaeb6e6a2e4..faf878dfc2a 100644
--- a/source/blender/blenkernel/BKE_object.h
+++ b/source/blender/blenkernel/BKE_object.h
@@ -270,6 +270,17 @@ void BKE_object_apply_mat4(struct Object *ob,
const float mat[4][4],
bool use_compat,
bool use_parent);
+
+/**
+ * Use parent's world location and rotation as the child's origin. The parent inverse will
+ * become identity when the parent has no shearing. Otherwise, it is non-identity and contains the
+ * object's local matrix data that cannot be decomposed into location, rotation and scale.
+ *
+ * Assumes the object's world matrix has no shear.
+ * Assumes parent exists.
+ */
+void BKE_object_apply_parent_inverse(struct Object *ob);
+
void BKE_object_matrix_local_get(struct Object *ob, float r_mat[4][4]);
bool BKE_object_pose_context_check(const struct Object *ob);
diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h
index 1b296277b8f..4ae37095411 100644
--- a/source/blender/blenkernel/BKE_paint.h
+++ b/source/blender/blenkernel/BKE_paint.h
@@ -30,7 +30,9 @@ struct EdgeSet;
struct EnumPropertyItem;
struct GHash;
struct GridPaintMask;
+struct Image;
struct ImagePool;
+struct ImageUser;
struct ListBase;
struct MLoop;
struct MLoopTri;
@@ -42,6 +44,7 @@ struct Object;
struct PBVH;
struct Paint;
struct PaintCurve;
+struct PaintModeSettings;
struct Palette;
struct PaletteColor;
struct Scene;
@@ -649,6 +652,11 @@ typedef struct SculptSession {
*/
bool sticky_shading_color;
+ /**
+ * Last used painting canvas key.
+ */
+ char *last_paint_canvas_key;
+
} SculptSession;
void BKE_sculptsession_free(struct Object *ob);
@@ -725,6 +733,20 @@ enum {
SCULPT_MASK_LAYER_CALC_LOOP = (1 << 1),
};
+/* paint_canvas.cc */
+/**
+ * Create a key that can be used to compare with previous ones to identify changes.
+ * The resulting 'string' is owned by the caller.
+ */
+char *BKE_paint_canvas_key_get(struct PaintModeSettings *settings, struct Object *ob);
+
+bool BKE_paint_canvas_image_get(struct PaintModeSettings *settings,
+ struct Object *ob,
+ struct Image **r_image,
+ struct ImageUser **r_image_user);
+int BKE_paint_canvas_uvmap_layer_index_get(const struct PaintModeSettings *settings,
+ struct Object *ob);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h
index 775847f27f8..bb918fcfdcb 100644
--- a/source/blender/blenkernel/BKE_pbvh.h
+++ b/source/blender/blenkernel/BKE_pbvh.h
@@ -31,10 +31,13 @@ struct MLoopTri;
struct MPoly;
struct MVert;
struct Mesh;
+struct MeshElemMap;
struct PBVH;
struct PBVHNode;
struct SubdivCCG;
struct TaskParallelSettings;
+struct Image;
+struct ImageUser;
struct MeshElemMap;
typedef struct PBVH PBVH;
@@ -48,6 +51,15 @@ typedef struct {
float (*color)[4];
} PBVHColorBufferNode;
+typedef struct PBVHPixelsNode {
+ /**
+ * Contains triangle/pixel data used during texture painting.
+ *
+ * Contains #blender::bke::pbvh::pixels::NodeData.
+ */
+ void *node_data;
+} PBVHPixelsNode;
+
typedef enum {
PBVH_Leaf = 1 << 0,
@@ -66,6 +78,8 @@ typedef enum {
PBVH_UpdateTopology = 1 << 13,
PBVH_UpdateColor = 1 << 14,
+ PBVH_RebuildPixels = 1 << 15,
+
} PBVHNodeFlags;
typedef struct PBVHFrustumPlanes {
@@ -127,6 +141,12 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh,
struct BMLog *log,
int cd_vert_node_offset,
int cd_face_node_offset);
+
+void BKE_pbvh_build_pixels(PBVH *pbvh,
+ const struct MLoop *mloop,
+ struct CustomData *ldata,
+ struct Image *image,
+ struct ImageUser *image_user);
void BKE_pbvh_free(PBVH *pbvh);
/* Hierarchical Search in the BVH, two methods:
@@ -287,6 +307,7 @@ bool BKE_pbvh_node_fully_masked_get(PBVHNode *node);
void BKE_pbvh_node_fully_unmasked_set(PBVHNode *node, int fully_masked);
bool BKE_pbvh_node_fully_unmasked_get(PBVHNode *node);
+void BKE_pbvh_mark_rebuild_pixels(PBVH *pbvh);
void BKE_pbvh_vert_mark_update(PBVH *pbvh, int index);
void BKE_pbvh_node_get_grids(PBVH *pbvh,
diff --git a/source/blender/blenkernel/BKE_pbvh_pixels.hh b/source/blender/blenkernel/BKE_pbvh_pixels.hh
new file mode 100644
index 00000000000..35eb340d0a1
--- /dev/null
+++ b/source/blender/blenkernel/BKE_pbvh_pixels.hh
@@ -0,0 +1,184 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+#pragma once
+
+#include "BLI_math.h"
+#include "BLI_math_vec_types.hh"
+#include "BLI_rect.h"
+#include "BLI_vector.hh"
+
+#include "DNA_image_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_image.h"
+#include "BKE_image_wrappers.hh"
+
+#include "IMB_imbuf_types.h"
+
+namespace blender::bke::pbvh::pixels {
+
+struct TrianglePaintInput {
+ int3 vert_indices;
+ /**
+ * Delta barycentric coordinates between 2 neighbouring UV's in the U direction.
+ *
+ * Only the first two coordinates are stored. The third should be recalculated
+ */
+ float2 delta_barycentric_coord_u;
+
+ /**
+ * Initially only the vert indices are known.
+ *
+ * delta_barycentric_coord_u is initialized in a later stage as it requires image tile
+ * dimensions.
+ */
+ TrianglePaintInput(const int3 vert_indices)
+ : vert_indices(vert_indices), delta_barycentric_coord_u(0.0f, 0.0f)
+ {
+ }
+};
+
+/**
+ * Data shared between pixels that belong to the same triangle.
+ *
+ * Data is stored as a list of structs, grouped by usage to improve performance (improves CPU
+ * cache prefetching).
+ */
+struct Triangles {
+ /** Data accessed by the inner loop of the painting brush. */
+ Vector<TrianglePaintInput> paint_input;
+
+ public:
+ void append(const int3 vert_indices)
+ {
+ this->paint_input.append(TrianglePaintInput(vert_indices));
+ }
+
+ TrianglePaintInput &get_paint_input(const int index)
+ {
+ return paint_input[index];
+ }
+
+ const TrianglePaintInput &get_paint_input(const int index) const
+ {
+ return paint_input[index];
+ }
+
+ void clear()
+ {
+ paint_input.clear();
+ }
+
+ uint64_t size() const
+ {
+ return paint_input.size();
+ }
+
+ uint64_t mem_size() const
+ {
+ return paint_input.size() * sizeof(TrianglePaintInput);
+ }
+};
+
+/**
+ * Encode sequential pixels to reduce memory footprint.
+ */
+struct PackedPixelRow {
+ /** Barycentric coordinate of the first pixel. */
+ float2 start_barycentric_coord;
+ /** Image coordinate starting of the first pixel. */
+ ushort2 start_image_coordinate;
+ /** Number of sequential pixels encoded in this package. */
+ ushort num_pixels;
+ /** Reference to the pbvh triangle index. */
+ ushort triangle_index;
+};
+
+/**
+ * Node pixel data containing the pixels for a single UDIM tile.
+ */
+struct UDIMTilePixels {
+ /** UDIM Tile number. */
+ short tile_number;
+
+ struct {
+ bool dirty : 1;
+ } flags;
+
+ /* Dirty region of the tile in image space. */
+ rcti dirty_region;
+
+ Vector<PackedPixelRow> pixel_rows;
+
+ UDIMTilePixels()
+ {
+ flags.dirty = false;
+ BLI_rcti_init_minmax(&dirty_region);
+ }
+
+ void mark_dirty(const PackedPixelRow &pixel_row)
+ {
+ int2 start_image_coord(pixel_row.start_image_coordinate.x, pixel_row.start_image_coordinate.y);
+ BLI_rcti_do_minmax_v(&dirty_region, start_image_coord);
+ BLI_rcti_do_minmax_v(&dirty_region, start_image_coord + int2(pixel_row.num_pixels + 1, 0));
+ flags.dirty = true;
+ }
+
+ void clear_dirty()
+ {
+ BLI_rcti_init_minmax(&dirty_region);
+ flags.dirty = false;
+ }
+};
+
+struct NodeData {
+ struct {
+ bool dirty : 1;
+ } flags;
+
+ Vector<UDIMTilePixels> tiles;
+ Triangles triangles;
+
+ NodeData()
+ {
+ flags.dirty = false;
+ }
+
+ UDIMTilePixels *find_tile_data(const image::ImageTileWrapper &image_tile)
+ {
+ for (UDIMTilePixels &tile : tiles) {
+ if (tile.tile_number == image_tile.get_tile_number()) {
+ return &tile;
+ }
+ }
+ return nullptr;
+ }
+
+ void mark_region(Image &image, const image::ImageTileWrapper &image_tile, ImBuf &image_buffer)
+ {
+ UDIMTilePixels *tile = find_tile_data(image_tile);
+ if (tile && tile->flags.dirty) {
+ BKE_image_partial_update_mark_region(
+ &image, image_tile.image_tile, &image_buffer, &tile->dirty_region);
+ tile->clear_dirty();
+ }
+ }
+
+ void clear_data()
+ {
+ tiles.clear();
+ triangles.clear();
+ }
+
+ static void free_func(void *instance)
+ {
+ NodeData *node_data = static_cast<NodeData *>(instance);
+ MEM_delete(node_data);
+ }
+};
+
+NodeData &BKE_pbvh_pixels_node_data_get(PBVHNode &node);
+void BKE_pbvh_pixels_mark_image_dirty(PBVHNode &node, Image &image, ImageUser &image_user);
+
+} // namespace blender::bke::pbvh::pixels
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index aca8cdf916e..710e2900d78 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -126,7 +126,7 @@ set(SRC
intern/editlattice.c
intern/editmesh.c
intern/editmesh_bvh.c
- intern/editmesh_cache.c
+ intern/editmesh_cache.cc
intern/editmesh_tangent.c
intern/effect.c
intern/fcurve.c
@@ -203,12 +203,12 @@ set(SRC
intern/mesh_normals.cc
intern/mesh_remap.c
intern/mesh_remesh_voxel.cc
- intern/mesh_runtime.c
+ intern/mesh_runtime.cc
intern/mesh_sample.cc
intern/mesh_tangent.c
intern/mesh_tessellate.c
intern/mesh_validate.cc
- intern/mesh_wrapper.c
+ intern/mesh_wrapper.cc
intern/modifier.c
intern/movieclip.c
intern/multires.c
@@ -235,6 +235,7 @@ set(SRC
intern/outliner_treehash.c
intern/packedFile.c
intern/paint.c
+ intern/paint_canvas.cc
intern/paint_toolslots.c
intern/particle.c
intern/particle_child.c
@@ -243,6 +244,7 @@ set(SRC
intern/pbvh.cc
intern/pbvh.c
intern/pbvh_bmesh.c
+ intern/pbvh_pixels.cc
intern/pointcache.c
intern/pointcloud.cc
intern/preferences.c
diff --git a/source/blender/blenkernel/intern/anim_path.c b/source/blender/blenkernel/intern/anim_path.c
index 1f8c6df6147..a8c25069c19 100644
--- a/source/blender/blenkernel/intern/anim_path.c
+++ b/source/blender/blenkernel/intern/anim_path.c
@@ -295,10 +295,12 @@ bool BKE_where_on_path(const Object *ob,
key_curve_tangent_weights(frac, w, KEY_BSPLINE);
- interp_v3_v3v3v3v3(r_dir, p0->vec, p1->vec, p2->vec, p3->vec, w);
+ if (r_dir) {
+ interp_v3_v3v3v3v3(r_dir, p0->vec, p1->vec, p2->vec, p3->vec, w);
- /* Make compatible with #vec_to_quat. */
- negate_v3(r_dir);
+ /* Make compatible with #vec_to_quat. */
+ negate_v3(r_dir);
+ }
//}
const ListBase *nurbs = BKE_curve_editNurbs_get(cu);
diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c
index 8a5bf2b81dd..54fee079947 100644
--- a/source/blender/blenkernel/intern/anim_sys.c
+++ b/source/blender/blenkernel/intern/anim_sys.c
@@ -1531,6 +1531,192 @@ static NlaEvalChannel *nlaevalchan_verify(PointerRNA *ptr, NlaEvalData *nlaeval,
/* ---------------------- */
+/** \returns true if a solution exists and the output was written to. */
+static bool nla_blend_get_inverted_lower_value(const int blendmode,
+ const float strip_value,
+ const float blended_value,
+ const float influence,
+ float *r_lower_value)
+{
+ if (IS_EQF(influence, 0.0f)) {
+ *r_lower_value = blended_value;
+ return true;
+ }
+
+ switch (blendmode) {
+ case NLASTRIP_MODE_ADD:
+ /* Simply subtract the scaled value on to the stack. */
+ *r_lower_value = blended_value - (strip_value * influence);
+ return true;
+
+ case NLASTRIP_MODE_SUBTRACT:
+ /* Simply add the scaled value from the stack. */
+ *r_lower_value = blended_value + (strip_value * influence);
+ return true;
+
+ case NLASTRIP_MODE_MULTIPLY: {
+ /* Check for division by zero. */
+ const float denominator = (influence * strip_value + (1.0f - influence));
+ if (IS_EQF(denominator, 0.0f)) {
+ /* For 0/0, any r_lower_value is a solution. We'll just choose 1.
+ *
+ * Any r_lower_value is a solution. In this case, ideally we would insert redundant
+ * keyframes, those that don't change the fcurve's shape. Otherwise, we're likely messing
+ * up interpolation for the animator, requiring further cleanup on their part.
+ */
+ if (IS_EQF(blended_value, 0.0f)) {
+ /* When denominator==0:
+ *
+ * denominator = (inf * strip_value + (1.0f - inf))
+ * 0 = inf * strip_value + (1-inf)
+ * -inf * strip_value = 1 - inf
+ * -strip_value = (1 - inf) / inf
+ * strip_value = (inf - 1) / inf
+ * strip_value = 1 - (1/inf)
+ *
+ * For blending, nla_blend_value(), this results in:
+ *
+ * blended_value = inf * (lower_value * strip_value) + (1 - inf) * lower_value;
+ * = inf * (lower_value * (1 - (1/inf))) + ...
+ * = inf * (1 - (1/inf)) * lower_value + ...
+ * = (inf - (inf/inf)) * lower_value + ...
+ * = -(inf - 1) * lower_value + (1 - inf) * lower_value;
+ * blended_value = 0
+ *
+ * Effectively, blended_value will equal 0 no matter what lower_value is. Put another
+ * way, when (blended_value==0 and denominator==0), then lower_value can be any value and
+ * blending will give us back blended_value=0. We have infinite solutions for this case.
+ */
+ *r_lower_value = 1;
+ return true;
+ }
+ /* No solution for division by zero. */
+ return false;
+ }
+ /* Math:
+ * blended_value = inf * (lower_value * strip_value) + (1 - inf) * lower_value
+ * = lower_value * (inf * strip_value + (1-inf))
+ * lower_value = blended_value / (inf * strip_value + (1-inf))
+ * lower_value = blended_value / denominator
+ */
+ *r_lower_value = blended_value / denominator;
+ return true;
+ }
+ case NLASTRIP_MODE_COMBINE:
+ BLI_assert_msg(0, "Use nla_combine_get_inverted_lower_value()");
+ return false;
+
+ case NLASTRIP_MODE_REPLACE:
+
+ /* No solution if lower strip has 0 influence. */
+ if (IS_EQF(influence, 1.0f)) {
+ return false;
+ }
+
+ /* Math:
+ *
+ * blended_value = lower_value * (1.0f - inf) + (strip_value * inf)
+ * blended_value - (strip_value * inf) = lower_value * (1.0f - inf)
+ * blended_value - (strip_value * inf) / (1.0f - inf) = lower_value
+ *
+ * lower_value = blended_value - (strip_value * inf) / (1.0f - inf)
+ */
+ *r_lower_value = (blended_value - (strip_value * influence)) / (1.0f - influence);
+ return true;
+ }
+
+ BLI_assert_msg(0, "invalid blend mode");
+ return false;
+}
+
+/** \returns true if solution exists and output written to. */
+static bool nla_combine_get_inverted_lower_value(const int mix_mode,
+ float base_value,
+ const float strip_value,
+ const float blended_value,
+ const float influence,
+ float *r_lower_value)
+{
+ if (IS_EQF(influence, 0.0f)) {
+ *r_lower_value = blended_value;
+ return true;
+ }
+
+ /* Perform blending. */
+ switch (mix_mode) {
+ case NEC_MIX_ADD:
+ case NEC_MIX_AXIS_ANGLE:
+ *r_lower_value = blended_value - (strip_value - base_value) * influence;
+ return true;
+ case NEC_MIX_MULTIPLY:
+ /* Division by zero. */
+ if (IS_EQF(strip_value, 0.0f)) {
+ /* Resolve 0/0 to 1.
+ *
+ * Any r_lower_value is a solution. In this case, ideally we would insert redundant
+ * keyframes, those that don't change the fcurve's shape. Otherwise, we're likely messing
+ * up interpolation for the animator, requiring further cleanup on their part.
+ */
+ if (IS_EQF(blended_value, 0.0f)) {
+ /* For blending, nla_combine_value(), when strip_value==0:
+ *
+ * blended_value = lower_value * powf(strip_value / base_value, infl);
+ * blended_value = lower_value * powf(0, infl);
+ * blended_value = lower_value * 0;
+ * blended_value = 0;
+ *
+ * Effectively, blended_value will equal 0 no matter what lower_value is. Put another
+ * way, when (blended_value==0 and strip_value==0), then lower_value can be any value and
+ * blending will give us back blended_value=0. We have infinite solutions for this case.
+ */
+ *r_lower_value = 1.0f;
+ return true;
+ }
+ /* No solution. */
+ return false;
+ }
+
+ if (IS_EQF(base_value, 0.0f)) {
+ base_value = 1.0f;
+ }
+
+ *r_lower_value = blended_value / powf(strip_value / base_value, influence);
+ return true;
+
+ case NEC_MIX_QUATERNION:
+ BLI_assert_msg(0, "Use nla_combine_quaternion_get_inverted_lower_values()");
+ return false;
+ }
+
+ BLI_assert_msg(0, "Mixmode not implemented");
+ return false;
+}
+
+static void nla_combine_quaternion_get_inverted_lower_values(const float strip_values[4],
+ const float blended_values[4],
+ const float influence,
+ float r_lower_value[4])
+{
+ if (IS_EQF(influence, 0.0f)) {
+ normalize_qt_qt(r_lower_value, blended_values);
+ return;
+ }
+
+ /* blended_value = lower_values @ strip_values^infl
+ * blended_value @ inv(strip_values^inf) = lower_values
+ *
+ * Returns: lower_values = blended_value @ inv(strip_values^inf) */
+ float tmp_strip_values[4], tmp_blended[4];
+
+ normalize_qt_qt(tmp_strip_values, strip_values);
+ normalize_qt_qt(tmp_blended, blended_values);
+
+ pow_qt_fl_normalized(tmp_strip_values, influence);
+ invert_qt_normalized(tmp_strip_values);
+
+ mul_qt_qtqt(r_lower_value, tmp_blended, tmp_strip_values);
+}
+
/* Blend the lower nla stack value and upper strip value of a channel according to mode and
* influence. */
static float nla_blend_value(const int blendmode,
@@ -1772,6 +1958,25 @@ static void nlaevalchan_assert_blendOrcombine_compatible(NlaEvalChannelSnapshot
BLI_assert(lower_necs->length == blended_necs->length);
}
+/** Check each remap domain of blended values individually in case animator had a non-combine NLA
+ * strip with a subset of quaternion channels and remapping through any of them failed and thus
+ * potentially has undefined values.
+ *
+ * \returns true if case occured and handled. Returns false if case didn't occur.
+ */
+static bool nlaevalchan_combine_quaternion_handle_undefined_blend_values(
+ NlaEvalChannelSnapshot *blended_necs, NlaEvalChannelSnapshot *upper_or_lower_necs)
+{
+ for (int j = 0; j < 4; j++) {
+ if (!BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, j)) {
+ BLI_bitmap_set_all(upper_or_lower_necs->remap_domain.ptr, false, 4);
+ return true;
+ }
+ }
+
+ return false;
+}
+
/* Assert that the channels given can be blended or combined together as a quaternion. */
static void nlaevalchan_assert_blendOrcombine_compatible_quaternion(
NlaEvalChannelSnapshot *lower_necs,
@@ -1787,14 +1992,12 @@ static void nlaevalchan_copy_values(NlaEvalChannelSnapshot *dst, NlaEvalChannelS
memcpy(dst->values, src->values, src->length * sizeof(float));
}
-/**
- * Copies lower necs to blended necs if upper necs is NULL or has zero influence.
- * \return true if copied.
- */
-static bool nlaevalchan_blendOrcombine_try_copy_lower(NlaEvalChannelSnapshot *lower_necs,
- NlaEvalChannelSnapshot *upper_necs,
- const float upper_influence,
- NlaEvalChannelSnapshot *r_blended_necs)
+/** Copies from lower necs to blended necs if upper necs is NULL or has zero influence.
+ * \return true if copied. */
+static bool nlaevalchan_blendOrcombine_try_copy_from_lower(NlaEvalChannelSnapshot *lower_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_blended_necs)
{
const bool has_influence = !IS_EQF(upper_influence, 0.0f);
if (upper_necs != NULL && has_influence) {
@@ -1805,10 +2008,35 @@ static bool nlaevalchan_blendOrcombine_try_copy_lower(NlaEvalChannelSnapshot *lo
return true;
}
-/**
- * Based on blend-mode, blend lower necs with upper necs into blended necs.
+/** Copies to lower necs from blended necs if upper necs is NULL or has zero influence. If
+ * successful, copies blended_necs remap domains to lower_necs.
*
- * Each upper value's blend domain determines whether to blend or to copy directly from lower.
+ * Does not check upper value blend domains.
+ *
+ * \return true if copied. */
+static bool nlaevalchan_blendOrcombine_try_copy_to_lower(NlaEvalChannelSnapshot *blended_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_lower_necs)
+{
+ const bool has_influence = !IS_EQF(upper_influence, 0.0f);
+ if (upper_necs != NULL && has_influence) {
+ return false;
+ }
+
+ nlaevalchan_copy_values(r_lower_necs, blended_necs);
+
+ /* Must copy remap domain to handle case where some blended values are out of domain. */
+ BLI_bitmap_copy_all(
+ r_lower_necs->remap_domain.ptr, blended_necs->remap_domain.ptr, r_lower_necs->length);
+
+ return true;
+}
+
+/** Based on blendmode, blend lower necs with upper necs into blended necs.
+ *
+ * Each upper value's blend domain determines whether to blend or to copy directly
+ * from lower.
*/
static void nlaevalchan_blend_value(NlaEvalChannelSnapshot *lower_necs,
NlaEvalChannelSnapshot *upper_necs,
@@ -1817,7 +2045,7 @@ static void nlaevalchan_blend_value(NlaEvalChannelSnapshot *lower_necs,
NlaEvalChannelSnapshot *r_blended_necs)
{
nlaevalchan_assert_blendOrcombine_compatible(lower_necs, upper_necs, r_blended_necs);
- if (nlaevalchan_blendOrcombine_try_copy_lower(
+ if (nlaevalchan_blendOrcombine_try_copy_from_lower(
lower_necs, upper_necs, upper_influence, r_blended_necs)) {
return;
}
@@ -1846,7 +2074,7 @@ static void nlaevalchan_combine_value(NlaEvalChannelSnapshot *lower_necs,
NlaEvalChannelSnapshot *r_blended_necs)
{
nlaevalchan_assert_blendOrcombine_compatible(lower_necs, upper_necs, r_blended_necs);
- if (nlaevalchan_blendOrcombine_try_copy_lower(
+ if (nlaevalchan_blendOrcombine_try_copy_from_lower(
lower_necs, upper_necs, upper_influence, r_blended_necs)) {
return;
}
@@ -1879,7 +2107,7 @@ static void nlaevalchan_combine_quaternion(NlaEvalChannelSnapshot *lower_necs,
NlaEvalChannelSnapshot *r_blended_necs)
{
nlaevalchan_assert_blendOrcombine_compatible_quaternion(lower_necs, upper_necs, r_blended_necs);
- if (nlaevalchan_blendOrcombine_try_copy_lower(
+ if (nlaevalchan_blendOrcombine_try_copy_from_lower(
lower_necs, upper_necs, upper_influence, r_blended_necs)) {
return;
}
@@ -2033,14 +2261,8 @@ static void nlaevalchan_combine_quaternion_get_inverted_upper_evalchan(
nlaevalchan_assert_nonNull(r_upper_necs);
nlaevalchan_assert_blendOrcombine_compatible_quaternion(lower_necs, r_upper_necs, blended_necs);
- /* Must check each domain index individually in case animator had a non-combine NLA strip with a
- * subset of quaternion channels and remapping through any of them failed and thus potentially
- * has undefined values. */
- for (int j = 0; j < 4; j++) {
- if (!BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, j)) {
- BLI_bitmap_set_all(r_upper_necs->remap_domain.ptr, false, 4);
- return;
- }
+ if (nlaevalchan_combine_quaternion_handle_undefined_blend_values(blended_necs, r_upper_necs)) {
+ return;
}
const bool success = nla_combine_quaternion_get_inverted_strip_values(
@@ -2109,6 +2331,173 @@ static void nlaevalchan_blendOrcombine_get_inverted_upper_evalchan(
}
}
+static void nlaevalchan_blend_value_get_inverted_lower_evalchan(
+ NlaEvalChannelSnapshot *blended_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ const int upper_blendmode,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_lower_necs)
+{
+ nlaevalchan_assert_blendOrcombine_compatible(r_lower_necs, upper_necs, blended_necs);
+
+ if (nlaevalchan_blendOrcombine_try_copy_to_lower(
+ blended_necs, upper_necs, upper_influence, r_lower_necs)) {
+ return;
+ }
+
+ const int length = r_lower_necs->length;
+
+ for (int j = 0; j < length; j++) {
+ if (!BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, j)) {
+ BLI_BITMAP_DISABLE(r_lower_necs->remap_domain.ptr, j);
+ continue;
+ }
+
+ /* If upper value was not blended, then the blended value was directly copied from the lower
+ * value. */
+ if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, j)) {
+ r_lower_necs->values[j] = blended_necs->values[j];
+ BLI_BITMAP_ENABLE(r_lower_necs->remap_domain.ptr, j);
+ continue;
+ }
+
+ const bool success = nla_blend_get_inverted_lower_value(upper_blendmode,
+ upper_necs->values[j],
+ blended_necs->values[j],
+ upper_influence,
+ &r_lower_necs->values[j]);
+
+ BLI_BITMAP_SET(r_lower_necs->remap_domain.ptr, j, success);
+ }
+}
+
+static void nlaevalchan_combine_value_get_inverted_lower_evalchan(
+ NlaEvalChannelSnapshot *blended_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_lower_necs)
+{
+ nlaevalchan_assert_blendOrcombine_compatible(r_lower_necs, upper_necs, blended_necs);
+
+ if (nlaevalchan_blendOrcombine_try_copy_to_lower(
+ blended_necs, upper_necs, upper_influence, r_lower_necs)) {
+ return;
+ }
+
+ float *base_values = r_lower_necs->channel->base_snapshot.values;
+ const int mix_mode = r_lower_necs->channel->mix_mode;
+ const int length = r_lower_necs->length;
+
+ for (int j = 0; j < length; j++) {
+ if (!BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, j)) {
+ BLI_BITMAP_DISABLE(r_lower_necs->remap_domain.ptr, j);
+ continue;
+ }
+
+ /* If upper value was not blended, then the blended value was directly copied from the lower
+ * value. */
+ if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, j)) {
+ r_lower_necs->values[j] = blended_necs->values[j];
+ BLI_BITMAP_ENABLE(r_lower_necs->remap_domain.ptr, j);
+ continue;
+ }
+
+ const bool success = nla_combine_get_inverted_lower_value(mix_mode,
+ base_values[j],
+ upper_necs->values[j],
+ blended_necs->values[j],
+ upper_influence,
+ &r_lower_necs->values[j]);
+
+ BLI_BITMAP_SET(r_lower_necs->remap_domain.ptr, j, success);
+ }
+}
+
+static void nlaevalchan_combine_quaternion_get_inverted_lower_evalchan(
+ NlaEvalChannelSnapshot *blended_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_lower_necs)
+{
+ nlaevalchan_assert_blendOrcombine_compatible_quaternion(r_lower_necs, upper_necs, blended_necs);
+
+ if (nlaevalchan_combine_quaternion_handle_undefined_blend_values(blended_necs, r_lower_necs)) {
+ return;
+ }
+
+ if (nlaevalchan_blendOrcombine_try_copy_to_lower(
+ blended_necs, upper_necs, upper_influence, r_lower_necs)) {
+ return;
+ }
+
+ /* If upper value was not blended, then the blended value was directly copied from the lower
+ * value. */
+ if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, 0)) {
+ memcpy(r_lower_necs->values, blended_necs->values, 4 * sizeof(float));
+ BLI_bitmap_set_all(r_lower_necs->remap_domain.ptr, true, 4);
+ return;
+ }
+
+ nla_combine_quaternion_get_inverted_lower_values(
+ upper_necs->values, blended_necs->values, upper_influence, r_lower_necs->values);
+
+ BLI_bitmap_set_all(r_lower_necs->remap_domain.ptr, true, 4);
+}
+
+/** Based on blendmode and mix mode, solve for the lower values such that when lower blended or
+ * combined with upper then we get blended values as a result.
+ *
+ * Only processes blended values in the remap domain. Successfully remapped lower values are placed
+ * in the remap domain so caller knows which values are usable.
+ *
+ * \param blended_necs: Never NULL.
+ * \param upper_necs: Can be NULL.
+ * \param upper_blendmode: Enum value in eNlaStrip_Blend_Mode.
+ * \param upper_influence: Value in range [0, 1].
+ * \param r_lower_necs: Never NULL.
+ */
+static void nlaevalchan_blendOrCombine_get_inverted_lower_evalchan(
+ NlaEvalChannelSnapshot *blended_necs,
+ NlaEvalChannelSnapshot *upper_necs,
+ const int upper_blendmode,
+ const float upper_influence,
+ NlaEvalChannelSnapshot *r_lower_necs)
+
+{
+ nlaevalchan_assert_nonNull(r_lower_necs);
+
+ switch (upper_blendmode) {
+ case NLASTRIP_MODE_COMBINE: {
+ switch (r_lower_necs->channel->mix_mode) {
+ case NEC_MIX_QUATERNION: {
+ nlaevalchan_combine_quaternion_get_inverted_lower_evalchan(
+ blended_necs, upper_necs, upper_influence, r_lower_necs);
+ return;
+ }
+ case NEC_MIX_ADD:
+ case NEC_MIX_AXIS_ANGLE:
+ case NEC_MIX_MULTIPLY: {
+ nlaevalchan_combine_value_get_inverted_lower_evalchan(
+ blended_necs, upper_necs, upper_influence, r_lower_necs);
+ return;
+ }
+ }
+ BLI_assert_msg(0, "Mix mode should've been handled");
+ return;
+ }
+ case NLASTRIP_MODE_ADD:
+ case NLASTRIP_MODE_SUBTRACT:
+ case NLASTRIP_MODE_MULTIPLY:
+ case NLASTRIP_MODE_REPLACE: {
+ nlaevalchan_blend_value_get_inverted_lower_evalchan(
+ blended_necs, upper_necs, upper_blendmode, upper_influence, r_lower_necs);
+ return;
+ }
+ }
+
+ BLI_assert_msg(0, "Blend mode should've been handled");
+}
+
/* ---------------------- */
/* F-Modifier stack joining/separation utilities -
* should we generalize these for BLI_listbase.h interface? */
@@ -2223,7 +2612,8 @@ static void nlasnapshot_from_action(PointerRNA *ptr,
}
/* evaluate action-clip strip */
-static void nlastrip_evaluate_actionclip(PointerRNA *ptr,
+static void nlastrip_evaluate_actionclip(const int evaluation_mode,
+ PointerRNA *ptr,
NlaEvalData *channels,
ListBase *modifiers,
NlaEvalStrip *nes,
@@ -2247,22 +2637,49 @@ static void nlastrip_evaluate_actionclip(PointerRNA *ptr,
/* join this strip's modifiers to the parent's modifiers (own modifiers first) */
nlaeval_fmodifiers_join_stacks(&tmp_modifiers, &strip->modifiers, modifiers);
- NlaEvalSnapshot strip_snapshot;
- nlaeval_snapshot_init(&strip_snapshot, channels, NULL);
+ switch (evaluation_mode) {
+ case STRIP_EVAL_BLEND: {
+
+ NlaEvalSnapshot strip_snapshot;
+ nlaeval_snapshot_init(&strip_snapshot, channels, NULL);
+
+ nlasnapshot_from_action(
+ ptr, channels, &tmp_modifiers, strip->act, strip->strip_time, &strip_snapshot);
+ nlasnapshot_blend(
+ channels, snapshot, &strip_snapshot, strip->blendmode, strip->influence, snapshot);
- nlasnapshot_from_action(
- ptr, channels, &tmp_modifiers, strip->act, strip->strip_time, &strip_snapshot);
- nlasnapshot_blend(
- channels, snapshot, &strip_snapshot, strip->blendmode, strip->influence, snapshot);
+ nlaeval_snapshot_free_data(&strip_snapshot);
+
+ break;
+ }
+ case STRIP_EVAL_BLEND_GET_INVERTED_LOWER_SNAPSHOT: {
- nlaeval_snapshot_free_data(&strip_snapshot);
+ NlaEvalSnapshot strip_snapshot;
+ nlaeval_snapshot_init(&strip_snapshot, channels, NULL);
+
+ nlasnapshot_from_action(
+ ptr, channels, &tmp_modifiers, strip->act, strip->strip_time, &strip_snapshot);
+ nlasnapshot_blend_get_inverted_lower_snapshot(
+ channels, snapshot, &strip_snapshot, strip->blendmode, strip->influence, snapshot);
+
+ nlaeval_snapshot_free_data(&strip_snapshot);
+
+ break;
+ }
+ case STRIP_EVAL_NOBLEND: {
+ nlasnapshot_from_action(
+ ptr, channels, &tmp_modifiers, strip->act, strip->strip_time, snapshot);
+ break;
+ }
+ }
/* unlink this strip's modifiers from the parent's modifiers again */
nlaeval_fmodifiers_split_stacks(&strip->modifiers, modifiers);
}
/* evaluate transition strip */
-static void nlastrip_evaluate_transition(PointerRNA *ptr,
+static void nlastrip_evaluate_transition(const int evaluation_mode,
+ PointerRNA *ptr,
NlaEvalData *channels,
ListBase *modifiers,
NlaEvalStrip *nes,
@@ -2294,49 +2711,126 @@ static void nlastrip_evaluate_transition(PointerRNA *ptr,
s2 = nes->strip->next;
}
- /* prepare template for 'evaluation strip'
- * - based on the transition strip's evaluation strip data
- * - strip_mode is NES_TIME_TRANSITION_* based on which endpoint
- * - strip_time is the 'normalized' (i.e. in-strip) time for evaluation,
- * which doubles up as an additional weighting factor for the strip influences
- * which allows us to appear to be 'interpolating' between the two extremes
- */
- tmp_nes = *nes;
-
- /* evaluate these strips into a temp-buffer (tmp_channels) */
- /* FIXME: modifier evaluation here needs some work... */
- /* first strip */
- tmp_nes.strip_mode = NES_TIME_TRANSITION_START;
- tmp_nes.strip = s1;
- tmp_nes.strip_time = s1->strip_time;
- nlaeval_snapshot_init(&snapshot1, channels, snapshot);
- nlastrip_evaluate(
- ptr, channels, &tmp_modifiers, &tmp_nes, &snapshot1, anim_eval_context, flush_to_original);
+ switch (evaluation_mode) {
+ case STRIP_EVAL_BLEND: {
- /* second strip */
- tmp_nes.strip_mode = NES_TIME_TRANSITION_END;
- tmp_nes.strip = s2;
- tmp_nes.strip_time = s2->strip_time;
- nlaeval_snapshot_init(&snapshot2, channels, snapshot);
- nlastrip_evaluate(
- ptr, channels, &tmp_modifiers, &tmp_nes, &snapshot2, anim_eval_context, flush_to_original);
+ /* prepare template for 'evaluation strip'
+ * - based on the transition strip's evaluation strip data
+ * - strip_mode is NES_TIME_TRANSITION_* based on which endpoint
+ * - strip_time is the 'normalized' (i.e. in-strip) time for evaluation,
+ * which doubles up as an additional weighting factor for the strip influences
+ * which allows us to appear to be 'interpolating' between the two extremes
+ */
+ tmp_nes = *nes;
+
+ /* evaluate these strips into a temp-buffer (tmp_channels) */
+ /* FIXME: modifier evaluation here needs some work... */
+ /* first strip */
+ tmp_nes.strip_mode = NES_TIME_TRANSITION_START;
+ tmp_nes.strip = s1;
+ tmp_nes.strip_time = s1->strip_time;
+ nlaeval_snapshot_init(&snapshot1, channels, snapshot);
+ nlasnapshot_blend_strip(ptr,
+ channels,
+ &tmp_modifiers,
+ &tmp_nes,
+ &snapshot1,
+ anim_eval_context,
+ flush_to_original);
+
+ /* second strip */
+ tmp_nes.strip_mode = NES_TIME_TRANSITION_END;
+ tmp_nes.strip = s2;
+ tmp_nes.strip_time = s2->strip_time;
+ nlaeval_snapshot_init(&snapshot2, channels, snapshot);
+ nlasnapshot_blend_strip(ptr,
+ channels,
+ &tmp_modifiers,
+ &tmp_nes,
+ &snapshot2,
+ anim_eval_context,
+ flush_to_original);
+
+ /** Replace \a snapshot2 NULL channels with base or default values so all channels blend. */
+ nlasnapshot_ensure_channels(channels, &snapshot2);
+ /** Mark all \a snapshot2 channel's values to blend. */
+ nlasnapshot_enable_all_blend_domain(&snapshot2);
+ nlasnapshot_blend(
+ channels, &snapshot1, &snapshot2, NLASTRIP_MODE_REPLACE, nes->strip_time, snapshot);
+
+ nlaeval_snapshot_free_data(&snapshot1);
+ nlaeval_snapshot_free_data(&snapshot2);
+
+ break;
+ }
+ case STRIP_EVAL_BLEND_GET_INVERTED_LOWER_SNAPSHOT: {
+ /* No support for remapping values through a transition. Mark all channel values affected by
+ * transition as non-remappable. */
+ tmp_nes = *nes;
+
+ /* Process first strip. */
+ tmp_nes.strip = s1;
+ tmp_nes.strip_time = s1->strip_time;
+ nlaeval_snapshot_init(&snapshot1, channels, snapshot);
+ nlasnapshot_blend_strip_no_blend(
+ ptr, channels, &tmp_modifiers, &tmp_nes, &snapshot1, anim_eval_context);
+
+ /* Remove channel values affected by transition from the remap domain. */
+ LISTBASE_FOREACH (NlaEvalChannel *, nec, &channels->channels) {
+ NlaEvalChannelSnapshot *necs = nlaeval_snapshot_get(&snapshot1, nec->index);
+ if (necs == NULL) {
+ continue;
+ }
+ NlaEvalChannelSnapshot *output_necs = nlaeval_snapshot_ensure_channel(snapshot, nec);
+ for (int i = 0; i < necs->length; i++) {
+ if (BLI_BITMAP_TEST_BOOL(necs->blend_domain.ptr, i)) {
+ BLI_BITMAP_DISABLE(output_necs->remap_domain.ptr, i);
+ }
+ }
+ }
+
+ nlaeval_snapshot_free_data(&snapshot1);
+
+ /* Process second strip. */
+ tmp_nes.strip = s2;
+ tmp_nes.strip_time = s2->strip_time;
+ nlaeval_snapshot_init(&snapshot2, channels, snapshot);
+ nlasnapshot_blend_strip_no_blend(
+ ptr, channels, &tmp_modifiers, &tmp_nes, &snapshot2, anim_eval_context);
+
+ /* Remove channel values affected by transition from the remap domain. */
+ LISTBASE_FOREACH (NlaEvalChannel *, nec, &channels->channels) {
+ NlaEvalChannelSnapshot *necs = nlaeval_snapshot_get(&snapshot2, nec->index);
+ if (necs == NULL) {
+ continue;
+ }
+ NlaEvalChannelSnapshot *output_necs = nlaeval_snapshot_ensure_channel(snapshot, nec);
+ for (int i = 0; i < necs->length; i++) {
+ if (BLI_BITMAP_TEST_BOOL(necs->blend_domain.ptr, i)) {
+ BLI_BITMAP_DISABLE(output_necs->remap_domain.ptr, i);
+ }
+ }
+ }
- /** Replace \a snapshot2 NULL channels with base or default values so all channels blend. */
- nlasnapshot_ensure_channels(channels, &snapshot2);
- /** Mark all \a snapshot2 channel's values to blend. */
- nlasnapshot_enable_all_blend_domain(&snapshot2);
- nlasnapshot_blend(
- channels, &snapshot1, &snapshot2, NLASTRIP_MODE_REPLACE, nes->strip_time, snapshot);
+ nlaeval_snapshot_free_data(&snapshot2);
- nlaeval_snapshot_free_data(&snapshot1);
- nlaeval_snapshot_free_data(&snapshot2);
+ break;
+ }
+ case STRIP_EVAL_NOBLEND: {
+ BLI_assert(
+ !"This case shouldn't occur. Transitions assumed to not reference other "
+ "transitions. ");
+ break;
+ }
+ }
/* unlink this strip's modifiers from the parent's modifiers again */
nlaeval_fmodifiers_split_stacks(&nes->strip->modifiers, modifiers);
}
/* evaluate meta-strip */
-static void nlastrip_evaluate_meta(PointerRNA *ptr,
+static void nlastrip_evaluate_meta(const int evaluation_mode,
+ PointerRNA *ptr,
NlaEvalData *channels,
ListBase *modifiers,
NlaEvalStrip *nes,
@@ -2366,12 +2860,31 @@ static void nlastrip_evaluate_meta(PointerRNA *ptr,
evaltime);
tmp_nes = nlastrips_ctime_get_strip(NULL, &strip->strips, -1, &child_context, flush_to_original);
+ /* Assert currently supported modes. If new mode added, then assertion marks potentially missed
+ * area.
+ *
+ * Note: In the future if support is ever added to metastrips to support nested tracks, then
+ * STRIP_EVAL_BLEND and STRIP_EVAL_BLEND_GET_INVERTED_LOWER_SNAPSHOT cases are no longer
+ * equivalent. The output of nlastrips_ctime_get_strip() may return a list of strips. The only
+ * case difference should be the evaluation order.
+ */
+ BLI_assert(ELEM(evaluation_mode,
+ STRIP_EVAL_BLEND,
+ STRIP_EVAL_BLEND_GET_INVERTED_LOWER_SNAPSHOT,
+ STRIP_EVAL_NOBLEND));
+
/* directly evaluate child strip into accumulation buffer...
* - there's no need to use a temporary buffer (as it causes issues [T40082])
*/
if (tmp_nes) {
- nlastrip_evaluate(
- ptr, channels, &tmp_modifiers, tmp_nes, snapshot, &child_context, flush_to_original);
+ nlastrip_evaluate(evaluation_mode,
+ ptr,
+ channels,
+ &tmp_modifiers,
+ tmp_nes,
+ snapshot,
+ &child_context,
+ flush_to_original);
/* free temp eval-strip */
MEM_freeN(tmp_nes);
@@ -2381,7 +2894,8 @@ static void nlastrip_evaluate_meta(PointerRNA *ptr,
nlaeval_fmodifiers_split_stacks(&strip->modifiers, modifiers);
}
-void nlastrip_evaluate(PointerRNA *ptr,
+void nlastrip_evaluate(const int evaluation_mode,
+ PointerRNA *ptr,
NlaEvalData *channels,
ListBase *modifiers,
NlaEvalStrip *nes,
@@ -2406,15 +2920,27 @@ void nlastrip_evaluate(PointerRNA *ptr,
/* actions to take depend on the type of strip */
switch (strip->type) {
case NLASTRIP_TYPE_CLIP: /* action-clip */
- nlastrip_evaluate_actionclip(ptr, channels, modifiers, nes, snapshot);
+ nlastrip_evaluate_actionclip(evaluation_mode, ptr, channels, modifiers, nes, snapshot);
break;
case NLASTRIP_TYPE_TRANSITION: /* transition */
- nlastrip_evaluate_transition(
- ptr, channels, modifiers, nes, snapshot, anim_eval_context, flush_to_original);
+ nlastrip_evaluate_transition(evaluation_mode,
+ ptr,
+ channels,
+ modifiers,
+ nes,
+ snapshot,
+ anim_eval_context,
+ flush_to_original);
break;
case NLASTRIP_TYPE_META: /* meta */
- nlastrip_evaluate_meta(
- ptr, channels, modifiers, nes, snapshot, anim_eval_context, flush_to_original);
+ nlastrip_evaluate_meta(evaluation_mode,
+ ptr,
+ channels,
+ modifiers,
+ nes,
+ snapshot,
+ anim_eval_context,
+ flush_to_original);
break;
default: /* do nothing */
@@ -2425,6 +2951,53 @@ void nlastrip_evaluate(PointerRNA *ptr,
strip->flag &= ~NLASTRIP_FLAG_EDIT_TOUCHED;
}
+void nlasnapshot_blend_strip(PointerRNA *ptr,
+ NlaEvalData *channels,
+ ListBase *modifiers,
+ NlaEvalStrip *nes,
+ NlaEvalSnapshot *snapshot,
+ const struct AnimationEvalContext *anim_eval_context,
+ const bool flush_to_original)
+{
+ nlastrip_evaluate(STRIP_EVAL_BLEND,
+ ptr,
+ channels,
+ modifiers,
+ nes,
+ snapshot,
+ anim_eval_context,
+ flush_to_original);
+}
+
+void nlasnapshot_blend_strip_get_inverted_lower_snapshot(
+ PointerRNA *ptr,
+ NlaEvalData *channels,
+ ListBase *modifiers,
+ NlaEvalStrip *nes,
+ NlaEvalSnapshot *snapshot,
+ const struct AnimationEvalContext *anim_eval_context)
+{
+ nlastrip_evaluate(STRIP_EVAL_BLEND_GET_INVERTED_LOWER_SNAPSHOT,
+ ptr,
+ channels,
+ modifiers,
+ nes,
+ snapshot,
+ anim_eval_context,
+ false);
+}
+
+void nlasnapshot_blend_strip_no_blend(PointerRNA *ptr,
+ NlaEvalData *channels,
+ ListBase *modifiers,
+ NlaEvalStrip *nes,
+ NlaEvalSnapshot *snapshot,
+ const struct AnimationEvalContext *anim_eval_context)
+{
+ nlastrip_evaluate(
+ STRIP_EVAL_NOBLEND, ptr, channels, modifiers, nes, snapshot, anim_eval_context, false);
+}
+
void nladata_flush_channels(PointerRNA *ptr,
NlaEvalData *channels,
NlaEvalSnapshot *snapshot,
@@ -2524,8 +3097,14 @@ static void animsys_evaluate_nla_domain(PointerRNA *ptr, NlaEvalData *channels,
{
GSet *touched_actions = BLI_gset_ptr_new(__func__);
- if (adt->action) {
- nla_eval_domain_action(ptr, channels, adt->action, touched_actions);
+ /* Include domain of Action Track. */
+ if ((adt->flag & ADT_NLA_EDIT_ON) == 0) {
+ if (adt->action) {
+ nla_eval_domain_action(ptr, channels, adt->action, touched_actions);
+ }
+ }
+ else if (adt->tmpact && (adt->flag & ADT_NLA_EVAL_UPPER_TRACKS)) {
+ nla_eval_domain_action(ptr, channels, adt->tmpact, touched_actions);
}
/* NLA Data - Animation Data for Strips */
@@ -2634,7 +3213,8 @@ static void animsys_create_action_track_strip(const AnimData *adt,
const bool tweaking = (adt->flag & ADT_NLA_EDIT_ON) != 0;
const bool soloing = (adt->flag & ADT_NLA_SOLO_TRACK) != 0;
- const bool actionstrip_evaluated = r_action_strip->act && !soloing && !tweaking;
+ const bool eval_upper = !tweaking || (adt->flag & ADT_NLA_EVAL_UPPER_TRACKS) != 0;
+ const bool actionstrip_evaluated = r_action_strip->act && !soloing && eval_upper;
if (!actionstrip_evaluated) {
r_action_strip->flag |= NLASTRIP_FLAG_MUTED;
}
@@ -2779,13 +3359,13 @@ static bool animsys_evaluate_nla_for_flush(NlaEvalData *echannels,
/* Per strip, evaluate and accumulate on top of existing channels. */
for (nes = estrips.first; nes; nes = nes->next) {
- nlastrip_evaluate(ptr,
- echannels,
- NULL,
- nes,
- &echannels->eval_snapshot,
- anim_eval_context,
- flush_to_original);
+ nlasnapshot_blend_strip(ptr,
+ echannels,
+ NULL,
+ nes,
+ &echannels->eval_snapshot,
+ anim_eval_context,
+ flush_to_original);
}
/* Free temporary evaluation data that's not used elsewhere. */
@@ -2816,6 +3396,7 @@ static void animsys_evaluate_nla_for_keyframing(PointerRNA *ptr,
short track_index = 0;
bool has_strips = false;
+ ListBase *upper_estrips = &r_context->upper_estrips;
ListBase lower_estrips = {NULL, NULL};
NlaEvalStrip *nes;
@@ -2845,6 +3426,30 @@ static void animsys_evaluate_nla_for_keyframing(PointerRNA *ptr,
}
}
+ /* Get the upper stack of strips to evaluate at current time (influence calculated here).
+ * Var nlt exists only if tweak strip exists. */
+ if (nlt) {
+
+ /* Skip tweaked strip. */
+ nlt = nlt->next;
+ track_index++;
+
+ for (; nlt; nlt = nlt->next, track_index++) {
+
+ if (!is_nlatrack_evaluatable(adt, nlt)) {
+ continue;
+ }
+
+ if (nlt->strips.first) {
+ has_strips = true;
+ }
+
+ /* Get strip to evaluate for this channel. */
+ nes = nlastrips_ctime_get_strip(
+ upper_estrips, &nlt->strips, track_index, anim_eval_context, false);
+ }
+ }
+
/** NOTE: Although we early out, we can still keyframe to the non-pushed action since the
* keyframe remap function detects (r_context->strip.act == NULL) and will keyframe without
* remapping.
@@ -2856,6 +3461,10 @@ static void animsys_evaluate_nla_for_keyframing(PointerRNA *ptr,
/* Write r_context->eval_strip. */
if (adt->flag & ADT_NLA_EDIT_ON) {
+ /* Append action_track_strip to upper estrips. */
+ NlaStrip *action_strip = &r_context->action_track_strip;
+ animsys_create_action_track_strip(adt, false, action_strip);
+ nlastrips_ctime_get_strip_single(upper_estrips, action_strip, anim_eval_context, false);
NlaStrip *tweak_strip = &r_context->strip;
animsys_create_tweak_strip(adt, true, tweak_strip);
@@ -2885,13 +3494,13 @@ static void animsys_evaluate_nla_for_keyframing(PointerRNA *ptr,
/* For each strip, evaluate then accumulate on top of existing channels. */
for (nes = lower_estrips.first; nes; nes = nes->next) {
- nlastrip_evaluate(ptr,
- &r_context->lower_eval_data,
- NULL,
- nes,
- &r_context->lower_eval_data.eval_snapshot,
- anim_eval_context,
- false);
+ nlasnapshot_blend_strip(ptr,
+ &r_context->lower_eval_data,
+ NULL,
+ nes,
+ &r_context->lower_eval_data.eval_snapshot,
+ anim_eval_context,
+ false);
}
/* Free temporary evaluation data that's not used elsewhere. */
@@ -3009,6 +3618,41 @@ void nlasnapshot_blend_get_inverted_upper_snapshot(NlaEvalData *eval_data,
}
}
+/** Using \a blended_snapshot and \a upper_snapshot, we can solve for the \a r_lower_snapshot.
+ *
+ * Only channels that exist within \a blended_snapshot are processed.
+ * Only blended values within the \a remap_domain are processed.
+ *
+ * Writes to \a r_upper_snapshot NlaEvalChannelSnapshot->remap_domain to match remapping success.
+ *
+ * Assumes caller marked upper values that are in the \a blend_domain. This determines whether the
+ * blended value came directly from the lower snapshot or a result of blending.
+ **/
+void nlasnapshot_blend_get_inverted_lower_snapshot(NlaEvalData *eval_data,
+ NlaEvalSnapshot *blended_snapshot,
+ NlaEvalSnapshot *upper_snapshot,
+ const short upper_blendmode,
+ const float upper_influence,
+ NlaEvalSnapshot *r_lower_snapshot)
+{
+ nlaeval_snapshot_ensure_size(r_lower_snapshot, eval_data->num_channels);
+
+ LISTBASE_FOREACH (NlaEvalChannel *, nec, &eval_data->channels) {
+ NlaEvalChannelSnapshot *blended_necs = nlaeval_snapshot_get(blended_snapshot, nec->index);
+ if (blended_necs == NULL) {
+ /* We assume the caller only wants a subset of channels to be inverted, those that exist
+ * within \a blended_snapshot. */
+ continue;
+ }
+
+ NlaEvalChannelSnapshot *upper_necs = nlaeval_snapshot_get(upper_snapshot, nec->index);
+ NlaEvalChannelSnapshot *result_necs = nlaeval_snapshot_ensure_channel(r_lower_snapshot, nec);
+
+ nlaevalchan_blendOrCombine_get_inverted_lower_evalchan(
+ blended_necs, upper_necs, upper_blendmode, upper_influence, result_necs);
+ }
+}
+
/* ---------------------- */
NlaKeyframingContext *BKE_animsys_get_nla_keyframing_context(
@@ -3023,9 +3667,11 @@ NlaKeyframingContext *BKE_animsys_get_nla_keyframing_context(
return NULL;
}
- /* No remapping if editing an ordinary Replace action with full influence. */
+ /* No remapping if editing an ordinary Replace action with full influence and upper tracks not
+ * evaluated. */
if (!(adt->flag & ADT_NLA_EDIT_ON) &&
- (adt->act_blendmode == NLASTRIP_MODE_REPLACE && adt->act_influence == 1.0f)) {
+ (adt->act_blendmode == NLASTRIP_MODE_REPLACE && adt->act_influence == 1.0f) &&
+ (adt->flag & ADT_NLA_EVAL_UPPER_TRACKS) == 0) {
return NULL;
}
@@ -3047,39 +3693,60 @@ NlaKeyframingContext *BKE_animsys_get_nla_keyframing_context(
return ctx;
}
-bool BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context,
+void BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context,
struct PointerRNA *prop_ptr,
struct PropertyRNA *prop,
float *values,
int count,
int index,
- bool *r_force_all)
+ const struct AnimationEvalContext *anim_eval_context,
+ bool *r_force_all,
+ BLI_bitmap *r_successful_remaps)
{
+ BLI_bitmap_set_all(r_successful_remaps, false, count);
+
if (r_force_all != NULL) {
*r_force_all = false;
}
+ BLI_bitmap *remap_domain = BLI_BITMAP_NEW(count, __func__);
+ for (int i = 0; i < count; i++) {
+ if (!ELEM(index, i, -1)) {
+ continue;
+ }
+
+ BLI_BITMAP_ENABLE(remap_domain, i);
+ }
+
/* No context means no correction. */
if (context == NULL || context->strip.act == NULL) {
- return true;
+ BLI_bitmap_copy_all(r_successful_remaps, remap_domain, count);
+ MEM_freeN(remap_domain);
+ return;
}
/* If the strip is not evaluated, it is the same as zero influence. */
if (context->eval_strip == NULL) {
- return false;
+ MEM_freeN(remap_domain);
+ return;
}
- /* Full influence Replace strips also require no correction. */
+ /* Full influence Replace strips also require no correction if there are no upper tracks
+ * evaluating. */
int blend_mode = context->strip.blendmode;
float influence = context->strip.influence;
- if (blend_mode == NLASTRIP_MODE_REPLACE && influence == 1.0f) {
- return true;
+ if (blend_mode == NLASTRIP_MODE_REPLACE && influence == 1.0f &&
+ BLI_listbase_is_empty(&context->upper_estrips)) {
+ BLI_bitmap_copy_all(r_successful_remaps, remap_domain, count);
+ MEM_freeN(remap_domain);
+ return;
}
/* Zero influence is division by zero. */
if (influence <= 0.0f) {
- return false;
+ MEM_freeN(remap_domain);
+ return;
}
/** Create \a blended_snapshot and fill with input \a values. */
@@ -3097,12 +3764,38 @@ bool BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context,
if (nec->base_snapshot.length != count) {
BLI_assert_msg(0, "invalid value count");
nlaeval_snapshot_free_data(&blended_snapshot);
- return false;
+ MEM_freeN(remap_domain);
+ return;
}
NlaEvalChannelSnapshot *blended_necs = nlaeval_snapshot_ensure_channel(&blended_snapshot, nec);
memcpy(blended_necs->values, values, sizeof(float) * count);
- BLI_bitmap_set_all(blended_necs->remap_domain.ptr, true, count);
+
+ /* Force all channels to be remapped for quaternions in a Combine strip, otherwise it will
+ * always fail. See nlaevalchan_combine_quaternion_handle_undefined_blend_values().
+ */
+ const bool can_force_all = r_force_all != NULL;
+ if (blended_necs->channel->mix_mode == NEC_MIX_QUATERNION &&
+ blend_mode == NLASTRIP_MODE_COMBINE && can_force_all) {
+
+ *r_force_all = true;
+ index = -1;
+ BLI_bitmap_set_all(remap_domain, true, 4);
+ }
+
+ BLI_bitmap_copy_all(blended_necs->remap_domain.ptr, remap_domain, count);
+
+ /* Need to send id_ptr instead of prop_ptr so fcurve RNA paths resolve properly. */
+ PointerRNA id_ptr;
+ RNA_id_pointer_create(prop_ptr->owner_id, &id_ptr);
+
+ /* Per iteration, remove effect of upper strip which gives output of nla stack below it. */
+ LISTBASE_FOREACH_BACKWARD (NlaEvalStrip *, nes, &context->upper_estrips) {
+ /* This will disable blended_necs->remap_domain bits if an upper strip is not invertible
+ * (full replace, multiply zero, or transition). Then there is no remap solution. */
+ nlasnapshot_blend_strip_get_inverted_lower_snapshot(
+ &id_ptr, eval_data, NULL, nes, &blended_snapshot, anim_eval_context);
+ }
/** Remove lower NLA stack effects. */
nlasnapshot_blend_get_inverted_upper_snapshot(eval_data,
@@ -3112,40 +3805,25 @@ bool BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context,
influence,
&blended_snapshot);
- /** Write results into \a values. */
- bool successful_remap = true;
- if (blended_necs->channel->mix_mode == NEC_MIX_QUATERNION &&
- blend_mode == NLASTRIP_MODE_COMBINE) {
-
- if (r_force_all != NULL) {
- *r_force_all = true;
- index = -1;
- }
- else {
- successful_remap = false;
- }
- }
-
+ /* Write results into \a values for successfully remapped values. */
for (int i = 0; i < count; i++) {
- if (!ELEM(index, i, -1)) {
- continue;
- }
if (!BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, i)) {
- successful_remap = false;
+ continue;
}
-
values[i] = blended_necs->values[i];
}
- nlaeval_snapshot_free_data(&blended_snapshot);
+ BLI_bitmap_copy_all(r_successful_remaps, blended_necs->remap_domain.ptr, blended_necs->length);
- return successful_remap;
+ nlaeval_snapshot_free_data(&blended_snapshot);
+ MEM_freeN(remap_domain);
}
void BKE_animsys_free_nla_keyframing_context_cache(struct ListBase *cache)
{
LISTBASE_FOREACH (NlaKeyframingContext *, ctx, cache) {
MEM_SAFE_FREE(ctx->eval_strip);
+ BLI_freelistN(&ctx->upper_estrips);
nlaeval_free(&ctx->lower_eval_data);
}
diff --git a/source/blender/blenkernel/intern/armature_update.c b/source/blender/blenkernel/intern/armature_update.c
index 361ab176abd..2db4c086e04 100644
--- a/source/blender/blenkernel/intern/armature_update.c
+++ b/source/blender/blenkernel/intern/armature_update.c
@@ -405,8 +405,8 @@ static void splineik_evaluate_bone(
if (pchan->bone->length < FLT_EPSILON) {
/* Only move the bone position with zero length bones. */
- float bone_pos[4], dir[3], rad;
- BKE_where_on_path(ik_data->tar, state->curve_position, bone_pos, dir, NULL, &rad, NULL);
+ float bone_pos[4], rad;
+ BKE_where_on_path(ik_data->tar, state->curve_position, bone_pos, NULL, NULL, &rad, NULL);
apply_curve_transform(ik_data, ob, rad, bone_pos, &rad);
@@ -445,13 +445,13 @@ static void splineik_evaluate_bone(
/* Step 1: determine the positions for the endpoints of the bone. */
if (point_start < 1.0f) {
- float vec[4], dir[3], rad;
+ float vec[4], rad;
radius = 0.0f;
/* Calculate head position. */
if (point_start == 0.0f) {
/* Start of the path. We have no previous tail position to copy. */
- BKE_where_on_path(ik_data->tar, point_start, vec, dir, NULL, &rad, NULL);
+ BKE_where_on_path(ik_data->tar, point_start, vec, NULL, NULL, &rad, NULL);
}
else {
copy_v3_v3(vec, state->prev_tail_loc);
@@ -486,7 +486,7 @@ static void splineik_evaluate_bone(
}
else {
/* Scale to fit curve end position. */
- if (BKE_where_on_path(ik_data->tar, point_end, vec, dir, NULL, &rad, NULL)) {
+ if (BKE_where_on_path(ik_data->tar, point_end, vec, NULL, NULL, &rad, NULL)) {
state->prev_tail_radius = rad;
copy_v3_v3(state->prev_tail_loc, vec);
copy_v3_v3(pose_tail, vec);
diff --git a/source/blender/blenkernel/intern/attribute.c b/source/blender/blenkernel/intern/attribute.c
index d8c7c3c6dd7..307868ce6d9 100644
--- a/source/blender/blenkernel/intern/attribute.c
+++ b/source/blender/blenkernel/intern/attribute.c
@@ -292,7 +292,7 @@ int BKE_id_attributes_length(const ID *id, AttributeDomainMask domain_mask, Cust
return length;
}
-AttributeDomain BKE_id_attribute_domain(ID *id, const CustomDataLayer *layer)
+AttributeDomain BKE_id_attribute_domain(const ID *id, const CustomDataLayer *layer)
{
DomainInfo info[ATTR_DOMAIN_NUM];
get_domains(id, info);
@@ -451,7 +451,7 @@ CustomDataLayer *BKE_id_attribute_from_index(ID *id,
for (int i = 0; i < customdata->totlayer; i++) {
if (!(layer_mask & CD_TYPE_AS_MASK(customdata->layers[i].type)) ||
- (CD_TYPE_AS_MASK(customdata->layers[i].type) & CD_FLAG_TEMPORARY)) {
+ (customdata->layers[i].flag & CD_FLAG_TEMPORARY)) {
continue;
}
@@ -504,7 +504,7 @@ int BKE_id_attribute_to_index(const struct ID *id,
CustomDataLayer *layer_iter = cdata->layers + j;
if (!(CD_TYPE_AS_MASK(layer_iter->type) & layer_mask) ||
- (CD_TYPE_AS_MASK(layer_iter->type) & CD_FLAG_TEMPORARY)) {
+ (layer_iter->flag & CD_FLAG_TEMPORARY)) {
continue;
}
@@ -541,8 +541,7 @@ CustomDataLayer *BKE_id_attribute_subset_active_get(const ID *id,
for (int j = 0; j < cdata->totlayer; j++) {
CustomDataLayer *layer = cdata->layers + j;
- if (!(CD_TYPE_AS_MASK(layer->type) & mask) ||
- (CD_TYPE_AS_MASK(layer->type) & CD_FLAG_TEMPORARY)) {
+ if (!(CD_TYPE_AS_MASK(layer->type) & mask) || (layer->flag & CD_FLAG_TEMPORARY)) {
continue;
}
@@ -581,8 +580,7 @@ void BKE_id_attribute_subset_active_set(ID *id,
for (int j = 0; j < cdata->totlayer; j++) {
CustomDataLayer *layer_iter = cdata->layers + j;
- if (!(CD_TYPE_AS_MASK(layer_iter->type) & mask) ||
- (CD_TYPE_AS_MASK(layer_iter->type) & CD_FLAG_TEMPORARY)) {
+ if (!(CD_TYPE_AS_MASK(layer_iter->type) & mask) || (layer_iter->flag & CD_FLAG_TEMPORARY)) {
continue;
}
diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc
index d0420b4170a..0ae9fa4356b 100644
--- a/source/blender/blenkernel/intern/attribute_access.cc
+++ b/source/blender/blenkernel/intern/attribute_access.cc
@@ -746,15 +746,14 @@ bool CustomDataAttributes::create_by_move(const AttributeIDRef &attribute_id,
bool CustomDataAttributes::remove(const AttributeIDRef &attribute_id)
{
- bool result = false;
for (const int i : IndexRange(data.totlayer)) {
const CustomDataLayer &layer = data.layers[i];
if (custom_data_layer_matches_attribute_id(layer, attribute_id)) {
CustomData_free_layer(&data, layer.type, size_, i);
- result = true;
+ return true;
}
}
- return result;
+ return false;
}
void CustomDataAttributes::reallocate(const int size)
diff --git a/source/blender/blenkernel/intern/blendfile_link_append.c b/source/blender/blenkernel/intern/blendfile_link_append.c
index be2abdbfb69..da48365a43e 100644
--- a/source/blender/blenkernel/intern/blendfile_link_append.c
+++ b/source/blender/blenkernel/intern/blendfile_link_append.c
@@ -1187,6 +1187,11 @@ void BKE_blendfile_append(BlendfileLinkAppendContext *lapp_context, ReportList *
BLI_assert(ID_IS_LINKED(id));
BLI_assert(id->newid != NULL);
+ /* Do NOT delete a linked data that was already linked before this append. */
+ if (id->tag & LIB_TAG_PRE_EXISTING) {
+ continue;
+ }
+
id->tag |= LIB_TAG_DOIT;
item->new_id = id->newid;
}
diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c
index 3897df9f05f..32925168437 100644
--- a/source/blender/blenkernel/intern/camera.c
+++ b/source/blender/blenkernel/intern/camera.c
@@ -698,12 +698,12 @@ static bool camera_frame_fit_calc_from_data(CameraParams *params,
add_v3_v3v3(plane_isect_1_other, plane_isect_1, plane_isect_1_no);
add_v3_v3v3(plane_isect_2_other, plane_isect_2, plane_isect_2_no);
- if (!isect_line_line_v3(plane_isect_1,
- plane_isect_1_other,
- plane_isect_2,
- plane_isect_2_other,
- plane_isect_pt_1,
- plane_isect_pt_2) != 0) {
+ if (isect_line_line_v3(plane_isect_1,
+ plane_isect_1_other,
+ plane_isect_2,
+ plane_isect_2_other,
+ plane_isect_pt_1,
+ plane_isect_pt_2) == 0) {
return false;
}
diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c
index 939f7c7b3ff..0edc16e822c 100644
--- a/source/blender/blenkernel/intern/collection.c
+++ b/source/blender/blenkernel/intern/collection.c
@@ -360,7 +360,7 @@ IDTypeInfo IDType_ID_GR = {
.name = "Collection",
.name_plural = "collections",
.translation_context = BLT_I18NCONTEXT_ID_COLLECTION,
- .flags = IDTYPE_FLAGS_NO_ANIMDATA,
+ .flags = IDTYPE_FLAGS_NO_ANIMDATA | IDTYPE_FLAGS_APPEND_IS_REUSABLE,
.asset_type_info = NULL,
.init_data = collection_init_data,
diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c
index e85524d4bcb..35f2f94bc91 100644
--- a/source/blender/blenkernel/intern/constraint.c
+++ b/source/blender/blenkernel/intern/constraint.c
@@ -1495,7 +1495,7 @@ static void followpath_get_tarmat(struct Depsgraph *UNUSED(depsgraph),
if (VALID_CONS_TARGET(ct) && (ct->tar->type == OB_CURVES_LEGACY)) {
Curve *cu = ct->tar->data;
- float vec[4], dir[3], radius;
+ float vec[4], radius;
float curvetime;
unit_m4(ct->matrix);
@@ -1532,7 +1532,7 @@ static void followpath_get_tarmat(struct Depsgraph *UNUSED(depsgraph),
if (BKE_where_on_path(ct->tar,
curvetime,
vec,
- dir,
+ NULL,
(data->followflag & FOLLOWPATH_FOLLOW) ? quat : NULL,
&radius,
NULL)) { /* quat_pt is quat or NULL. */
@@ -3886,7 +3886,7 @@ static void clampto_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar
/* get targetmatrix */
if (data->tar->runtime.curve_cache && data->tar->runtime.curve_cache->anim_path_accum_length) {
- float vec[4], dir[3], totmat[4][4];
+ float vec[4], totmat[4][4];
float curvetime;
short clamp_axis;
@@ -3969,7 +3969,7 @@ static void clampto_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar
}
/* 3. position on curve */
- if (BKE_where_on_path(ct->tar, curvetime, vec, dir, NULL, NULL, NULL)) {
+ if (BKE_where_on_path(ct->tar, curvetime, vec, NULL, NULL, NULL, NULL)) {
unit_m4(totmat);
copy_v3_v3(totmat[3], vec);
diff --git a/source/blender/blenkernel/intern/curve.cc b/source/blender/blenkernel/intern/curve.cc
index 2d72ad28d18..6815b4d7486 100644
--- a/source/blender/blenkernel/intern/curve.cc
+++ b/source/blender/blenkernel/intern/curve.cc
@@ -665,7 +665,7 @@ Nurb *BKE_nurb_duplicate(const Nurb *nu)
if (newnu == nullptr) {
return nullptr;
}
- memcpy(newnu, nu, sizeof(Nurb));
+ *newnu = blender::dna::shallow_copy(*nu);
if (nu->bezt) {
newnu->bezt = (BezTriple *)MEM_malloc_arrayN(nu->pntsu, sizeof(BezTriple), "duplicateNurb2");
@@ -699,7 +699,7 @@ Nurb *BKE_nurb_duplicate(const Nurb *nu)
Nurb *BKE_nurb_copy(Nurb *src, int pntsu, int pntsv)
{
Nurb *newnu = (Nurb *)MEM_mallocN(sizeof(Nurb), "copyNurb");
- memcpy(newnu, src, sizeof(Nurb));
+ *newnu = blender::dna::shallow_copy(*src);
if (pntsu == 1) {
SWAP(int, pntsu, pntsv);
diff --git a/source/blender/blenkernel/intern/curve_bezier.cc b/source/blender/blenkernel/intern/curve_bezier.cc
index dfe462d8566..13695525616 100644
--- a/source/blender/blenkernel/intern/curve_bezier.cc
+++ b/source/blender/blenkernel/intern/curve_bezier.cc
@@ -281,6 +281,11 @@ static void interpolate_to_evaluated(const Span<T> src,
BLI_assert(!src.is_empty());
BLI_assert(evaluated_offsets.size() == src.size());
BLI_assert(evaluated_offsets.last() == dst.size());
+ if (src.size() == 1) {
+ BLI_assert(dst.size() == 1);
+ dst.first() = src.first();
+ return;
+ }
linear_interpolation(src.first(), src[1], dst.take_front(evaluated_offsets.first()));
diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc
index 6e09d1e8f10..d1ec9499298 100644
--- a/source/blender/blenkernel/intern/curve_eval.cc
+++ b/source/blender/blenkernel/intern/curve_eval.cc
@@ -385,8 +385,8 @@ std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves)
VArray_Span<float> nurbs_weights{
src_component.attribute_get_for_read<float>("nurbs_weight", ATTR_DOMAIN_POINT, 0.0f)};
- VArray_Span<int> nurbs_orders{
- src_component.attribute_get_for_read<int>("nurbs_order", ATTR_DOMAIN_CURVE, 4)};
+ VArray_Span<int8_t> nurbs_orders{
+ src_component.attribute_get_for_read<int8_t>("nurbs_order", ATTR_DOMAIN_CURVE, 4)};
VArray_Span<int8_t> nurbs_knots_modes{
src_component.attribute_get_for_read<int8_t>("knots_mode", ATTR_DOMAIN_CURVE, 0)};
@@ -402,7 +402,8 @@ std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves)
const IndexRange point_range = geometry.points_for_curve(curve_index);
std::unique_ptr<Spline> spline;
- switch (curve_types[curve_index]) {
+ /* #CurveEval does not support catmull rom curves, so convert those to poly splines. */
+ switch (std::max<int8_t>(1, curve_types[curve_index])) {
case CURVE_TYPE_POLY: {
spline = std::make_unique<PolySpline>();
spline->resize(point_range.size());
diff --git a/source/blender/blenkernel/intern/curve_poly.cc b/source/blender/blenkernel/intern/curve_poly.cc
index b0ed62d38dd..2db7cd71ad3 100644
--- a/source/blender/blenkernel/intern/curve_poly.cc
+++ b/source/blender/blenkernel/intern/curve_poly.cc
@@ -6,7 +6,7 @@
#include <algorithm>
-#include "BLI_math_vector.h"
+#include "BLI_math_rotation.hh"
#include "BLI_math_vector.hh"
#include "BKE_curves.hh"
@@ -54,20 +54,6 @@ void calculate_tangents(const Span<float3> positions,
}
}
-static float3 rotate_direction_around_axis(const float3 &direction,
- const float3 &axis,
- const float angle)
-{
- BLI_ASSERT_UNIT_V3(direction);
- BLI_ASSERT_UNIT_V3(axis);
-
- const float3 axis_scaled = axis * math::dot(direction, axis);
- const float3 diff = direction - axis_scaled;
- const float3 cross = math::cross(axis, diff);
-
- return axis_scaled + diff * std::cos(angle) + cross * std::sin(angle);
-}
-
void calculate_normals_z_up(const Span<float3> tangents, MutableSpan<float3> normals)
{
BLI_assert(normals.size() == tangents.size());
@@ -98,7 +84,7 @@ static float3 calculate_next_normal(const float3 &last_normal,
const float angle = angle_normalized_v3v3(last_tangent, current_tangent);
if (angle != 0.0) {
const float3 axis = math::normalize(math::cross(last_tangent, current_tangent));
- return rotate_direction_around_axis(last_normal, axis, angle);
+ return math::rotate_direction_around_axis(last_normal, axis, angle);
}
return last_normal;
}
@@ -147,7 +133,7 @@ void calculate_normals_minimum(const Span<float3> tangents,
const float angle_step = correction_angle / normals.size();
for (const int i : normals.index_range()) {
const float angle = angle_step * i;
- normals[i] = rotate_direction_around_axis(normals[i], tangents[i], angle);
+ normals[i] = math::rotate_direction_around_axis(normals[i], tangents[i], angle);
}
}
diff --git a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc
index 9b22a4c9726..c48d155f5ce 100644
--- a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc
+++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc
@@ -9,65 +9,15 @@
#include "BKE_attribute_access.hh"
#include "BKE_attribute_math.hh"
+#include "BKE_curves.hh"
#include "BKE_geometry_set.hh"
#include "BKE_material.h"
#include "BKE_mesh.h"
-#include "BKE_spline.hh"
#include "BKE_curve_to_mesh.hh"
namespace blender::bke {
-/** Information about the creation of one curve spline and profile spline combination. */
-struct ResultInfo {
- const Spline &spline;
- const Spline &profile;
- int vert_offset;
- int edge_offset;
- int loop_offset;
- int poly_offset;
- int spline_vert_len;
- int spline_edge_len;
- int profile_vert_len;
- int profile_edge_len;
-};
-
-static void vert_extrude_to_mesh_data(const Spline &spline,
- const float3 profile_vert,
- MutableSpan<MVert> r_verts,
- MutableSpan<MEdge> r_edges,
- const int vert_offset,
- const int edge_offset)
-{
- const int eval_size = spline.evaluated_points_size();
- for (const int i : IndexRange(eval_size - 1)) {
- MEdge &edge = r_edges[edge_offset + i];
- edge.v1 = vert_offset + i;
- edge.v2 = vert_offset + i + 1;
- edge.flag = ME_LOOSEEDGE;
- }
-
- if (spline.is_cyclic() && spline.evaluated_edges_size() > 1) {
- MEdge &edge = r_edges[edge_offset + spline.evaluated_edges_size() - 1];
- edge.v1 = vert_offset + eval_size - 1;
- edge.v2 = vert_offset;
- edge.flag = ME_LOOSEEDGE;
- }
-
- Span<float3> positions = spline.evaluated_positions();
- Span<float3> tangents = spline.evaluated_tangents();
- Span<float3> normals = spline.evaluated_normals();
- VArray<float> radii = spline.interpolate_to_evaluated(spline.radii());
- for (const int i : IndexRange(eval_size)) {
- float4x4 point_matrix = float4x4::from_normalized_axis_data(
- positions[i], normals[i], tangents[i]);
- point_matrix.apply_scale(radii[i]);
-
- MVert &vert = r_verts[vert_offset + i];
- copy_v3_v3(vert.co, point_matrix * profile_vert);
- }
-}
-
static void mark_edges_sharp(MutableSpan<MEdge> edges)
{
for (MEdge &edge : edges) {
@@ -75,36 +25,50 @@ static void mark_edges_sharp(MutableSpan<MEdge> edges)
}
}
-static void spline_extrude_to_mesh_data(const ResultInfo &info,
- const bool fill_caps,
- MutableSpan<MVert> r_verts,
- MutableSpan<MEdge> r_edges,
- MutableSpan<MLoop> r_loops,
- MutableSpan<MPoly> r_polys)
+static void fill_mesh_topology(const int vert_offset,
+ const int edge_offset,
+ const int poly_offset,
+ const int loop_offset,
+ const int main_point_num,
+ const int profile_point_num,
+ const bool main_cyclic,
+ const bool profile_cyclic,
+ const bool fill_caps,
+ MutableSpan<MEdge> edges,
+ MutableSpan<MLoop> loops,
+ MutableSpan<MPoly> polys)
{
- const Spline &spline = info.spline;
- const Spline &profile = info.profile;
- if (info.profile_vert_len == 1) {
- vert_extrude_to_mesh_data(spline,
- profile.evaluated_positions()[0],
- r_verts,
- r_edges,
- info.vert_offset,
- info.edge_offset);
+ const int main_segment_num = curves::curve_segment_size(main_point_num, main_cyclic);
+ const int profile_segment_num = curves::curve_segment_size(profile_point_num, profile_cyclic);
+
+ if (profile_point_num == 1) {
+ for (const int i : IndexRange(main_point_num - 1)) {
+ MEdge &edge = edges[edge_offset + i];
+ edge.v1 = vert_offset + i;
+ edge.v2 = vert_offset + i + 1;
+ edge.flag = ME_LOOSEEDGE;
+ }
+
+ if (main_cyclic && main_segment_num > 1) {
+ MEdge &edge = edges[edge_offset + main_segment_num - 1];
+ edge.v1 = vert_offset + main_point_num - 1;
+ edge.v2 = vert_offset;
+ edge.flag = ME_LOOSEEDGE;
+ }
return;
}
/* Add the edges running along the length of the curve, starting at each profile vertex. */
- const int spline_edges_start = info.edge_offset;
- for (const int i_profile : IndexRange(info.profile_vert_len)) {
- const int profile_edge_offset = spline_edges_start + i_profile * info.spline_edge_len;
- for (const int i_ring : IndexRange(info.spline_edge_len)) {
- const int i_next_ring = (i_ring == info.spline_vert_len - 1) ? 0 : i_ring + 1;
+ const int main_edges_start = edge_offset;
+ for (const int i_profile : IndexRange(profile_point_num)) {
+ const int profile_edge_offset = main_edges_start + i_profile * main_segment_num;
+ for (const int i_ring : IndexRange(main_segment_num)) {
+ const int i_next_ring = (i_ring == main_point_num - 1) ? 0 : i_ring + 1;
- const int ring_vert_offset = info.vert_offset + info.profile_vert_len * i_ring;
- const int next_ring_vert_offset = info.vert_offset + info.profile_vert_len * i_next_ring;
+ const int ring_vert_offset = vert_offset + profile_point_num * i_ring;
+ const int next_ring_vert_offset = vert_offset + profile_point_num * i_next_ring;
- MEdge &edge = r_edges[profile_edge_offset + i_ring];
+ MEdge &edge = edges[profile_edge_offset + i_ring];
edge.v1 = ring_vert_offset + i_profile;
edge.v2 = next_ring_vert_offset + i_profile;
edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
@@ -112,16 +76,15 @@ static void spline_extrude_to_mesh_data(const ResultInfo &info,
}
/* Add the edges running along each profile ring. */
- const int profile_edges_start = spline_edges_start +
- info.profile_vert_len * info.spline_edge_len;
- for (const int i_ring : IndexRange(info.spline_vert_len)) {
- const int ring_vert_offset = info.vert_offset + info.profile_vert_len * i_ring;
+ const int profile_edges_start = main_edges_start + profile_point_num * main_segment_num;
+ for (const int i_ring : IndexRange(main_point_num)) {
+ const int ring_vert_offset = vert_offset + profile_point_num * i_ring;
- const int ring_edge_offset = profile_edges_start + i_ring * info.profile_edge_len;
- for (const int i_profile : IndexRange(info.profile_edge_len)) {
- const int i_next_profile = (i_profile == info.profile_vert_len - 1) ? 0 : i_profile + 1;
+ const int ring_edge_offset = profile_edges_start + i_ring * profile_segment_num;
+ for (const int i_profile : IndexRange(profile_segment_num)) {
+ const int i_next_profile = (i_profile == profile_point_num - 1) ? 0 : i_profile + 1;
- MEdge &edge = r_edges[ring_edge_offset + i_profile];
+ MEdge &edge = edges[ring_edge_offset + i_profile];
edge.v1 = ring_vert_offset + i_profile;
edge.v2 = ring_vert_offset + i_next_profile;
edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
@@ -129,368 +92,410 @@ static void spline_extrude_to_mesh_data(const ResultInfo &info,
}
/* Calculate poly and corner indices. */
- for (const int i_ring : IndexRange(info.spline_edge_len)) {
- const int i_next_ring = (i_ring == info.spline_vert_len - 1) ? 0 : i_ring + 1;
+ for (const int i_ring : IndexRange(main_segment_num)) {
+ const int i_next_ring = (i_ring == main_point_num - 1) ? 0 : i_ring + 1;
- const int ring_vert_offset = info.vert_offset + info.profile_vert_len * i_ring;
- const int next_ring_vert_offset = info.vert_offset + info.profile_vert_len * i_next_ring;
+ const int ring_vert_offset = vert_offset + profile_point_num * i_ring;
+ const int next_ring_vert_offset = vert_offset + profile_point_num * i_next_ring;
- const int ring_edge_start = profile_edges_start + info.profile_edge_len * i_ring;
- const int next_ring_edge_offset = profile_edges_start + info.profile_edge_len * i_next_ring;
+ const int ring_edge_start = profile_edges_start + profile_segment_num * i_ring;
+ const int next_ring_edge_offset = profile_edges_start + profile_segment_num * i_next_ring;
- const int ring_poly_offset = info.poly_offset + i_ring * info.profile_edge_len;
- const int ring_loop_offset = info.loop_offset + i_ring * info.profile_edge_len * 4;
+ const int ring_poly_offset = poly_offset + i_ring * profile_segment_num;
+ const int ring_loop_offset = loop_offset + i_ring * profile_segment_num * 4;
- for (const int i_profile : IndexRange(info.profile_edge_len)) {
+ for (const int i_profile : IndexRange(profile_segment_num)) {
const int ring_segment_loop_offset = ring_loop_offset + i_profile * 4;
- const int i_next_profile = (i_profile == info.profile_vert_len - 1) ? 0 : i_profile + 1;
+ const int i_next_profile = (i_profile == profile_point_num - 1) ? 0 : i_profile + 1;
- const int spline_edge_start = spline_edges_start + info.spline_edge_len * i_profile;
- const int next_spline_edge_start = spline_edges_start +
- info.spline_edge_len * i_next_profile;
+ const int main_edge_start = main_edges_start + main_segment_num * i_profile;
+ const int next_main_edge_start = main_edges_start + main_segment_num * i_next_profile;
- MPoly &poly = r_polys[ring_poly_offset + i_profile];
+ MPoly &poly = polys[ring_poly_offset + i_profile];
poly.loopstart = ring_segment_loop_offset;
poly.totloop = 4;
poly.flag = ME_SMOOTH;
- MLoop &loop_a = r_loops[ring_segment_loop_offset];
+ MLoop &loop_a = loops[ring_segment_loop_offset];
loop_a.v = ring_vert_offset + i_profile;
loop_a.e = ring_edge_start + i_profile;
- MLoop &loop_b = r_loops[ring_segment_loop_offset + 1];
+ MLoop &loop_b = loops[ring_segment_loop_offset + 1];
loop_b.v = ring_vert_offset + i_next_profile;
- loop_b.e = next_spline_edge_start + i_ring;
- MLoop &loop_c = r_loops[ring_segment_loop_offset + 2];
+ loop_b.e = next_main_edge_start + i_ring;
+ MLoop &loop_c = loops[ring_segment_loop_offset + 2];
loop_c.v = next_ring_vert_offset + i_next_profile;
loop_c.e = next_ring_edge_offset + i_profile;
- MLoop &loop_d = r_loops[ring_segment_loop_offset + 3];
+ MLoop &loop_d = loops[ring_segment_loop_offset + 3];
loop_d.v = next_ring_vert_offset + i_profile;
- loop_d.e = spline_edge_start + i_ring;
+ loop_d.e = main_edge_start + i_ring;
}
}
- const bool has_caps = fill_caps && profile.is_cyclic() && !spline.is_cyclic();
+ const bool has_caps = fill_caps && !main_cyclic && profile_cyclic;
if (has_caps) {
- const int poly_size = info.spline_edge_len * info.profile_edge_len;
- const int cap_loop_offset = info.loop_offset + poly_size * 4;
- const int cap_poly_offset = info.poly_offset + poly_size;
+ const int poly_size = main_segment_num * profile_segment_num;
+ const int cap_loop_offset = loop_offset + poly_size * 4;
+ const int cap_poly_offset = poly_offset + poly_size;
- MPoly &poly_start = r_polys[cap_poly_offset];
+ MPoly &poly_start = polys[cap_poly_offset];
poly_start.loopstart = cap_loop_offset;
- poly_start.totloop = info.profile_edge_len;
- MPoly &poly_end = r_polys[cap_poly_offset + 1];
- poly_end.loopstart = cap_loop_offset + info.profile_edge_len;
- poly_end.totloop = info.profile_edge_len;
-
- const int last_ring_index = info.spline_vert_len - 1;
- const int last_ring_vert_offset = info.vert_offset + info.profile_vert_len * last_ring_index;
- const int last_ring_edge_offset = profile_edges_start +
- info.profile_edge_len * last_ring_index;
-
- for (const int i : IndexRange(info.profile_edge_len)) {
- const int i_inv = info.profile_edge_len - i - 1;
- MLoop &loop_start = r_loops[cap_loop_offset + i];
- loop_start.v = info.vert_offset + i_inv;
- loop_start.e = profile_edges_start + ((i == (info.profile_edge_len - 1)) ?
- (info.profile_edge_len - 1) :
- (i_inv - 1));
- MLoop &loop_end = r_loops[cap_loop_offset + info.profile_edge_len + i];
+ poly_start.totloop = profile_segment_num;
+ MPoly &poly_end = polys[cap_poly_offset + 1];
+ poly_end.loopstart = cap_loop_offset + profile_segment_num;
+ poly_end.totloop = profile_segment_num;
+
+ const int last_ring_index = main_point_num - 1;
+ const int last_ring_vert_offset = vert_offset + profile_point_num * last_ring_index;
+ const int last_ring_edge_offset = profile_edges_start + profile_segment_num * last_ring_index;
+
+ for (const int i : IndexRange(profile_segment_num)) {
+ const int i_inv = profile_segment_num - i - 1;
+ MLoop &loop_start = loops[cap_loop_offset + i];
+ loop_start.v = vert_offset + i_inv;
+ loop_start.e = profile_edges_start +
+ ((i == (profile_segment_num - 1)) ? (profile_segment_num - 1) : (i_inv - 1));
+ MLoop &loop_end = loops[cap_loop_offset + profile_segment_num + i];
loop_end.v = last_ring_vert_offset + i;
loop_end.e = last_ring_edge_offset + i;
}
- mark_edges_sharp(r_edges.slice(profile_edges_start, info.profile_edge_len));
- mark_edges_sharp(r_edges.slice(last_ring_edge_offset, info.profile_edge_len));
+ mark_edges_sharp(edges.slice(profile_edges_start, profile_segment_num));
+ mark_edges_sharp(edges.slice(last_ring_edge_offset, profile_segment_num));
}
+}
- /* Calculate the positions of each profile ring profile along the spline. */
- Span<float3> positions = spline.evaluated_positions();
- Span<float3> tangents = spline.evaluated_tangents();
- Span<float3> normals = spline.evaluated_normals();
- Span<float3> profile_positions = profile.evaluated_positions();
-
- VArray<float> radii = spline.interpolate_to_evaluated(spline.radii());
- for (const int i_ring : IndexRange(info.spline_vert_len)) {
- float4x4 point_matrix = float4x4::from_normalized_axis_data(
- positions[i_ring], normals[i_ring], tangents[i_ring]);
- point_matrix.apply_scale(radii[i_ring]);
-
- const int ring_vert_start = info.vert_offset + i_ring * info.profile_vert_len;
- for (const int i_profile : IndexRange(info.profile_vert_len)) {
- MVert &vert = r_verts[ring_vert_start + i_profile];
- copy_v3_v3(vert.co, point_matrix * profile_positions[i_profile]);
- }
+static void mark_bezier_vector_edges_sharp(const int profile_point_num,
+ const int main_segment_num,
+ const Span<int> control_point_offsets,
+ const Span<int8_t> handle_types_left,
+ const Span<int8_t> handle_types_right,
+ MutableSpan<MEdge> edges)
+{
+ const int main_edges_start = 0;
+ if (curves::bezier::point_is_sharp(handle_types_left, handle_types_right, 0)) {
+ mark_edges_sharp(edges.slice(main_edges_start, main_segment_num));
}
- /* Mark edge loops from sharp vector control points sharp. */
- if (profile.type() == CURVE_TYPE_BEZIER) {
- const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(profile);
- Span<int> control_point_offsets = bezier_spline.control_point_offsets();
- for (const int i : IndexRange(bezier_spline.size())) {
- if (bezier_spline.point_is_sharp(i)) {
- mark_edges_sharp(
- r_edges.slice(spline_edges_start + info.spline_edge_len * control_point_offsets[i],
- info.spline_edge_len));
- }
+ for (const int i : IndexRange(profile_point_num).drop_front(1)) {
+ if (curves::bezier::point_is_sharp(handle_types_left, handle_types_right, i)) {
+ mark_edges_sharp(edges.slice(
+ main_edges_start + main_segment_num * control_point_offsets[i - 1], main_segment_num));
}
}
}
-static inline int spline_extrude_vert_size(const Spline &curve, const Spline &profile)
+static void fill_mesh_positions(const int main_point_num,
+ const int profile_point_num,
+ const Span<float3> main_positions,
+ const Span<float3> profile_positions,
+ const Span<float3> tangents,
+ const Span<float3> normals,
+ const Span<float> radii,
+ MutableSpan<MVert> mesh_positions)
{
- return curve.evaluated_points_size() * profile.evaluated_points_size();
-}
+ if (profile_point_num == 1) {
+ for (const int i_ring : IndexRange(main_point_num)) {
+ float4x4 point_matrix = float4x4::from_normalized_axis_data(
+ main_positions[i_ring], normals[i_ring], tangents[i_ring]);
+ if (!radii.is_empty()) {
+ point_matrix.apply_scale(radii[i_ring]);
+ }
-static inline int spline_extrude_edge_size(const Spline &curve, const Spline &profile)
-{
- /* Add the ring edges, with one ring for every curve vertex, and the edge loops
- * that run along the length of the curve, starting on the first profile. */
- return curve.evaluated_points_size() * profile.evaluated_edges_size() +
- curve.evaluated_edges_size() * profile.evaluated_points_size();
-}
+ MVert &vert = mesh_positions[i_ring];
+ copy_v3_v3(vert.co, point_matrix * profile_positions.first());
+ }
+ }
+ else {
+ for (const int i_ring : IndexRange(main_point_num)) {
+ float4x4 point_matrix = float4x4::from_normalized_axis_data(
+ main_positions[i_ring], normals[i_ring], tangents[i_ring]);
+ if (!radii.is_empty()) {
+ point_matrix.apply_scale(radii[i_ring]);
+ }
-static inline int spline_extrude_loop_size(const Spline &curve,
- const Spline &profile,
- const bool fill_caps)
-{
- const int tube = curve.evaluated_edges_size() * profile.evaluated_edges_size() * 4;
- const bool has_caps = fill_caps && profile.is_cyclic() && !curve.is_cyclic();
- const int caps = has_caps ? profile.evaluated_edges_size() * 2 : 0;
- return tube + caps;
+ const int ring_vert_start = i_ring * profile_point_num;
+ for (const int i_profile : IndexRange(profile_point_num)) {
+ MVert &vert = mesh_positions[ring_vert_start + i_profile];
+ copy_v3_v3(vert.co, point_matrix * profile_positions[i_profile]);
+ }
+ }
+ }
}
-static inline int spline_extrude_poly_size(const Spline &curve,
- const Spline &profile,
- const bool fill_caps)
+struct CurvesInfo {
+ const CurvesGeometry &main;
+ const CurvesGeometry &profile;
+
+ /* Make sure these are spans because they are potentially accessed many times. */
+ VArray_Span<bool> main_cyclic;
+ VArray_Span<bool> profile_cyclic;
+
+ /* TODO: Remove once these are cached on #CurvesGeometry. */
+ std::array<int, CURVE_TYPES_NUM> main_type_counts;
+ std::array<int, CURVE_TYPES_NUM> profile_type_counts;
+};
+static CurvesInfo get_curves_info(const CurvesGeometry &main, const CurvesGeometry &profile)
{
- const int tube = curve.evaluated_edges_size() * profile.evaluated_edges_size();
- const bool has_caps = fill_caps && profile.is_cyclic() && !curve.is_cyclic();
- const int caps = has_caps ? 2 : 0;
- return tube + caps;
+ return {main,
+ profile,
+ main.cyclic(),
+ profile.cyclic(),
+ main.count_curve_types(),
+ profile.count_curve_types()};
}
struct ResultOffsets {
+ /** The total number of curve combinations. */
+ int total;
+
+ /** Offsets into the result mesh for each combination. */
Array<int> vert;
Array<int> edge;
Array<int> loop;
Array<int> poly;
+
+ /* The indices of the main and profile curves that form each combination. */
+ Array<int> main_indices;
+ Array<int> profile_indices;
};
-static ResultOffsets calculate_result_offsets(Span<SplinePtr> profiles,
- Span<SplinePtr> curves,
- const bool fill_caps)
+static ResultOffsets calculate_result_offsets(const CurvesInfo &info, const bool fill_caps)
{
- const int total = profiles.size() * curves.size();
- Array<int> vert(total + 1);
- Array<int> edge(total + 1);
- Array<int> loop(total + 1);
- Array<int> poly(total + 1);
+ ResultOffsets result;
+ result.total = info.main.curves_num() * info.profile.curves_num();
+ result.vert.reinitialize(result.total + 1);
+ result.edge.reinitialize(result.total + 1);
+ result.loop.reinitialize(result.total + 1);
+ result.poly.reinitialize(result.total + 1);
+
+ result.main_indices.reinitialize(result.total);
+ result.profile_indices.reinitialize(result.total);
+
+ info.main.ensure_evaluated_offsets();
+ info.profile.ensure_evaluated_offsets();
int mesh_index = 0;
int vert_offset = 0;
int edge_offset = 0;
int loop_offset = 0;
int poly_offset = 0;
- for (const int i_spline : curves.index_range()) {
- for (const int i_profile : profiles.index_range()) {
- vert[mesh_index] = vert_offset;
- edge[mesh_index] = edge_offset;
- loop[mesh_index] = loop_offset;
- poly[mesh_index] = poly_offset;
- vert_offset += spline_extrude_vert_size(*curves[i_spline], *profiles[i_profile]);
- edge_offset += spline_extrude_edge_size(*curves[i_spline], *profiles[i_profile]);
- loop_offset += spline_extrude_loop_size(*curves[i_spline], *profiles[i_profile], fill_caps);
- poly_offset += spline_extrude_poly_size(*curves[i_spline], *profiles[i_profile], fill_caps);
+ for (const int i_main : info.main.curves_range()) {
+ const bool main_cyclic = info.main_cyclic[i_main];
+ const int main_point_num = info.main.evaluated_points_for_curve(i_main).size();
+ const int main_segment_num = curves::curve_segment_size(main_point_num, main_cyclic);
+ for (const int i_profile : info.profile.curves_range()) {
+ result.vert[mesh_index] = vert_offset;
+ result.edge[mesh_index] = edge_offset;
+ result.loop[mesh_index] = loop_offset;
+ result.poly[mesh_index] = poly_offset;
+
+ result.main_indices[mesh_index] = i_main;
+ result.profile_indices[mesh_index] = i_profile;
+
+ const bool profile_cyclic = info.profile_cyclic[i_profile];
+ const int profile_point_num = info.profile.evaluated_points_for_curve(i_profile).size();
+ const int profile_segment_num = curves::curve_segment_size(profile_point_num,
+ profile_cyclic);
+
+ const bool has_caps = fill_caps && !main_cyclic && profile_cyclic;
+ const int tube_face_num = main_segment_num * profile_segment_num;
+
+ vert_offset += main_point_num * profile_point_num;
+
+ /* Add the ring edges, with one ring for every curve vertex, and the edge loops
+ * that run along the length of the curve, starting on the first profile. */
+ edge_offset += main_point_num * profile_segment_num + main_segment_num * profile_point_num;
+
+ /* Add two cap N-gons for every ending. */
+ poly_offset += tube_face_num + (has_caps ? 2 : 0);
+
+ /* All faces on the tube are quads, and all cap faces are N-gons with an edge for each
+ * profile edge. */
+ loop_offset += tube_face_num * 4 + (has_caps ? profile_segment_num * 2 : 0);
+
mesh_index++;
}
}
- vert.last() = vert_offset;
- edge.last() = edge_offset;
- loop.last() = loop_offset;
- poly.last() = poly_offset;
- return {std::move(vert), std::move(edge), std::move(loop), std::move(poly)};
+ result.vert.last() = vert_offset;
+ result.edge.last() = edge_offset;
+ result.loop.last() = loop_offset;
+ result.poly.last() = poly_offset;
+
+ return result;
}
-static AttributeDomain get_result_attribute_domain(const MeshComponent &component,
- const AttributeIDRef &attribute_id)
+static AttributeDomain get_attribute_domain_for_mesh(const MeshComponent &mesh,
+ const AttributeIDRef &attribute_id)
{
/* Only use a different domain if it is builtin and must only exist on one domain. */
- if (!component.attribute_is_builtin(attribute_id)) {
+ if (!mesh.attribute_is_builtin(attribute_id)) {
return ATTR_DOMAIN_POINT;
}
- std::optional<AttributeMetaData> meta_data = component.attribute_get_meta_data(attribute_id);
+ std::optional<AttributeMetaData> meta_data = mesh.attribute_get_meta_data(attribute_id);
if (!meta_data) {
- /* This function has to return something in this case, but it shouldn't be used,
- * so return an output that will assert later if the code attempts to handle it. */
- return ATTR_DOMAIN_AUTO;
+ return ATTR_DOMAIN_POINT;
}
return meta_data->domain;
}
-/**
- * The data stored in the attribute and its domain from #OutputAttribute, to avoid calling
- * `as_span()` for every single profile and curve spline combination, and for readability.
- */
-struct ResultAttributeData {
- GMutableSpan data;
- AttributeDomain domain;
-};
-
-static std::optional<ResultAttributeData> create_attribute_and_get_span(
- MeshComponent &component,
- const AttributeIDRef &attribute_id,
- AttributeMetaData meta_data,
- Vector<OutputAttribute> &r_attributes)
+static bool should_add_attribute_to_mesh(const CurveComponent &curve_component,
+ const MeshComponent &mesh_component,
+ const AttributeIDRef &id)
{
- const AttributeDomain domain = get_result_attribute_domain(component, attribute_id);
- OutputAttribute attribute = component.attribute_try_get_for_output_only(
- attribute_id, domain, meta_data.data_type);
- if (!attribute) {
- return std::nullopt;
+ /* The position attribute has special non-generic evaluation. */
+ if (id.is_named() && id.name() == "position") {
+ return false;
+ }
+ /* Don't propagate built-in curves attributes that are not built-in on meshes. */
+ if (curve_component.attribute_is_builtin(id) && !mesh_component.attribute_is_builtin(id)) {
+ return false;
}
+ if (!id.should_be_kept()) {
+ return false;
+ }
+ return true;
+}
- GMutableSpan span = attribute.as_span();
- r_attributes.append(std::move(attribute));
- return std::make_optional<ResultAttributeData>({span, domain});
+static GSpan evaluated_attribute_if_necessary(const GVArray &src,
+ const CurvesGeometry &curves,
+ const Span<int> type_counts,
+ Vector<std::byte> &buffer)
+{
+ if (type_counts[CURVE_TYPE_POLY] == curves.curves_num() && src.is_span()) {
+ return src.get_internal_span();
+ }
+ buffer.reinitialize(curves.evaluated_points_num() * src.type().size());
+ GMutableSpan eval{src.type(), buffer.data(), curves.evaluated_points_num()};
+ curves.interpolate_to_evaluated(src.get_internal_span(), eval);
+ return eval;
}
-/**
- * Store the references to the attribute data from the curve and profile inputs. Here we rely on
- * the invariants of the storage of curve attributes, that the order will be consistent between
- * splines, and all splines will have the same attributes.
- */
-struct ResultAttributes {
- /**
- * Result attributes on the mesh corresponding to each attribute on the curve input, in the same
- * order. The data is optional only in case the attribute does not exist on the mesh for some
- * reason, like "shade_smooth" when the result has no faces.
- */
- Vector<std::optional<ResultAttributeData>> curve_point_attributes;
- Vector<std::optional<ResultAttributeData>> curve_spline_attributes;
-
- /**
- * Result attributes corresponding the attributes on the profile input, in the same order. The
- * attributes are optional in case the attribute names correspond to a names used by the curve
- * input, in which case the curve input attributes take precedence.
- */
- Vector<std::optional<ResultAttributeData>> profile_point_attributes;
- Vector<std::optional<ResultAttributeData>> profile_spline_attributes;
-
- /**
- * Because some builtin attributes are not stored contiguously, and the curve inputs might have
- * attributes with those names, it's necessary to keep OutputAttributes around to give access to
- * the result data in a contiguous array.
- */
- Vector<OutputAttribute> attributes;
+/** Information at a specific combination of main and profile curves. */
+struct CombinationInfo {
+ int i_main;
+ int i_profile;
+
+ IndexRange main_points;
+ IndexRange profile_points;
+
+ bool main_cyclic;
+ bool profile_cyclic;
+
+ int main_segment_num;
+ int profile_segment_num;
+
+ IndexRange vert_range;
+ IndexRange edge_range;
+ IndexRange poly_range;
+ IndexRange loop_range;
};
-static ResultAttributes create_result_attributes(const CurveEval &curve,
- const CurveEval &profile,
- MeshComponent &mesh_component)
+template<typename Fn>
+static void foreach_curve_combination(const CurvesInfo &info,
+ const ResultOffsets &offsets,
+ const Fn &fn)
{
- Set<AttributeIDRef> curve_attributes;
-
- /* In order to prefer attributes on the main curve input when there are name collisions, first
- * check the attributes on the curve, then add attributes on the profile that are not also on the
- * main curve input. */
- ResultAttributes result;
- curve.splines().first()->attributes.foreach_attribute(
- [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) {
- curve_attributes.add_new(id);
- result.curve_point_attributes.append(
- create_attribute_and_get_span(mesh_component, id, meta_data, result.attributes));
- return true;
- },
- ATTR_DOMAIN_POINT);
- curve.attributes.foreach_attribute(
- [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) {
- curve_attributes.add_new(id);
- result.curve_spline_attributes.append(
- create_attribute_and_get_span(mesh_component, id, meta_data, result.attributes));
- return true;
- },
- ATTR_DOMAIN_CURVE);
- profile.splines().first()->attributes.foreach_attribute(
- [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) {
- if (curve_attributes.contains(id)) {
- result.profile_point_attributes.append({});
- }
- else {
- result.profile_point_attributes.append(
- create_attribute_and_get_span(mesh_component, id, meta_data, result.attributes));
- }
- return true;
- },
- ATTR_DOMAIN_POINT);
- profile.attributes.foreach_attribute(
- [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) {
- if (curve_attributes.contains(id)) {
- result.profile_spline_attributes.append({});
- }
- else {
- result.profile_spline_attributes.append(
- create_attribute_and_get_span(mesh_component, id, meta_data, result.attributes));
- }
- return true;
- },
- ATTR_DOMAIN_CURVE);
-
- return result;
+ threading::parallel_for(IndexRange(offsets.total), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ const int i_main = offsets.main_indices[i];
+ const int i_profile = offsets.profile_indices[i];
+
+ const IndexRange main_points = info.main.evaluated_points_for_curve(i_main);
+ const IndexRange profile_points = info.profile.evaluated_points_for_curve(i_profile);
+
+ const bool main_cyclic = info.main_cyclic[i_main];
+ const bool profile_cyclic = info.profile_cyclic[i_profile];
+
+ /* Pass all information in a struct to avoid repeating arguments in many lambdas.
+ * The idea is that inlining `fn` will help avoid accessing unnecessary information,
+ * though that may or may not happen in practice. */
+ fn(CombinationInfo{i_main,
+ i_profile,
+ main_points,
+ profile_points,
+ main_cyclic,
+ profile_cyclic,
+ curves::curve_segment_size(main_points.size(), main_cyclic),
+ curves::curve_segment_size(profile_points.size(), profile_cyclic),
+ offsets_to_range(offsets.vert.as_span(), i),
+ offsets_to_range(offsets.edge.as_span(), i),
+ offsets_to_range(offsets.poly.as_span(), i),
+ offsets_to_range(offsets.loop.as_span(), i)});
+ }
+ });
}
template<typename T>
-static void copy_curve_point_data_to_mesh_verts(const Span<T> src,
- const ResultInfo &info,
- MutableSpan<T> dst)
+static void copy_main_point_data_to_mesh_verts(const Span<T> src,
+ const int profile_point_num,
+ MutableSpan<T> dst)
{
- for (const int i_ring : IndexRange(info.spline_vert_len)) {
- const int ring_vert_start = info.vert_offset + i_ring * info.profile_vert_len;
- dst.slice(ring_vert_start, info.profile_vert_len).fill(src[i_ring]);
+ for (const int i_ring : src.index_range()) {
+ const int ring_vert_start = i_ring * profile_point_num;
+ dst.slice(ring_vert_start, profile_point_num).fill(src[i_ring]);
}
}
template<typename T>
-static void copy_curve_point_data_to_mesh_edges(const Span<T> src,
- const ResultInfo &info,
- MutableSpan<T> dst)
+static void copy_main_point_data_to_mesh_edges(const Span<T> src,
+ const int profile_point_num,
+ const int main_segment_num,
+ const int profile_segment_num,
+ MutableSpan<T> dst)
{
- const int edges_start = info.edge_offset + info.profile_vert_len * info.spline_edge_len;
- for (const int i_ring : IndexRange(info.spline_vert_len)) {
- const int ring_edge_start = edges_start + info.profile_edge_len * i_ring;
- dst.slice(ring_edge_start, info.profile_edge_len).fill(src[i_ring]);
+ const int edges_start = profile_point_num * main_segment_num;
+ for (const int i_ring : src.index_range()) {
+ const int ring_edge_start = edges_start + profile_segment_num * i_ring;
+ dst.slice(ring_edge_start, profile_segment_num).fill(src[i_ring]);
}
}
template<typename T>
-static void copy_curve_point_data_to_mesh_faces(const Span<T> src,
- const ResultInfo &info,
- MutableSpan<T> dst)
+static void copy_main_point_data_to_mesh_faces(const Span<T> src,
+ const int main_segment_num,
+ const int profile_segment_num,
+ MutableSpan<T> dst)
{
- for (const int i_ring : IndexRange(info.spline_edge_len)) {
- const int ring_face_start = info.poly_offset + info.profile_edge_len * i_ring;
- dst.slice(ring_face_start, info.profile_edge_len).fill(src[i_ring]);
+ for (const int i_ring : IndexRange(main_segment_num)) {
+ const int ring_face_start = profile_segment_num * i_ring;
+ dst.slice(ring_face_start, profile_segment_num).fill(src[i_ring]);
}
}
-static void copy_curve_point_attribute_to_mesh(const GSpan src,
- const ResultInfo &info,
- ResultAttributeData &dst)
+static void copy_main_point_domain_attribute_to_mesh(const CurvesInfo &curves_info,
+ const ResultOffsets &offsets,
+ const AttributeDomain dst_domain,
+ const GSpan src_all,
+ GMutableSpan dst_all)
{
- GVArray interpolated_gvarray = info.spline.interpolate_to_evaluated(src);
- GSpan interpolated = interpolated_gvarray.get_internal_span();
-
- attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
+ attribute_math::convert_to_static_type(src_all.type(), [&](auto dummy) {
using T = decltype(dummy);
- switch (dst.domain) {
+ const Span<T> src = src_all.typed<T>();
+ MutableSpan<T> dst = dst_all.typed<T>();
+ switch (dst_domain) {
case ATTR_DOMAIN_POINT:
- copy_curve_point_data_to_mesh_verts(interpolated.typed<T>(), info, dst.data.typed<T>());
+ foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) {
+ copy_main_point_data_to_mesh_verts(
+ src.slice(info.main_points), info.profile_points.size(), dst.slice(info.vert_range));
+ });
break;
case ATTR_DOMAIN_EDGE:
- copy_curve_point_data_to_mesh_edges(interpolated.typed<T>(), info, dst.data.typed<T>());
+ foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) {
+ copy_main_point_data_to_mesh_edges(src.slice(info.main_points),
+ info.profile_points.size(),
+ info.main_segment_num,
+ info.profile_segment_num,
+ dst.slice(info.edge_range));
+ });
break;
case ATTR_DOMAIN_FACE:
- copy_curve_point_data_to_mesh_faces(interpolated.typed<T>(), info, dst.data.typed<T>());
+ foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) {
+ copy_main_point_data_to_mesh_faces(src.slice(info.main_points),
+ info.main_segment_num,
+ info.profile_segment_num,
+ dst.slice(info.poly_range));
+ });
break;
case ATTR_DOMAIN_CORNER:
/* Unsupported for now, since there are no builtin attributes to convert into. */
@@ -504,12 +509,12 @@ static void copy_curve_point_attribute_to_mesh(const GSpan src,
template<typename T>
static void copy_profile_point_data_to_mesh_verts(const Span<T> src,
- const ResultInfo &info,
+ const int main_point_num,
MutableSpan<T> dst)
{
- for (const int i_ring : IndexRange(info.spline_vert_len)) {
- const int profile_vert_start = info.vert_offset + i_ring * info.profile_vert_len;
- for (const int i_profile : IndexRange(info.profile_vert_len)) {
+ for (const int i_ring : IndexRange(main_point_num)) {
+ const int profile_vert_start = i_ring * src.size();
+ for (const int i_profile : src.index_range()) {
dst[profile_vert_start + i_profile] = src[i_profile];
}
}
@@ -517,46 +522,59 @@ static void copy_profile_point_data_to_mesh_verts(const Span<T> src,
template<typename T>
static void copy_profile_point_data_to_mesh_edges(const Span<T> src,
- const ResultInfo &info,
+ const int main_segment_num,
MutableSpan<T> dst)
{
- for (const int i_profile : IndexRange(info.profile_vert_len)) {
- const int profile_edge_offset = info.edge_offset + i_profile * info.spline_edge_len;
- dst.slice(profile_edge_offset, info.spline_edge_len).fill(src[i_profile]);
+ for (const int i_profile : src.index_range()) {
+ const int profile_edge_offset = i_profile * main_segment_num;
+ dst.slice(profile_edge_offset, main_segment_num).fill(src[i_profile]);
}
}
template<typename T>
static void copy_profile_point_data_to_mesh_faces(const Span<T> src,
- const ResultInfo &info,
+ const int main_segment_num,
+ const int profile_segment_num,
MutableSpan<T> dst)
{
- for (const int i_ring : IndexRange(info.spline_edge_len)) {
- const int profile_face_start = info.poly_offset + i_ring * info.profile_edge_len;
- for (const int i_profile : IndexRange(info.profile_edge_len)) {
+ for (const int i_ring : IndexRange(main_segment_num)) {
+ const int profile_face_start = i_ring * profile_segment_num;
+ for (const int i_profile : IndexRange(profile_segment_num)) {
dst[profile_face_start + i_profile] = src[i_profile];
}
}
}
-static void copy_profile_point_attribute_to_mesh(const GSpan src,
- const ResultInfo &info,
- ResultAttributeData &dst)
+static void copy_profile_point_domain_attribute_to_mesh(const CurvesInfo &curves_info,
+ const ResultOffsets &offsets,
+ const AttributeDomain dst_domain,
+ const GSpan src_all,
+ GMutableSpan dst_all)
{
- GVArray interpolated_gvarray = info.profile.interpolate_to_evaluated(src);
- GSpan interpolated = interpolated_gvarray.get_internal_span();
-
- attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
+ attribute_math::convert_to_static_type(src_all.type(), [&](auto dummy) {
using T = decltype(dummy);
- switch (dst.domain) {
+ const Span<T> src = src_all.typed<T>();
+ MutableSpan<T> dst = dst_all.typed<T>();
+ switch (dst_domain) {
case ATTR_DOMAIN_POINT:
- copy_profile_point_data_to_mesh_verts(interpolated.typed<T>(), info, dst.data.typed<T>());
+ foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) {
+ copy_profile_point_data_to_mesh_verts(
+ src.slice(info.profile_points), info.main_points.size(), dst.slice(info.vert_range));
+ });
break;
case ATTR_DOMAIN_EDGE:
- copy_profile_point_data_to_mesh_edges(interpolated.typed<T>(), info, dst.data.typed<T>());
+ foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) {
+ copy_profile_point_data_to_mesh_edges(
+ src.slice(info.profile_points), info.main_segment_num, dst.slice(info.edge_range));
+ });
break;
case ATTR_DOMAIN_FACE:
- copy_profile_point_data_to_mesh_faces(interpolated.typed<T>(), info, dst.data.typed<T>());
+ foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) {
+ copy_profile_point_data_to_mesh_faces(src.slice(info.profile_points),
+ info.main_segment_num,
+ info.profile_segment_num,
+ dst.slice(info.poly_range));
+ });
break;
case ATTR_DOMAIN_CORNER:
/* Unsupported for now, since there are no builtin attributes to convert into. */
@@ -568,198 +586,236 @@ static void copy_profile_point_attribute_to_mesh(const GSpan src,
});
}
-static void copy_point_domain_attributes_to_mesh(const ResultInfo &info,
- ResultAttributes &attributes)
-{
- if (!attributes.curve_point_attributes.is_empty()) {
- int i = 0;
- info.spline.attributes.foreach_attribute(
- [&](const AttributeIDRef &id, const AttributeMetaData &UNUSED(meta_data)) {
- if (attributes.curve_point_attributes[i]) {
- copy_curve_point_attribute_to_mesh(*info.spline.attributes.get_for_read(id),
- info,
- *attributes.curve_point_attributes[i]);
- }
- i++;
- return true;
- },
- ATTR_DOMAIN_POINT);
- }
- if (!attributes.profile_point_attributes.is_empty()) {
- int i = 0;
- info.profile.attributes.foreach_attribute(
- [&](const AttributeIDRef &id, const AttributeMetaData &UNUSED(meta_data)) {
- if (attributes.profile_point_attributes[i]) {
- copy_profile_point_attribute_to_mesh(*info.profile.attributes.get_for_read(id),
- info,
- *attributes.profile_point_attributes[i]);
- }
- i++;
- return true;
- },
- ATTR_DOMAIN_POINT);
- }
-}
-
template<typename T>
-static void copy_spline_data_to_mesh(Span<T> src, Span<int> offsets, MutableSpan<T> dst)
+static void copy_indices_to_offset_ranges(const VArray<T> &src,
+ const Span<int> curve_indices,
+ const Span<int> mesh_offsets,
+ MutableSpan<T> dst)
{
- for (const int i : IndexRange(src.size())) {
- dst.slice(offsets[i], offsets[i + 1] - offsets[i]).fill(src[i]);
- }
+ /* This unnecessarily instantiates the "is single" case (which should be handled elsewhere if
+ * it's ever used for attributes), but the alternative is duplicating the function for spans and
+ * other virtual arrays. */
+ devirtualize_varray(src, [&](const auto &src) {
+ threading::parallel_for(curve_indices.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ dst.slice(offsets_to_range(mesh_offsets, i)).fill(src[curve_indices[i]]);
+ }
+ });
+ });
}
-/**
- * Since the offsets for each combination of curve and profile spline are stored for every mesh
- * domain, and this just needs to fill the chunks corresponding to each combination, we can use
- * the same function for all mesh domains.
- */
-static void copy_spline_attribute_to_mesh(const GSpan src,
- const ResultOffsets &offsets,
- ResultAttributeData &dst_attribute)
+static void copy_curve_domain_attribute_to_mesh(const ResultOffsets &mesh_offsets,
+ const Span<int> curve_indices,
+ const AttributeDomain dst_domain,
+ const GVArray &src,
+ GMutableSpan dst)
{
+ Span<int> offsets;
+ switch (dst_domain) {
+ case ATTR_DOMAIN_POINT:
+ offsets = mesh_offsets.vert;
+ break;
+ case ATTR_DOMAIN_EDGE:
+ offsets = mesh_offsets.edge;
+ break;
+ case ATTR_DOMAIN_FACE:
+ offsets = mesh_offsets.poly;
+ break;
+ case ATTR_DOMAIN_CORNER:
+ offsets = mesh_offsets.loop;
+ break;
+ default:
+ BLI_assert_unreachable();
+ return;
+ }
attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
using T = decltype(dummy);
- switch (dst_attribute.domain) {
- case ATTR_DOMAIN_POINT:
- copy_spline_data_to_mesh(src.typed<T>(), offsets.vert, dst_attribute.data.typed<T>());
- break;
- case ATTR_DOMAIN_EDGE:
- copy_spline_data_to_mesh(src.typed<T>(), offsets.edge, dst_attribute.data.typed<T>());
- break;
- case ATTR_DOMAIN_FACE:
- copy_spline_data_to_mesh(src.typed<T>(), offsets.poly, dst_attribute.data.typed<T>());
- break;
- case ATTR_DOMAIN_CORNER:
- copy_spline_data_to_mesh(src.typed<T>(), offsets.loop, dst_attribute.data.typed<T>());
- break;
- default:
- BLI_assert_unreachable();
- break;
- }
+ copy_indices_to_offset_ranges(src.typed<T>(), curve_indices, offsets, dst.typed<T>());
});
}
-static void copy_spline_domain_attributes_to_mesh(const CurveEval &curve,
- const CurveEval &profile,
- const ResultOffsets &offsets,
- ResultAttributes &attributes)
-{
- if (!attributes.curve_spline_attributes.is_empty()) {
- int i = 0;
- curve.attributes.foreach_attribute(
- [&](const AttributeIDRef &id, const AttributeMetaData &UNUSED(meta_data)) {
- if (attributes.curve_spline_attributes[i]) {
- copy_spline_attribute_to_mesh(*curve.attributes.get_for_read(id),
- offsets,
- *attributes.curve_spline_attributes[i]);
- }
- i++;
- return true;
- },
- ATTR_DOMAIN_CURVE);
- }
- if (!attributes.profile_spline_attributes.is_empty()) {
- int i = 0;
- profile.attributes.foreach_attribute(
- [&](const AttributeIDRef &id, const AttributeMetaData &UNUSED(meta_data)) {
- if (attributes.profile_spline_attributes[i]) {
- copy_spline_attribute_to_mesh(*profile.attributes.get_for_read(id),
- offsets,
- *attributes.profile_spline_attributes[i]);
- }
- i++;
- return true;
- },
- ATTR_DOMAIN_CURVE);
- }
-}
-
-Mesh *curve_to_mesh_sweep(const CurveEval &curve, const CurveEval &profile, const bool fill_caps)
+Mesh *curve_to_mesh_sweep(const CurvesGeometry &main,
+ const CurvesGeometry &profile,
+ const bool fill_caps)
{
- Span<SplinePtr> profiles = profile.splines();
- Span<SplinePtr> curves = curve.splines();
+ const CurvesInfo curves_info = get_curves_info(main, profile);
- const ResultOffsets offsets = calculate_result_offsets(profiles, curves, fill_caps);
+ const ResultOffsets offsets = calculate_result_offsets(curves_info, fill_caps);
if (offsets.vert.last() == 0) {
return nullptr;
}
Mesh *mesh = BKE_mesh_new_nomain(
offsets.vert.last(), offsets.edge.last(), 0, offsets.loop.last(), offsets.poly.last());
- BKE_id_material_eval_ensure_default_slot(&mesh->id);
mesh->flag |= ME_AUTOSMOOTH;
mesh->smoothresh = DEG2RADF(180.0f);
BKE_mesh_normals_tag_dirty(mesh);
+ MutableSpan<MVert> verts(mesh->mvert, mesh->totvert);
+ MutableSpan<MEdge> edges(mesh->medge, mesh->totedge);
+ MutableSpan<MLoop> loops(mesh->mloop, mesh->totloop);
+ MutableSpan<MPoly> polys(mesh->mpoly, mesh->totpoly);
+
+ foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) {
+ fill_mesh_topology(info.vert_range.start(),
+ info.edge_range.start(),
+ info.poly_range.start(),
+ info.loop_range.start(),
+ info.main_points.size(),
+ info.profile_points.size(),
+ info.main_cyclic,
+ info.profile_cyclic,
+ fill_caps,
+ edges,
+ loops,
+ polys);
+ });
+
+ const Span<float3> main_positions = main.evaluated_positions();
+ const Span<float3> tangents = main.evaluated_tangents();
+ const Span<float3> normals = main.evaluated_normals();
+ const Span<float3> profile_positions = profile.evaluated_positions();
+
+ Vector<std::byte> eval_buffer;
+
+ Curves main_id = {nullptr};
+ main_id.geometry = reinterpret_cast<const ::CurvesGeometry &>(main);
+ CurveComponent main_component;
+ main_component.replace(&main_id, GeometryOwnershipType::Editable);
+
+ Curves profile_id = {nullptr};
+ profile_id.geometry = reinterpret_cast<const ::CurvesGeometry &>(profile);
+ CurveComponent profile_component;
+ profile_component.replace(&profile_id, GeometryOwnershipType::Editable);
+
+ Span<float> radii = {};
+ if (main_component.attribute_exists("radius")) {
+ radii = evaluated_attribute_if_necessary(
+ main_component.attribute_get_for_read<float>("radius", ATTR_DOMAIN_POINT, 1.0f),
+ main,
+ curves_info.main_type_counts,
+ eval_buffer)
+ .typed<float>();
+ }
+
+ foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) {
+ fill_mesh_positions(info.main_points.size(),
+ info.profile_points.size(),
+ main_positions.slice(info.main_points),
+ profile_positions.slice(info.profile_points),
+ tangents.slice(info.main_points),
+ normals.slice(info.main_points),
+ radii.is_empty() ? radii : radii.slice(info.main_points),
+ verts.slice(info.vert_range));
+ });
+
+ if (curves_info.profile_type_counts[CURVE_TYPE_BEZIER] > 0) {
+ const VArray<int8_t> curve_types = profile.curve_types();
+ const VArray_Span<int8_t> handle_types_left{profile.handle_types_left()};
+ const VArray_Span<int8_t> handle_types_right{profile.handle_types_right()};
+
+ foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) {
+ if (curve_types[info.i_profile] == CURVE_TYPE_BEZIER) {
+ const IndexRange points = profile.points_for_curve(info.i_profile);
+ mark_bezier_vector_edges_sharp(points.size(),
+ info.main_segment_num,
+ profile.bezier_evaluated_offsets_for_curve(info.i_profile),
+ handle_types_left.slice(points),
+ handle_types_right.slice(points),
+ edges.slice(info.edge_range));
+ }
+ });
+ }
+
+ Set<AttributeIDRef> main_attributes;
- /* Create the mesh component for retrieving attributes at this scope, since output attributes
- * can keep a reference to the component for updating after retrieving write access. */
MeshComponent mesh_component;
mesh_component.replace(mesh, GeometryOwnershipType::Editable);
- ResultAttributes attributes = create_result_attributes(curve, profile, mesh_component);
- threading::parallel_for(curves.index_range(), 128, [&](IndexRange curves_range) {
- for (const int i_spline : curves_range) {
- const Spline &spline = *curves[i_spline];
- if (spline.evaluated_points_size() == 0) {
- continue;
- }
- const int spline_start_index = i_spline * profiles.size();
- threading::parallel_for(profiles.index_range(), 128, [&](IndexRange profiles_range) {
- for (const int i_profile : profiles_range) {
- const Spline &profile = *profiles[i_profile];
- const int i_mesh = spline_start_index + i_profile;
- ResultInfo info{
- spline,
- profile,
- offsets.vert[i_mesh],
- offsets.edge[i_mesh],
- offsets.loop[i_mesh],
- offsets.poly[i_mesh],
- spline.evaluated_points_size(),
- spline.evaluated_edges_size(),
- profile.evaluated_points_size(),
- profile.evaluated_edges_size(),
- };
-
- spline_extrude_to_mesh_data(info,
- fill_caps,
- {mesh->mvert, mesh->totvert},
- {mesh->medge, mesh->totedge},
- {mesh->mloop, mesh->totloop},
- {mesh->mpoly, mesh->totpoly});
-
- copy_point_domain_attributes_to_mesh(info, attributes);
- }
- });
+ main_component.attribute_foreach([&](const AttributeIDRef &id,
+ const AttributeMetaData meta_data) {
+ if (!should_add_attribute_to_mesh(main_component, mesh_component, id)) {
+ return true;
}
+ main_attributes.add_new(id);
+
+ const AttributeDomain src_domain = meta_data.domain;
+ const CustomDataType type = meta_data.data_type;
+ GVArray src = main_component.attribute_try_get_for_read(id, src_domain, type);
+
+ const AttributeDomain dst_domain = get_attribute_domain_for_mesh(mesh_component, id);
+ OutputAttribute dst = mesh_component.attribute_try_get_for_output_only(id, dst_domain, type);
+ if (!dst) {
+ return true;
+ }
+
+ if (src_domain == ATTR_DOMAIN_POINT) {
+ copy_main_point_domain_attribute_to_mesh(
+ curves_info,
+ offsets,
+ dst_domain,
+ evaluated_attribute_if_necessary(src, main, curves_info.main_type_counts, eval_buffer),
+ dst.as_span());
+ }
+ else if (src_domain == ATTR_DOMAIN_CURVE) {
+ copy_curve_domain_attribute_to_mesh(
+ offsets, offsets.main_indices, dst_domain, src, dst.as_span());
+ }
+
+ dst.save();
+ return true;
});
- copy_spline_domain_attributes_to_mesh(curve, profile, offsets, attributes);
+ profile_component.attribute_foreach([&](const AttributeIDRef &id,
+ const AttributeMetaData meta_data) {
+ if (main_attributes.contains(id)) {
+ return true;
+ }
+ if (!should_add_attribute_to_mesh(profile_component, mesh_component, id)) {
+ return true;
+ }
+ const AttributeDomain src_domain = meta_data.domain;
+ const CustomDataType type = meta_data.data_type;
+ GVArray src = profile_component.attribute_try_get_for_read(id, src_domain, type);
+
+ const AttributeDomain dst_domain = get_attribute_domain_for_mesh(mesh_component, id);
+ OutputAttribute dst = mesh_component.attribute_try_get_for_output_only(id, dst_domain, type);
+ if (!dst) {
+ return true;
+ }
+
+ if (src_domain == ATTR_DOMAIN_POINT) {
+ copy_profile_point_domain_attribute_to_mesh(
+ curves_info,
+ offsets,
+ dst_domain,
+ evaluated_attribute_if_necessary(
+ src, profile, curves_info.profile_type_counts, eval_buffer),
+ dst.as_span());
+ }
+ else if (src_domain == ATTR_DOMAIN_CURVE) {
+ copy_curve_domain_attribute_to_mesh(
+ offsets, offsets.profile_indices, dst_domain, src, dst.as_span());
+ }
- for (OutputAttribute &output_attribute : attributes.attributes) {
- output_attribute.save();
- }
+ dst.save();
+ return true;
+ });
return mesh;
}
-static CurveEval get_curve_single_vert()
+static CurvesGeometry get_curve_single_vert()
{
- CurveEval curve;
- std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
- spline->resize(1.0f);
- spline->positions().fill(float3(0));
- spline->radii().fill(1.0f);
- spline->tilts().fill(0.0f);
- curve.add_spline(std::move(spline));
-
- return curve;
+ CurvesGeometry curves(1, 1);
+ curves.offsets_for_write().last() = 1;
+ curves.positions_for_write().fill(float3(0));
+
+ return curves;
}
-Mesh *curve_to_wire_mesh(const CurveEval &curve)
+Mesh *curve_to_wire_mesh(const CurvesGeometry &curve)
{
- static const CurveEval vert_curve = get_curve_single_vert();
+ static const CurvesGeometry vert_curve = get_curve_single_vert();
return curve_to_mesh_sweep(curve, vert_curve, false);
}
diff --git a/source/blender/blenkernel/intern/curves.cc b/source/blender/blenkernel/intern/curves.cc
index ebbdff55a55..5b51fb8af31 100644
--- a/source/blender/blenkernel/intern/curves.cc
+++ b/source/blender/blenkernel/intern/curves.cc
@@ -27,6 +27,7 @@
#include "BKE_anim_data.h"
#include "BKE_curves.hh"
#include "BKE_customdata.h"
+#include "BKE_geometry_set.hh"
#include "BKE_global.h"
#include "BKE_idtype.h"
#include "BKE_lib_id.h"
@@ -282,13 +283,11 @@ Curves *BKE_curves_copy_for_eval(Curves *curves_src, bool reference)
return result;
}
-static Curves *curves_evaluate_modifiers(struct Depsgraph *depsgraph,
- struct Scene *scene,
- Object *object,
- Curves *curves_input)
+static void curves_evaluate_modifiers(struct Depsgraph *depsgraph,
+ struct Scene *scene,
+ Object *object,
+ GeometrySet &geometry_set)
{
- Curves *curves = curves_input;
-
/* Modifier evaluation modes. */
const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
const int required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime;
@@ -308,27 +307,10 @@ static Curves *curves_evaluate_modifiers(struct Depsgraph *depsgraph,
continue;
}
- if ((mti->type == eModifierTypeType_OnlyDeform) &&
- (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly)) {
- /* Ensure we are not modifying the input. */
- if (curves == curves_input) {
- curves = BKE_curves_copy_for_eval(curves, true);
- }
-
- /* Created deformed coordinates array on demand. */
- blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap(
- curves->geometry);
- MutableSpan<float3> positions = geometry.positions_for_write();
-
- mti->deformVerts(md,
- &mectx,
- nullptr,
- reinterpret_cast<float(*)[3]>(positions.data()),
- curves->geometry.point_size);
+ if (mti->modifyGeometrySet != nullptr) {
+ mti->modifyGeometrySet(md, &mectx, &geometry_set);
}
}
-
- return curves;
}
void BKE_curves_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Object *object)
@@ -338,11 +320,20 @@ void BKE_curves_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Ob
/* Evaluate modifiers. */
Curves *curves = static_cast<Curves *>(object->data);
- Curves *curves_eval = curves_evaluate_modifiers(depsgraph, scene, object, curves);
+ GeometrySet geometry_set = GeometrySet::create_with_curves(curves,
+ GeometryOwnershipType::ReadOnly);
+ curves_evaluate_modifiers(depsgraph, scene, object, geometry_set);
/* Assign evaluated object. */
- const bool is_owned = (curves != curves_eval);
- BKE_object_eval_assign_data(object, &curves_eval->id, is_owned);
+ Curves *curves_eval = const_cast<Curves *>(geometry_set.get_curves_for_read());
+ if (curves_eval == nullptr) {
+ curves_eval = blender::bke::curves_new_nomain(0, 0);
+ BKE_object_eval_assign_data(object, &curves_eval->id, true);
+ }
+ else {
+ BKE_object_eval_assign_data(object, &curves_eval->id, false);
+ }
+ object->runtime.geometry_set_eval = new GeometrySet(std::move(geometry_set));
}
/* Draw Cache */
diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc
index 5c89dfd4df5..8e97884516c 100644
--- a/source/blender/blenkernel/intern/curves_geometry.cc
+++ b/source/blender/blenkernel/intern/curves_geometry.cc
@@ -12,6 +12,7 @@
#include "BLI_bounds.hh"
#include "BLI_index_mask_ops.hh"
#include "BLI_length_parameterize.hh"
+#include "BLI_math_rotation.hh"
#include "DNA_curves_types.h"
@@ -22,6 +23,7 @@ namespace blender::bke {
static const std::string ATTR_POSITION = "position";
static const std::string ATTR_RADIUS = "radius";
+static const std::string ATTR_TILT = "tilt";
static const std::string ATTR_CURVE_TYPE = "curve_type";
static const std::string ATTR_CYCLIC = "cyclic";
static const std::string ATTR_RESOLUTION = "resolution";
@@ -330,6 +332,15 @@ MutableSpan<int8_t> CurvesGeometry::normal_mode_for_write()
return get_mutable_attribute<int8_t>(*this, ATTR_DOMAIN_CURVE, ATTR_NORMAL_MODE);
}
+VArray<float> CurvesGeometry::tilt() const
+{
+ return get_varray_attribute<float>(*this, ATTR_DOMAIN_POINT, ATTR_TILT, 0.0f);
+}
+MutableSpan<float> CurvesGeometry::tilt_for_write()
+{
+ return get_mutable_attribute<float>(*this, ATTR_DOMAIN_POINT, ATTR_TILT);
+}
+
VArray<int8_t> CurvesGeometry::handle_types_left() const
{
return get_varray_attribute<int8_t>(*this, ATTR_DOMAIN_POINT, ATTR_HANDLE_TYPE_LEFT, 0);
@@ -577,18 +588,25 @@ void CurvesGeometry::ensure_nurbs_basis_cache() const
Span<float3> CurvesGeometry::evaluated_positions() const
{
if (!this->runtime->position_cache_dirty) {
- return this->runtime->evaluated_position_cache;
+ return this->runtime->evaluated_positions_span;
}
/* A double checked lock. */
std::scoped_lock lock{this->runtime->position_cache_mutex};
if (!this->runtime->position_cache_dirty) {
- return this->runtime->evaluated_position_cache;
+ return this->runtime->evaluated_positions_span;
}
threading::isolate_task([&]() {
+ if (this->is_single_type(CURVE_TYPE_POLY)) {
+ this->runtime->evaluated_positions_span = this->positions();
+ this->runtime->evaluated_position_cache.clear_and_make_inline();
+ return;
+ }
+
this->runtime->evaluated_position_cache.resize(this->evaluated_points_num());
MutableSpan<float3> evaluated_positions = this->runtime->evaluated_position_cache;
+ this->runtime->evaluated_positions_span = evaluated_positions;
VArray<int8_t> types = this->curve_types();
VArray<bool> cyclic = this->cyclic();
@@ -645,7 +663,7 @@ Span<float3> CurvesGeometry::evaluated_positions() const
});
this->runtime->position_cache_dirty = false;
- return this->runtime->evaluated_position_cache;
+ return this->runtime->evaluated_positions_span;
}
Span<float3> CurvesGeometry::evaluated_tangents() const
@@ -710,6 +728,15 @@ Span<float3> CurvesGeometry::evaluated_tangents() const
return this->runtime->evaluated_tangent_cache;
}
+static void rotate_directions_around_axes(MutableSpan<float3> directions,
+ const Span<float3> axes,
+ const Span<float> angles)
+{
+ for (const int i : directions.index_range()) {
+ directions[i] = math::rotate_direction_around_axis(directions[i], axes[i], angles[i]);
+ }
+}
+
Span<float3> CurvesGeometry::evaluated_normals() const
{
if (!this->runtime->normal_cache_dirty) {
@@ -726,11 +753,16 @@ Span<float3> CurvesGeometry::evaluated_normals() const
const Span<float3> evaluated_tangents = this->evaluated_tangents();
const VArray<bool> cyclic = this->cyclic();
const VArray<int8_t> normal_mode = this->normal_mode();
+ const VArray<int8_t> types = this->curve_types();
+ const VArray<float> tilt = this->tilt();
this->runtime->evaluated_normal_cache.resize(this->evaluated_points_num());
MutableSpan<float3> evaluated_normals = this->runtime->evaluated_normal_cache;
threading::parallel_for(this->curves_range(), 128, [&](IndexRange curves_range) {
+ /* Reuse a buffer for the evaluated tilts. */
+ Vector<float> evaluated_tilts;
+
for (const int curve_index : curves_range) {
const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index);
if (UNLIKELY(evaluated_points.is_empty())) {
@@ -747,6 +779,27 @@ Span<float3> CurvesGeometry::evaluated_normals() const
evaluated_normals.slice(evaluated_points));
break;
}
+
+ /* If the "tilt" attribute exists, rotate the normals around the tangents by the
+ * evaluated angles. We can avoid copying the tilts to evaluate them for poly curves. */
+ if (!(tilt.is_single() && tilt.get_internal_single() == 0.0f)) {
+ const IndexRange points = this->points_for_curve(curve_index);
+ Span<float> curve_tilt = tilt.get_internal_span().slice(points);
+ if (types[curve_index] == CURVE_TYPE_POLY) {
+ rotate_directions_around_axes(evaluated_normals.slice(evaluated_points),
+ evaluated_tangents.slice(evaluated_points),
+ curve_tilt);
+ }
+ else {
+ evaluated_tilts.clear();
+ evaluated_tilts.resize(evaluated_points.size());
+ this->interpolate_to_evaluated(
+ curve_index, curve_tilt, evaluated_tilts.as_mutable_span());
+ rotate_directions_around_axes(evaluated_normals.slice(evaluated_points),
+ evaluated_tangents.slice(evaluated_points),
+ evaluated_tilts.as_span());
+ }
+ }
}
});
});
@@ -787,6 +840,48 @@ void CurvesGeometry::interpolate_to_evaluated(const int curve_index,
BLI_assert_unreachable();
}
+void CurvesGeometry::interpolate_to_evaluated(const GSpan src, GMutableSpan dst) const
+{
+ BLI_assert(!this->runtime->offsets_cache_dirty);
+ BLI_assert(!this->runtime->nurbs_basis_cache_dirty);
+ const VArray<int8_t> types = this->curve_types();
+ const VArray<int> resolution = this->resolution();
+ const VArray<bool> cyclic = this->cyclic();
+ const VArray<int8_t> nurbs_orders = this->nurbs_orders();
+ const Span<float> nurbs_weights = this->nurbs_weights();
+
+ threading::parallel_for(this->curves_range(), 512, [&](IndexRange curves_range) {
+ for (const int curve_index : curves_range) {
+ const IndexRange points = this->points_for_curve(curve_index);
+ const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index);
+ switch (types[curve_index]) {
+ case CURVE_TYPE_CATMULL_ROM:
+ curves::catmull_rom::interpolate_to_evaluated(src.slice(points),
+ cyclic[curve_index],
+ resolution[curve_index],
+ dst.slice(evaluated_points));
+ continue;
+ case CURVE_TYPE_POLY:
+ dst.slice(evaluated_points).copy_from(src.slice(points));
+ continue;
+ case CURVE_TYPE_BEZIER:
+ curves::bezier::interpolate_to_evaluated(
+ src.slice(points),
+ this->runtime->bezier_evaluated_offsets.as_span().slice(points),
+ dst.slice(evaluated_points));
+ continue;
+ case CURVE_TYPE_NURBS:
+ curves::nurbs::interpolate_to_evaluated(this->runtime->nurbs_basis_cache[curve_index],
+ nurbs_orders[curve_index],
+ nurbs_weights.slice(points),
+ src.slice(points),
+ dst.slice(evaluated_points));
+ continue;
+ }
+ }
+ });
+}
+
void CurvesGeometry::ensure_evaluated_lengths() const
{
if (!this->runtime->length_cache_dirty) {
diff --git a/source/blender/blenkernel/intern/editmesh.c b/source/blender/blenkernel/intern/editmesh.c
index 114d33b1a3d..d176bf41254 100644
--- a/source/blender/blenkernel/intern/editmesh.c
+++ b/source/blender/blenkernel/intern/editmesh.c
@@ -214,18 +214,14 @@ float (*BKE_editmesh_vert_coords_alloc(struct Depsgraph *depsgraph,
Object *ob,
int *r_vert_len))[3]
{
- Mesh *cage;
- BLI_bitmap *visit_bitmap;
- struct CageUserData data;
- float(*cos_cage)[3];
+ Mesh *cage = editbmesh_get_eval_cage(depsgraph, scene, ob, em, &CD_MASK_BAREMESH);
+ float(*cos_cage)[3] = MEM_callocN(sizeof(*cos_cage) * em->bm->totvert, "bmbvh cos_cage");
- cage = editbmesh_get_eval_cage(depsgraph, scene, ob, em, &CD_MASK_BAREMESH);
- cos_cage = MEM_callocN(sizeof(*cos_cage) * em->bm->totvert, "bmbvh cos_cage");
-
- /* when initializing cage verts, we only want the first cage coordinate for each vertex,
- * so that e.g. mirror or array use original vertex coordinates and not mirrored or duplicate */
- visit_bitmap = BLI_BITMAP_NEW(em->bm->totvert, __func__);
+ /* When initializing cage verts, we only want the first cage coordinate for each vertex,
+ * so that e.g. mirror or array use original vertex coordinates and not mirrored or duplicate. */
+ BLI_bitmap *visit_bitmap = BLI_BITMAP_NEW(em->bm->totvert, __func__);
+ struct CageUserData data;
data.totvert = em->bm->totvert;
data.cos_cage = cos_cage;
data.visit_bitmap = visit_bitmap;
diff --git a/source/blender/blenkernel/intern/editmesh_cache.c b/source/blender/blenkernel/intern/editmesh_cache.cc
index f80f300d149..438d287fb28 100644
--- a/source/blender/blenkernel/intern/editmesh_cache.c
+++ b/source/blender/blenkernel/intern/editmesh_cache.cc
@@ -8,7 +8,9 @@
#include "MEM_guardedalloc.h"
+#include "BLI_bounds.hh"
#include "BLI_math_vector.h"
+#include "BLI_span.hh"
#include "DNA_mesh_types.h"
@@ -21,23 +23,21 @@
void BKE_editmesh_cache_ensure_poly_normals(BMEditMesh *em, EditMeshData *emd)
{
- if (!(emd->vertexCos && (emd->polyNos == NULL))) {
+ if (!(emd->vertexCos && (emd->polyNos == nullptr))) {
return;
}
BMesh *bm = em->bm;
- const float(*vertexCos)[3];
- float(*polyNos)[3];
-
BMFace *efa;
BMIter fiter;
int i;
BM_mesh_elem_index_ensure(bm, BM_VERT);
- polyNos = MEM_mallocN(sizeof(*polyNos) * bm->totface, __func__);
+ float(*polyNos)[3] = static_cast<float(*)[3]>(
+ MEM_mallocN(sizeof(*polyNos) * bm->totface, __func__));
- vertexCos = emd->vertexCos;
+ const float(*vertexCos)[3] = emd->vertexCos;
BM_ITER_MESH_INDEX (efa, &fiter, bm, BM_FACES_OF_MESH, i) {
BM_elem_index_set(efa, i); /* set_inline */
@@ -50,7 +50,7 @@ void BKE_editmesh_cache_ensure_poly_normals(BMEditMesh *em, EditMeshData *emd)
void BKE_editmesh_cache_ensure_vert_normals(BMEditMesh *em, EditMeshData *emd)
{
- if (!(emd->vertexCos && (emd->vertexNos == NULL))) {
+ if (!(emd->vertexCos && (emd->vertexNos == nullptr))) {
return;
}
@@ -58,14 +58,14 @@ void BKE_editmesh_cache_ensure_vert_normals(BMEditMesh *em, EditMeshData *emd)
const float(*vertexCos)[3], (*polyNos)[3];
float(*vertexNos)[3];
- /* calculate vertex normals from poly normals */
+ /* Calculate vertex normals from poly normals. */
BKE_editmesh_cache_ensure_poly_normals(em, emd);
BM_mesh_elem_index_ensure(bm, BM_FACE);
polyNos = emd->polyNos;
vertexCos = emd->vertexCos;
- vertexNos = MEM_callocN(sizeof(*vertexNos) * bm->totvert, __func__);
+ vertexNos = static_cast<float(*)[3]>(MEM_callocN(sizeof(*vertexNos) * bm->totvert, __func__));
BM_verts_calc_normal_vcos(bm, polyNos, vertexCos, vertexNos);
@@ -74,17 +74,17 @@ void BKE_editmesh_cache_ensure_vert_normals(BMEditMesh *em, EditMeshData *emd)
void BKE_editmesh_cache_ensure_poly_centers(BMEditMesh *em, EditMeshData *emd)
{
- if (emd->polyCos != NULL) {
+ if (emd->polyCos != nullptr) {
return;
}
BMesh *bm = em->bm;
- float(*polyCos)[3];
BMFace *efa;
BMIter fiter;
int i;
- polyCos = MEM_mallocN(sizeof(*polyCos) * bm->totface, __func__);
+ float(*polyCos)[3] = static_cast<float(*)[3]>(
+ MEM_mallocN(sizeof(*polyCos) * bm->totface, __func__));
if (emd->vertexCos) {
const float(*vertexCos)[3];
@@ -116,18 +116,20 @@ bool BKE_editmesh_cache_calc_minmax(struct BMEditMesh *em,
float min[3],
float max[3])
{
+ using namespace blender;
BMesh *bm = em->bm;
- BMVert *eve;
- BMIter iter;
- int i;
if (bm->totvert) {
if (emd->vertexCos) {
- BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
- minmax_v3v3_v3(min, max, emd->vertexCos[i]);
- }
+ Span<float3> vert_coords(reinterpret_cast<const float3 *>(emd->vertexCos), bm->totvert);
+ std::optional<bounds::MinMaxResult<float3>> bounds = bounds::min_max(vert_coords);
+ BLI_assert(bounds.has_value());
+ copy_v3_v3(min, math::min(bounds->min, float3(min)));
+ copy_v3_v3(max, math::max(bounds->max, float3(max)));
}
else {
+ BMVert *eve;
+ BMIter iter;
BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
minmax_v3v3_v3(min, max, eve->co);
}
diff --git a/source/blender/blenkernel/intern/geometry_component_curves.cc b/source/blender/blenkernel/intern/geometry_component_curves.cc
index 0bcab0aae7a..4796135b32f 100644
--- a/source/blender/blenkernel/intern/geometry_component_curves.cc
+++ b/source/blender/blenkernel/intern/geometry_component_curves.cc
@@ -462,14 +462,14 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
static BuiltinCustomDataLayerProvider nurbs_order("nurbs_order",
ATTR_DOMAIN_CURVE,
- CD_PROP_INT32,
- CD_PROP_INT32,
+ CD_PROP_INT8,
+ CD_PROP_INT8,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Writable,
BuiltinAttributeProvider::Deletable,
curve_access,
- make_array_read_attribute<int>,
- make_array_write_attribute<int>,
+ make_array_read_attribute<int8_t>,
+ make_array_write_attribute<int8_t>,
tag_component_topology_changed);
static BuiltinCustomDataLayerProvider normal_mode("normal_mode",
@@ -545,6 +545,7 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
&handle_type_left,
&normal_mode,
&nurbs_order,
+ &nurbs_knots_mode,
&nurbs_weight,
&curve_type,
&resolution,
diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc
index 44ffd64e475..d3c3f41779a 100644
--- a/source/blender/blenkernel/intern/geometry_set_instances.cc
+++ b/source/blender/blenkernel/intern/geometry_set_instances.cc
@@ -50,7 +50,12 @@ GeometrySet object_get_evaluated_geometry_set(const Object &object)
return geometry_set;
}
if (object.runtime.geometry_set_eval != nullptr) {
- return *object.runtime.geometry_set_eval;
+ GeometrySet geometry_set = *object.runtime.geometry_set_eval;
+ /* Ensure that subdivision is performed on the CPU. */
+ if (geometry_set.has_mesh()) {
+ add_final_mesh_as_geometry_component(object, geometry_set);
+ }
+ return geometry_set;
}
/* Otherwise, construct a new geometry set with the component based on the object type. */
diff --git a/source/blender/blenkernel/intern/gpencil_geom.cc b/source/blender/blenkernel/intern/gpencil_geom.cc
index e1a79986719..041696fa8d3 100644
--- a/source/blender/blenkernel/intern/gpencil_geom.cc
+++ b/source/blender/blenkernel/intern/gpencil_geom.cc
@@ -981,7 +981,7 @@ bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist, const short mo
* \{ */
bool BKE_gpencil_stroke_smooth_point(bGPDstroke *gps,
- int i,
+ int point_index,
float influence,
int iterations,
const bool smooth_caps,
@@ -995,7 +995,7 @@ bool BKE_gpencil_stroke_smooth_point(bGPDstroke *gps,
/* Overview of the algorithm here and in the following smooth functions:
* The smooth functions return the new attribute in question for a single point.
- * The result is stored in r_gps->points[i], while the data is read from gps.
+ * The result is stored in r_gps->points[point_index], while the data is read from gps.
* To get a correct result, duplicate the stroke point data and read from the copy,
* while writing to the real stroke. Not doing that will result in acceptable, but
* asymmetric results.
@@ -1004,16 +1004,16 @@ bool BKE_gpencil_stroke_smooth_point(bGPDstroke *gps,
* the parameter "iterations" set to 1 or 2. (2 matches the old algorithm).
*/
- const bGPDspoint *pt = &gps->points[i];
+ const bGPDspoint *pt = &gps->points[point_index];
const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
/* If smooth_caps is false, the caps will not be translated by smoothing. */
- if (!smooth_caps && !is_cyclic && ELEM(i, 0, gps->totpoints - 1)) {
- copy_v3_v3(&r_gps->points[i].x, &pt->x);
+ if (!smooth_caps && !is_cyclic && ELEM(point_index, 0, gps->totpoints - 1)) {
+ copy_v3_v3(&r_gps->points[point_index].x, &pt->x);
return true;
}
/* This function uses a binomial kernel, which is the discrete version of gaussian blur.
- * The weight for a vertex at the relative index i is
+ * The weight for a vertex at the relative index point_index is
* w = nCr(n, j + n/2) / 2^n = (n/1 * (n-1)/2 * ... * (n-j-n/2)/(j+n/2)) / 2^n
* All weights together sum up to 1
* This is equivalent to doing multiple iterations of averaging neighbors,
@@ -1044,8 +1044,8 @@ bool BKE_gpencil_stroke_smooth_point(bGPDstroke *gps,
0.0;
double total_w = 0.0;
for (int step = iterations; step > 0; step--) {
- int before = i - step;
- int after = i + step;
+ int before = point_index - step;
+ int after = point_index + step;
float w_before = (float)(w - w2);
float w_after = (float)(w - w2);
@@ -1056,13 +1056,13 @@ bool BKE_gpencil_stroke_smooth_point(bGPDstroke *gps,
else {
if (before < 0) {
if (!smooth_caps) {
- w_before *= -before / (float)i;
+ w_before *= -before / (float)point_index;
}
before = 0;
}
if (after > gps->totpoints - 1) {
if (!smooth_caps) {
- w_after *= (after - (gps->totpoints - 1)) / (float)(gps->totpoints - 1 - i);
+ w_after *= (after - (gps->totpoints - 1)) / (float)(gps->totpoints - 1 - point_index);
}
after = gps->totpoints - 1;
}
@@ -1089,7 +1089,7 @@ bool BKE_gpencil_stroke_smooth_point(bGPDstroke *gps,
add_v3_v3(sco, &pt->x);
/* Based on influence factor, blend between original and optimal smoothed coordinate. */
- interp_v3_v3v3(&r_gps->points[i].x, &pt->x, sco, influence);
+ interp_v3_v3v3(&r_gps->points[point_index].x, &pt->x, sco, influence);
return true;
}
@@ -1101,7 +1101,7 @@ bool BKE_gpencil_stroke_smooth_point(bGPDstroke *gps,
* \{ */
bool BKE_gpencil_stroke_smooth_strength(
- bGPDstroke *gps, int i, float influence, int iterations, bGPDstroke *r_gps)
+ bGPDstroke *gps, int point_index, float influence, int iterations, bGPDstroke *r_gps)
{
/* If nothing to do, return early */
if (gps->totpoints <= 2 || iterations <= 0) {
@@ -1110,15 +1110,15 @@ bool BKE_gpencil_stroke_smooth_strength(
/* See BKE_gpencil_stroke_smooth_point for details on the algorithm. */
- const bGPDspoint *pt = &gps->points[i];
+ const bGPDspoint *pt = &gps->points[point_index];
const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
float strength = 0.0f;
const int n_half = (iterations * iterations) / 4 + iterations;
double w = 1.0;
double total_w = 0.0;
for (int step = iterations; step > 0; step--) {
- int before = i - step;
- int after = i + step;
+ int before = point_index - step;
+ int after = point_index + step;
float w_before = (float)w;
float w_after = (float)w;
@@ -1147,7 +1147,7 @@ bool BKE_gpencil_stroke_smooth_strength(
strength /= total_w;
/* Based on influence factor, blend between original and optimal smoothed value. */
- r_gps->points[i].strength = pt->strength + strength * influence;
+ r_gps->points[point_index].strength = pt->strength + strength * influence;
return true;
}
@@ -1159,7 +1159,7 @@ bool BKE_gpencil_stroke_smooth_strength(
* \{ */
bool BKE_gpencil_stroke_smooth_thickness(
- bGPDstroke *gps, int i, float influence, int iterations, bGPDstroke *r_gps)
+ bGPDstroke *gps, int point_index, float influence, int iterations, bGPDstroke *r_gps)
{
/* If nothing to do, return early */
if (gps->totpoints <= 2 || iterations <= 0) {
@@ -1168,15 +1168,15 @@ bool BKE_gpencil_stroke_smooth_thickness(
/* See BKE_gpencil_stroke_smooth_point for details on the algorithm. */
- const bGPDspoint *pt = &gps->points[i];
+ const bGPDspoint *pt = &gps->points[point_index];
const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
float pressure = 0.0f;
const int n_half = (iterations * iterations) / 4 + iterations;
double w = 1.0;
double total_w = 0.0;
for (int step = iterations; step > 0; step--) {
- int before = i - step;
- int after = i + step;
+ int before = point_index - step;
+ int after = point_index + step;
float w_before = (float)w;
float w_after = (float)w;
@@ -1205,7 +1205,7 @@ bool BKE_gpencil_stroke_smooth_thickness(
pressure /= total_w;
/* Based on influence factor, blend between original and optimal smoothed value. */
- r_gps->points[i].pressure = pt->pressure + pressure * influence;
+ r_gps->points[point_index].pressure = pt->pressure + pressure * influence;
return true;
}
@@ -1216,8 +1216,11 @@ bool BKE_gpencil_stroke_smooth_thickness(
/** \name Stroke Smooth UV
* \{ */
-bool BKE_gpencil_stroke_smooth_uv(
- struct bGPDstroke *gps, int i, float influence, int iterations, struct bGPDstroke *r_gps)
+bool BKE_gpencil_stroke_smooth_uv(struct bGPDstroke *gps,
+ int point_index,
+ float influence,
+ int iterations,
+ struct bGPDstroke *r_gps)
{
/* If nothing to do, return early */
if (gps->totpoints <= 2 || iterations <= 0) {
@@ -1226,13 +1229,13 @@ bool BKE_gpencil_stroke_smooth_uv(
/* See BKE_gpencil_stroke_smooth_point for details on the algorithm. */
- const bGPDspoint *pt = &gps->points[i];
+ const bGPDspoint *pt = &gps->points[point_index];
const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
/* If don't change the caps. */
- if (!is_cyclic && ELEM(i, 0, gps->totpoints - 1)) {
- r_gps->points[i].uv_rot = pt->uv_rot;
- r_gps->points[i].uv_fac = pt->uv_fac;
+ if (!is_cyclic && ELEM(point_index, 0, gps->totpoints - 1)) {
+ r_gps->points[point_index].uv_rot = pt->uv_rot;
+ r_gps->points[point_index].uv_fac = pt->uv_fac;
return true;
}
@@ -1242,8 +1245,8 @@ bool BKE_gpencil_stroke_smooth_uv(
double w = 1.0;
double total_w = 0.0;
for (int step = iterations; step > 0; step--) {
- int before = i - step;
- int after = i + step;
+ int before = point_index - step;
+ int after = point_index + step;
float w_before = (float)w;
float w_after = (float)w;
@@ -1253,11 +1256,11 @@ bool BKE_gpencil_stroke_smooth_uv(
}
else {
if (before < 0) {
- w_before *= -before / (float)i;
+ w_before *= -before / (float)point_index;
before = 0;
}
if (after > gps->totpoints - 1) {
- w_after *= (after - (gps->totpoints - 1)) / (float)(gps->totpoints - 1 - i);
+ w_after *= (after - (gps->totpoints - 1)) / (float)(gps->totpoints - 1 - point_index);
after = gps->totpoints - 1;
}
}
@@ -1281,8 +1284,8 @@ bool BKE_gpencil_stroke_smooth_uv(
uv_fac /= total_w;
/* Based on influence factor, blend between original and optimal smoothed value. */
- r_gps->points[i].uv_rot = pt->uv_rot + uv_rot * influence;
- r_gps->points[i].uv_fac = pt->uv_fac + uv_fac * influence;
+ r_gps->points[point_index].uv_rot = pt->uv_rot + uv_rot * influence;
+ r_gps->points[point_index].uv_fac = pt->uv_fac + uv_fac * influence;
return true;
}
diff --git a/source/blender/blenkernel/intern/image.cc b/source/blender/blenkernel/intern/image.cc
index b1385eab9cf..53ec148fd2d 100644
--- a/source/blender/blenkernel/intern/image.cc
+++ b/source/blender/blenkernel/intern/image.cc
@@ -1806,9 +1806,9 @@ void BKE_image_stamp_buf(Scene *scene,
int channels)
{
struct StampData stamp_data;
- float w, h, pad;
+ int w, h, pad;
int x, y, y_ofs;
- float h_fixed;
+ int h_fixed;
const int mono = blf_mono_font_render; /* XXX */
struct ColorManagedDisplay *display;
const char *display_device;
@@ -1816,20 +1816,20 @@ void BKE_image_stamp_buf(Scene *scene,
/* vars for calculating wordwrap */
struct {
struct ResultBLF info;
- rctf rect;
+ rcti rect;
} wrap;
/* this could be an argument if we want to operate on non linear float imbuf's
* for now though this is only used for renders which use scene settings */
#define TEXT_SIZE_CHECK(str, w, h) \
- ((str[0]) && ((void)(h = h_fixed), (w = BLF_width(mono, str, sizeof(str)))))
+ ((str[0]) && ((void)(h = h_fixed), (w = (int)BLF_width(mono, str, sizeof(str)))))
/* must enable BLF_WORD_WRAP before using */
#define TEXT_SIZE_CHECK_WORD_WRAP(str, w, h) \
((str[0]) && (BLF_boundbox_ex(mono, str, sizeof(str), &wrap.rect, &wrap.info), \
(void)(h = h_fixed * wrap.info.lines), \
- (w = BLI_rctf_size_x(&wrap.rect))))
+ (w = BLI_rcti_size_x(&wrap.rect))))
#define BUFF_MARGIN_X 2
#define BUFF_MARGIN_Y 1
@@ -3096,7 +3096,7 @@ void BKE_image_get_tile_label(Image *ima, ImageTile *tile, char *label, int len_
}
}
-bool BKE_image_get_tile_info(char *filepath, ListBase *tiles, int *tile_start, int *tile_range)
+bool BKE_image_get_tile_info(char *filepath, ListBase *tiles, int *r_tile_start, int *r_tile_range)
{
char filename[FILE_MAXFILE], dirname[FILE_MAXDIR];
BLI_split_dirfile(filepath, dirname, filename, sizeof(dirname), sizeof(filename));
@@ -3106,7 +3106,7 @@ bool BKE_image_get_tile_info(char *filepath, ListBase *tiles, int *tile_start, i
eUDIM_TILE_FORMAT tile_format;
char *udim_pattern = BKE_image_get_tile_strformat(filename, &tile_format);
- bool is_udim = true;
+ bool all_valid_udim = true;
int min_udim = IMA_UDIM_MAX + 1;
int max_udim = 0;
int id;
@@ -3124,7 +3124,7 @@ bool BKE_image_get_tile_info(char *filepath, ListBase *tiles, int *tile_start, i
}
if (id < 1001 || id > IMA_UDIM_MAX) {
- is_udim = false;
+ all_valid_udim = false;
break;
}
@@ -3135,11 +3135,14 @@ bool BKE_image_get_tile_info(char *filepath, ListBase *tiles, int *tile_start, i
BLI_filelist_free(dirs, dirs_num);
MEM_SAFE_FREE(udim_pattern);
- if (is_udim && min_udim <= IMA_UDIM_MAX) {
+ /* Ensure that all discovered UDIMs are valid and that there's at least 2 files in total.
+ * Downstream code checks the range value to determine tiled-ness; it's important we match that
+ * expectation here too (T97366). */
+ if (all_valid_udim && min_udim <= IMA_UDIM_MAX && max_udim > min_udim) {
BLI_join_dirfile(filepath, FILE_MAX, dirname, filename);
- *tile_start = min_udim;
- *tile_range = max_udim - min_udim + 1;
+ *r_tile_start = min_udim;
+ *r_tile_range = max_udim - min_udim + 1;
return true;
}
return false;
diff --git a/source/blender/blenkernel/intern/lattice_deform_test.cc b/source/blender/blenkernel/intern/lattice_deform_test.cc
index 1b1bca5fc53..58aadf652b7 100644
--- a/source/blender/blenkernel/intern/lattice_deform_test.cc
+++ b/source/blender/blenkernel/intern/lattice_deform_test.cc
@@ -69,7 +69,7 @@ static void test_lattice_deform_free(LatticeDeformTestContext *ctx)
TEST(lattice_deform_performance, performance_no_dvert_1)
{
const int32_t num_items = 1;
- LatticeDeformTestContext ctx = {{{nullptr}}};
+ LatticeDeformTestContext ctx = {dna::shallow_zero_initialize()};
RandomNumberGenerator rng;
test_lattice_deform_init(&ctx, &rng, num_items);
test_lattice_deform(&ctx, num_items);
@@ -78,7 +78,7 @@ TEST(lattice_deform_performance, performance_no_dvert_1)
TEST(lattice_deform_performance, performance_no_dvert_1000)
{
const int32_t num_items = 1000;
- LatticeDeformTestContext ctx = {{{nullptr}}};
+ LatticeDeformTestContext ctx = {dna::shallow_zero_initialize()};
RandomNumberGenerator rng;
test_lattice_deform_init(&ctx, &rng, num_items);
test_lattice_deform(&ctx, num_items);
@@ -87,7 +87,7 @@ TEST(lattice_deform_performance, performance_no_dvert_1000)
TEST(lattice_deform_performance, performance_no_dvert_10000)
{
const int32_t num_items = 10000;
- LatticeDeformTestContext ctx = {{{nullptr}}};
+ LatticeDeformTestContext ctx = {dna::shallow_zero_initialize()};
RandomNumberGenerator rng;
test_lattice_deform_init(&ctx, &rng, num_items);
test_lattice_deform(&ctx, num_items);
@@ -96,7 +96,7 @@ TEST(lattice_deform_performance, performance_no_dvert_10000)
TEST(lattice_deform_performance, performance_no_dvert_100000)
{
const int32_t num_items = 100000;
- LatticeDeformTestContext ctx = {{{nullptr}}};
+ LatticeDeformTestContext ctx = {dna::shallow_zero_initialize()};
RandomNumberGenerator rng;
test_lattice_deform_init(&ctx, &rng, num_items);
test_lattice_deform(&ctx, num_items);
@@ -105,7 +105,7 @@ TEST(lattice_deform_performance, performance_no_dvert_100000)
TEST(lattice_deform_performance, performance_no_dvert_1000000)
{
const int32_t num_items = 1000000;
- LatticeDeformTestContext ctx = {{{nullptr}}};
+ LatticeDeformTestContext ctx = {dna::shallow_zero_initialize()};
RandomNumberGenerator rng;
test_lattice_deform_init(&ctx, &rng, num_items);
test_lattice_deform(&ctx, num_items);
@@ -114,7 +114,7 @@ TEST(lattice_deform_performance, performance_no_dvert_1000000)
TEST(lattice_deform_performance, performance_no_dvert_10000000)
{
const int32_t num_items = 10000000;
- LatticeDeformTestContext ctx = {{{nullptr}}};
+ LatticeDeformTestContext ctx = {dna::shallow_zero_initialize()};
RandomNumberGenerator rng;
test_lattice_deform_init(&ctx, &rng, num_items);
test_lattice_deform(&ctx, num_items);
diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c
index 1cc1839d2d0..f0ccd305690 100644
--- a/source/blender/blenkernel/intern/layer.c
+++ b/source/blender/blenkernel/intern/layer.c
@@ -1658,7 +1658,10 @@ static void layer_collection_local_sync(ViewLayer *view_layer,
if (visible) {
LISTBASE_FOREACH (CollectionObject *, cob, &layer_collection->collection->gobject) {
- BLI_assert(cob->ob);
+ if (cob->ob == NULL) {
+ continue;
+ }
+
Base *base = BKE_view_layer_base_find(view_layer, cob->ob);
base->local_collections_bits |= local_collections_uuid;
}
diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c
index 1a6fcf5ff43..2b449ad50bb 100644
--- a/source/blender/blenkernel/intern/lib_remap.c
+++ b/source/blender/blenkernel/intern/lib_remap.c
@@ -317,7 +317,8 @@ static void libblock_remap_data_preprocess(ID *id_owner,
*/
static void libblock_remap_data_postprocess_object_update(Main *bmain,
Object *old_ob,
- Object *new_ob)
+ Object *new_ob,
+ const bool do_sync_collection)
{
if (new_ob == NULL) {
/* In case we unlinked old_ob (new_ob is NULL), the object has already
@@ -331,7 +332,9 @@ static void libblock_remap_data_postprocess_object_update(Main *bmain,
BKE_collections_object_remove_duplicates(bmain);
}
- BKE_main_collection_sync_remap(bmain);
+ if (do_sync_collection) {
+ BKE_main_collection_sync_remap(bmain);
+ }
if (old_ob == NULL) {
for (Object *ob = bmain->objects.first; ob != NULL; ob = ob->id.next) {
@@ -567,7 +570,8 @@ static void libblock_remap_foreach_idpair_cb(ID *old_id, ID *new_id, void *user_
* Maybe we should do a per-ID callback for this instead? */
switch (GS(old_id->name)) {
case ID_OB:
- libblock_remap_data_postprocess_object_update(bmain, (Object *)old_id, (Object *)new_id);
+ libblock_remap_data_postprocess_object_update(
+ bmain, (Object *)old_id, (Object *)new_id, true);
break;
case ID_GR:
libblock_remap_data_postprocess_collection_update(
@@ -719,7 +723,7 @@ static void libblock_relink_foreach_idpair_cb(ID *old_id, ID *new_id, void *user
case ID_OB:
if (!is_object_update_processed) {
libblock_remap_data_postprocess_object_update(
- bmain, (Object *)old_id, (Object *)new_id);
+ bmain, (Object *)old_id, (Object *)new_id, true);
is_object_update_processed = true;
}
break;
@@ -781,11 +785,14 @@ void BKE_libblock_relink_multiple(Main *bmain,
(Collection *)id_iter :
((Scene *)id_iter)->master_collection;
/* No choice but to check whole objects once, and all children collections. */
- libblock_remap_data_postprocess_collection_update(bmain, owner_collection, NULL, NULL);
if (!is_object_update_processed) {
- libblock_remap_data_postprocess_object_update(bmain, NULL, NULL);
+ /* We only want to affect Object pointers here, not Collection ones, LayerCollections
+ * will be resynced as part of the call to
+ * `libblock_remap_data_postprocess_collection_update` below. */
+ libblock_remap_data_postprocess_object_update(bmain, NULL, NULL, false);
is_object_update_processed = true;
}
+ libblock_remap_data_postprocess_collection_update(bmain, owner_collection, NULL, NULL);
break;
}
default:
diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c
index bc569956f66..4caaf314888 100644
--- a/source/blender/blenkernel/intern/material.c
+++ b/source/blender/blenkernel/intern/material.c
@@ -1423,13 +1423,15 @@ static bool fill_texpaint_slots_cb(bNode *node, void *userdata)
case SH_NODE_TEX_IMAGE: {
TexPaintSlot *slot = &ma->texpaintslot[index];
slot->ima = (Image *)node->id;
- slot->interp = ((NodeTexImage *)node->storage)->interpolation;
+ NodeTexImage *storage = (NodeTexImage *)node->storage;
+ slot->interp = storage->interpolation;
+ slot->image_user = &storage->iuser;
/* for new renderer, we need to traverse the treeback in search of a UV node */
bNode *uvnode = nodetree_uv_node_recursive(node);
if (uvnode) {
- NodeShaderUVMap *storage = (NodeShaderUVMap *)uvnode->storage;
- slot->uvname = storage->uv_map;
+ NodeShaderUVMap *uv_storage = (NodeShaderUVMap *)uvnode->storage;
+ slot->uvname = uv_storage->uv_map;
/* set a value to index so UI knows that we have a valid pointer for the mesh */
slot->valid = true;
}
diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc
index 5afc3c0be3b..25d97d0bd3c 100644
--- a/source/blender/blenkernel/intern/mesh.cc
+++ b/source/blender/blenkernel/intern/mesh.cc
@@ -218,7 +218,7 @@ static void mesh_blend_write(BlendWriter *writer, ID *id, const void *id_address
mesh->mface = nullptr;
mesh->totface = 0;
memset(&mesh->fdata, 0, sizeof(mesh->fdata));
- memset(&mesh->runtime, 0, sizeof(mesh->runtime));
+ mesh->runtime = blender::dna::shallow_zero_initialize();
flayers = flayers_buff;
/* Do not store actual geometry data in case this is a library override ID. */
@@ -329,7 +329,7 @@ static void mesh_blend_read_data(BlendDataReader *reader, ID *id)
mesh->texflag &= ~ME_AUTOSPACE_EVALUATED;
mesh->edit_mesh = nullptr;
- memset(&mesh->runtime, 0, sizeof(mesh->runtime));
+ mesh->runtime = blender::dna::shallow_zero_initialize();
BKE_mesh_runtime_init_data(mesh);
/* happens with old files */
diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc
index 6abaa471877..ff953ef5b46 100644
--- a/source/blender/blenkernel/intern/mesh_convert.cc
+++ b/source/blender/blenkernel/intern/mesh_convert.cc
@@ -25,6 +25,7 @@
#include "BLI_utildefines.h"
#include "BKE_DerivedMesh.h"
+#include "BKE_curves.hh"
#include "BKE_deform.h"
#include "BKE_displist.h"
#include "BKE_editmesh.h"
@@ -700,7 +701,7 @@ void BKE_mesh_to_curve_nurblist(const Mesh *me, ListBase *nurblist, const int ed
VertLink *vl;
/* create new 'nurb' within the curve */
- nu = MEM_cnew<Nurb>("MeshNurb");
+ nu = MEM_new<Nurb>("MeshNurb", blender::dna::shallow_zero_initialize());
nu->pntsu = totpoly;
nu->pntsv = 1;
@@ -970,8 +971,7 @@ static Mesh *mesh_new_from_evaluated_curve_type_object(const Object *evaluated_o
}
const Curves *curves = get_evaluated_curves_from_object(evaluated_object);
if (curves) {
- std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curves);
- return blender::bke::curve_to_wire_mesh(*curve);
+ return blender::bke::curve_to_wire_mesh(blender::bke::CurvesGeometry::wrap(curves->geometry));
}
return nullptr;
}
@@ -1446,8 +1446,7 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
/* mesh_src might depend on mesh_dst, so we need to do everything with a local copy */
/* TODO(Sybren): the above claim came from 2.7x derived-mesh code (DM_to_mesh);
* check whether it is still true with Mesh */
- Mesh tmp;
- memcpy(&tmp, mesh_dst, sizeof(tmp));
+ Mesh tmp = blender::dna::shallow_copy(*mesh_dst);
int totvert, totedge /*, totface */ /* UNUSED */, totloop, totpoly;
bool did_shapekeys = false;
eCDAllocType alloctype = CD_DUPLICATE;
diff --git a/source/blender/blenkernel/intern/mesh_remap.c b/source/blender/blenkernel/intern/mesh_remap.c
index 9a4c01ec7aa..2e634a14872 100644
--- a/source/blender/blenkernel/intern/mesh_remap.c
+++ b/source/blender/blenkernel/intern/mesh_remap.c
@@ -1350,7 +1350,7 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode,
if (need_lnors_dst) {
short(*custom_nors_dst)[2] = CustomData_get_layer(ldata_dst, CD_CUSTOMLOOPNORMAL);
- /* Cache poly nors into a temp CDLayer. */
+ /* Cache loop normals into a temporary custom data layer. */
loop_nors_dst = CustomData_get_layer(ldata_dst, CD_NORMAL);
const bool do_loop_nors_dst = (loop_nors_dst == NULL);
if (!loop_nors_dst) {
diff --git a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc
index 3c7992ec3d7..7be4a6f2f94 100644
--- a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc
+++ b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc
@@ -23,11 +23,13 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "BKE_attribute.h"
#include "BKE_bvhutils.h"
#include "BKE_customdata.h"
#include "BKE_editmesh.h"
#include "BKE_lib_id.h"
#include "BKE_mesh.h"
+#include "BKE_mesh_mapping.h"
#include "BKE_mesh_remesh_voxel.h" /* own include */
#include "BKE_mesh_runtime.h"
@@ -365,30 +367,139 @@ void BKE_remesh_reproject_vertex_paint(Mesh *target, const Mesh *source)
BVHTreeFromMesh bvhtree = {nullptr};
BKE_bvhtree_from_mesh_get(&bvhtree, source, BVHTREE_FROM_VERTS, 2);
- int tot_color_layer = CustomData_number_of_layers(&source->vdata, CD_PROP_COLOR);
+ int i = 0;
+ const CustomDataLayer *layer;
- for (int layer_n = 0; layer_n < tot_color_layer; layer_n++) {
- const char *layer_name = CustomData_get_layer_name(&source->vdata, CD_PROP_COLOR, layer_n);
- CustomData_add_layer_named(
- &target->vdata, CD_PROP_COLOR, CD_CALLOC, nullptr, target->totvert, layer_name);
+ MeshElemMap *source_lmap = nullptr;
+ int *source_lmap_mem = nullptr;
+ MeshElemMap *target_lmap = nullptr;
+ int *target_lmap_mem = nullptr;
- MPropCol *target_color = (MPropCol *)CustomData_get_layer_n(
- &target->vdata, CD_PROP_COLOR, layer_n);
+ while ((layer = BKE_id_attribute_from_index(
+ const_cast<ID *>(&source->id), i++, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL))) {
+ AttributeDomain domain = BKE_id_attribute_domain(&source->id, layer);
+
+ CustomData *target_cdata = domain == ATTR_DOMAIN_POINT ? &target->vdata : &target->ldata;
+ const CustomData *source_cdata = domain == ATTR_DOMAIN_POINT ? &source->vdata : &source->ldata;
+
+ /* Check attribute exists in target. */
+ int layer_i = CustomData_get_named_layer_index(target_cdata, layer->type, layer->name);
+ if (layer_i == -1) {
+ int elem_num = domain == ATTR_DOMAIN_POINT ? target->totvert : target->totloop;
+
+ CustomData_add_layer_named(
+ target_cdata, layer->type, CD_CALLOC, nullptr, elem_num, layer->name);
+ layer_i = CustomData_get_named_layer_index(target_cdata, layer->type, layer->name);
+ }
+
+ size_t data_size = CustomData_sizeof(layer->type);
+ void *target_data = target_cdata->layers[layer_i].data;
+ void *source_data = layer->data;
MVert *target_verts = (MVert *)CustomData_get_layer(&target->vdata, CD_MVERT);
- const MPropCol *source_color = (const MPropCol *)CustomData_get_layer_n(
- &source->vdata, CD_PROP_COLOR, layer_n);
- for (int i = 0; i < target->totvert; i++) {
- BVHTreeNearest nearest;
- nearest.index = -1;
- nearest.dist_sq = FLT_MAX;
- BLI_bvhtree_find_nearest(
- bvhtree.tree, target_verts[i].co, &nearest, bvhtree.nearest_callback, &bvhtree);
- if (nearest.index != -1) {
- copy_v4_v4(target_color[i].color, source_color[nearest.index].color);
+
+ if (domain == ATTR_DOMAIN_POINT) {
+ for (int i = 0; i < target->totvert; i++) {
+ BVHTreeNearest nearest;
+ nearest.index = -1;
+ nearest.dist_sq = FLT_MAX;
+ BLI_bvhtree_find_nearest(
+ bvhtree.tree, target_verts[i].co, &nearest, bvhtree.nearest_callback, &bvhtree);
+
+ if (nearest.index != -1) {
+ memcpy(POINTER_OFFSET(target_data, (size_t)i * data_size),
+ POINTER_OFFSET(source_data, (size_t)nearest.index * data_size),
+ data_size);
+ }
+ }
+ }
+ else {
+ /* Lazily init vertex -> loop maps. */
+ if (!source_lmap) {
+ const MPoly *source_polys = (MPoly *)CustomData_get_layer(&source->pdata, CD_MPOLY);
+ const MLoop *source_loops = (MLoop *)CustomData_get_layer(&source->ldata, CD_MLOOP);
+ const MPoly *target_polys = (MPoly *)CustomData_get_layer(&target->pdata, CD_MPOLY);
+ const MLoop *target_loops = (MLoop *)CustomData_get_layer(&target->ldata, CD_MLOOP);
+
+ BKE_mesh_vert_loop_map_create(&source_lmap,
+ &source_lmap_mem,
+ source_polys,
+ source_loops,
+ source->totvert,
+ source->totpoly,
+ source->totloop);
+
+ BKE_mesh_vert_loop_map_create(&target_lmap,
+ &target_lmap_mem,
+ target_polys,
+ target_loops,
+ target->totvert,
+ target->totpoly,
+ target->totloop);
+ }
+
+ for (int i = 0; i < target->totvert; i++) {
+ BVHTreeNearest nearest;
+ nearest.index = -1;
+ nearest.dist_sq = FLT_MAX;
+ BLI_bvhtree_find_nearest(
+ bvhtree.tree, target_verts[i].co, &nearest, bvhtree.nearest_callback, &bvhtree);
+
+ if (nearest.index == -1) {
+ continue;
+ }
+
+ MeshElemMap *source_loops = source_lmap + nearest.index;
+ MeshElemMap *target_loops = target_lmap + i;
+
+ if (target_loops->count == 0 || source_loops->count == 0) {
+ continue;
+ }
+
+ /*
+ * Average color data for loops around the source vertex into
+ * the first target loop around the target vertex
+ */
+
+ CustomData_interp(source_cdata,
+ target_cdata,
+ source_loops->indices,
+ nullptr,
+ nullptr,
+ source_loops->count,
+ target_loops->indices[0]);
+
+ void *elem = POINTER_OFFSET(target_data, (size_t)target_loops->indices[0] * data_size);
+
+ /* Copy to rest of target loops. */
+ for (int j = 1; j < target_loops->count; j++) {
+ memcpy(POINTER_OFFSET(target_data, (size_t)target_loops->indices[j] * data_size),
+ elem,
+ data_size);
+ }
}
}
}
+
+ MEM_SAFE_FREE(source_lmap);
+ MEM_SAFE_FREE(source_lmap_mem);
+ MEM_SAFE_FREE(target_lmap);
+ MEM_SAFE_FREE(target_lmap_mem);
free_bvhtree_from_mesh(&bvhtree);
+
+ /* Transfer active/render color attributes */
+
+ CustomDataLayer *active_layer = BKE_id_attributes_active_color_get(&source->id);
+ CustomDataLayer *render_layer = BKE_id_attributes_render_color_get(&source->id);
+
+ if (active_layer) {
+ BKE_id_attributes_active_color_set(
+ &target->id, BKE_id_attributes_color_find(&target->id, active_layer->name));
+ }
+
+ if (render_layer) {
+ BKE_id_attributes_render_color_set(
+ &target->id, BKE_id_attributes_color_find(&target->id, render_layer->name));
+ }
}
struct Mesh *BKE_mesh_remesh_voxel_fix_poles(const Mesh *mesh)
diff --git a/source/blender/blenkernel/intern/mesh_runtime.c b/source/blender/blenkernel/intern/mesh_runtime.cc
index 7bd52abeb0d..b06e867cf37 100644
--- a/source/blender/blenkernel/intern/mesh_runtime.c
+++ b/source/blender/blenkernel/intern/mesh_runtime.cc
@@ -14,8 +14,7 @@
#include "DNA_object_types.h"
#include "BLI_math_geom.h"
-#include "BLI_task.h"
-#include "BLI_threads.h"
+#include "BLI_task.hh"
#include "BKE_bvhutils.h"
#include "BKE_lib_id.h"
@@ -35,12 +34,12 @@
*/
static void mesh_runtime_init_mutexes(Mesh *mesh)
{
- mesh->runtime.eval_mutex = MEM_mallocN(sizeof(ThreadMutex), "mesh runtime eval_mutex");
- BLI_mutex_init(mesh->runtime.eval_mutex);
- mesh->runtime.normals_mutex = MEM_mallocN(sizeof(ThreadMutex), "mesh runtime normals_mutex");
- BLI_mutex_init(mesh->runtime.normals_mutex);
- mesh->runtime.render_mutex = MEM_mallocN(sizeof(ThreadMutex), "mesh runtime render_mutex");
- BLI_mutex_init(mesh->runtime.render_mutex);
+ mesh->runtime.eval_mutex = MEM_new<ThreadMutex>("mesh runtime eval_mutex");
+ BLI_mutex_init(static_cast<ThreadMutex *>(mesh->runtime.eval_mutex));
+ mesh->runtime.normals_mutex = MEM_new<ThreadMutex>("mesh runtime normals_mutex");
+ BLI_mutex_init(static_cast<ThreadMutex *>(mesh->runtime.normals_mutex));
+ mesh->runtime.render_mutex = MEM_new<ThreadMutex>("mesh runtime render_mutex");
+ BLI_mutex_init(static_cast<ThreadMutex *>(mesh->runtime.render_mutex));
}
/**
@@ -48,20 +47,20 @@ static void mesh_runtime_init_mutexes(Mesh *mesh)
*/
static void mesh_runtime_free_mutexes(Mesh *mesh)
{
- if (mesh->runtime.eval_mutex != NULL) {
- BLI_mutex_end(mesh->runtime.eval_mutex);
+ if (mesh->runtime.eval_mutex != nullptr) {
+ BLI_mutex_end(static_cast<ThreadMutex *>(mesh->runtime.eval_mutex));
MEM_freeN(mesh->runtime.eval_mutex);
- mesh->runtime.eval_mutex = NULL;
+ mesh->runtime.eval_mutex = nullptr;
}
- if (mesh->runtime.normals_mutex != NULL) {
- BLI_mutex_end(mesh->runtime.normals_mutex);
+ if (mesh->runtime.normals_mutex != nullptr) {
+ BLI_mutex_end(static_cast<ThreadMutex *>(mesh->runtime.normals_mutex));
MEM_freeN(mesh->runtime.normals_mutex);
- mesh->runtime.normals_mutex = NULL;
+ mesh->runtime.normals_mutex = nullptr;
}
- if (mesh->runtime.render_mutex != NULL) {
- BLI_mutex_end(mesh->runtime.render_mutex);
+ if (mesh->runtime.render_mutex != nullptr) {
+ BLI_mutex_end(static_cast<ThreadMutex *>(mesh->runtime.render_mutex));
MEM_freeN(mesh->runtime.render_mutex);
- mesh->runtime.render_mutex = NULL;
+ mesh->runtime.render_mutex = nullptr;
}
}
@@ -80,28 +79,28 @@ void BKE_mesh_runtime_reset_on_copy(Mesh *mesh, const int UNUSED(flag))
{
Mesh_Runtime *runtime = &mesh->runtime;
- runtime->mesh_eval = NULL;
- runtime->edit_data = NULL;
- runtime->batch_cache = NULL;
- runtime->subdiv_ccg = NULL;
- memset(&runtime->looptris, 0, sizeof(runtime->looptris));
- runtime->bvh_cache = NULL;
- runtime->shrinkwrap_data = NULL;
+ runtime->mesh_eval = nullptr;
+ runtime->edit_data = nullptr;
+ runtime->batch_cache = nullptr;
+ runtime->subdiv_ccg = nullptr;
+ runtime->looptris = blender::dna::shallow_zero_initialize();
+ runtime->bvh_cache = nullptr;
+ runtime->shrinkwrap_data = nullptr;
runtime->vert_normals_dirty = true;
runtime->poly_normals_dirty = true;
- runtime->vert_normals = NULL;
- runtime->poly_normals = NULL;
+ runtime->vert_normals = nullptr;
+ runtime->poly_normals = nullptr;
mesh_runtime_init_mutexes(mesh);
}
void BKE_mesh_runtime_clear_cache(Mesh *mesh)
{
- if (mesh->runtime.mesh_eval != NULL) {
- mesh->runtime.mesh_eval->edit_mesh = NULL;
- BKE_id_free(NULL, mesh->runtime.mesh_eval);
- mesh->runtime.mesh_eval = NULL;
+ if (mesh->runtime.mesh_eval != nullptr) {
+ mesh->runtime.mesh_eval->edit_mesh = nullptr;
+ BKE_id_free(nullptr, mesh->runtime.mesh_eval);
+ mesh->runtime.mesh_eval = nullptr;
}
BKE_mesh_runtime_clear_geometry(mesh);
BKE_mesh_batch_cache_free(mesh);
@@ -121,7 +120,7 @@ static void mesh_ensure_looptri_data(Mesh *mesh)
const uint totpoly = mesh->totpoly;
const int looptris_len = poly_to_tri_count(totpoly, mesh->totloop);
- BLI_assert(mesh->runtime.looptris.array_wip == NULL);
+ BLI_assert(mesh->runtime.looptris.array_wip == nullptr);
SWAP(MLoopTri *, mesh->runtime.looptris.array, mesh->runtime.looptris.array_wip);
@@ -133,9 +132,9 @@ static void mesh_ensure_looptri_data(Mesh *mesh)
}
if (totpoly) {
- if (mesh->runtime.looptris.array_wip == NULL) {
- mesh->runtime.looptris.array_wip = MEM_malloc_arrayN(
- looptris_len, sizeof(*mesh->runtime.looptris.array_wip), __func__);
+ if (mesh->runtime.looptris.array_wip == nullptr) {
+ mesh->runtime.looptris.array_wip = static_cast<MLoopTri *>(
+ MEM_malloc_arrayN(looptris_len, sizeof(*mesh->runtime.looptris.array_wip), __func__));
mesh->runtime.looptris.len_alloc = looptris_len;
}
@@ -146,7 +145,7 @@ static void mesh_ensure_looptri_data(Mesh *mesh)
void BKE_mesh_runtime_looptri_recalc(Mesh *mesh)
{
mesh_ensure_looptri_data(mesh);
- BLI_assert(mesh->totpoly == 0 || mesh->runtime.looptris.array_wip != NULL);
+ BLI_assert(mesh->totpoly == 0 || mesh->runtime.looptris.array_wip != nullptr);
BKE_mesh_recalc_looptri(mesh->mloop,
mesh->mpoly,
@@ -155,11 +154,11 @@ void BKE_mesh_runtime_looptri_recalc(Mesh *mesh)
mesh->totpoly,
mesh->runtime.looptris.array_wip);
- BLI_assert(mesh->runtime.looptris.array == NULL);
+ BLI_assert(mesh->runtime.looptris.array == nullptr);
atomic_cas_ptr((void **)&mesh->runtime.looptris.array,
mesh->runtime.looptris.array,
mesh->runtime.looptris.array_wip);
- mesh->runtime.looptris.array_wip = NULL;
+ mesh->runtime.looptris.array_wip = nullptr;
}
int BKE_mesh_runtime_looptri_len(const Mesh *mesh)
@@ -170,12 +169,6 @@ int BKE_mesh_runtime_looptri_len(const Mesh *mesh)
return looptri_len;
}
-static void mesh_runtime_looptri_recalc_isolated(void *userdata)
-{
- Mesh *mesh = userdata;
- BKE_mesh_runtime_looptri_recalc(mesh);
-}
-
const MLoopTri *BKE_mesh_runtime_looptri_ensure(const Mesh *mesh)
{
ThreadMutex *mesh_eval_mutex = (ThreadMutex *)mesh->runtime.eval_mutex;
@@ -183,12 +176,13 @@ const MLoopTri *BKE_mesh_runtime_looptri_ensure(const Mesh *mesh)
MLoopTri *looptri = mesh->runtime.looptris.array;
- if (looptri != NULL) {
+ if (looptri != nullptr) {
BLI_assert(BKE_mesh_runtime_looptri_len(mesh) == mesh->runtime.looptris.len);
}
else {
/* Must isolate multithreaded tasks while holding a mutex lock. */
- BLI_task_isolate(mesh_runtime_looptri_recalc_isolated, (void *)mesh);
+ blender::threading::isolate_task(
+ [&]() { BKE_mesh_runtime_looptri_recalc(const_cast<Mesh *>(mesh)); });
looptri = mesh->runtime.looptris.array;
}
@@ -211,18 +205,18 @@ void BKE_mesh_runtime_verttri_from_looptri(MVertTri *r_verttri,
bool BKE_mesh_runtime_ensure_edit_data(struct Mesh *mesh)
{
- if (mesh->runtime.edit_data != NULL) {
+ if (mesh->runtime.edit_data != nullptr) {
return false;
}
- mesh->runtime.edit_data = MEM_callocN(sizeof(EditMeshData), "EditMeshData");
+ mesh->runtime.edit_data = MEM_cnew<EditMeshData>(__func__);
return true;
}
bool BKE_mesh_runtime_reset_edit_data(Mesh *mesh)
{
EditMeshData *edit_data = mesh->runtime.edit_data;
- if (edit_data == NULL) {
+ if (edit_data == nullptr) {
return false;
}
@@ -236,13 +230,13 @@ bool BKE_mesh_runtime_reset_edit_data(Mesh *mesh)
bool BKE_mesh_runtime_clear_edit_data(Mesh *mesh)
{
- if (mesh->runtime.edit_data == NULL) {
+ if (mesh->runtime.edit_data == nullptr) {
return false;
}
BKE_mesh_runtime_reset_edit_data(mesh);
MEM_freeN(mesh->runtime.edit_data);
- mesh->runtime.edit_data = NULL;
+ mesh->runtime.edit_data = nullptr;
return true;
}
@@ -251,13 +245,13 @@ void BKE_mesh_runtime_clear_geometry(Mesh *mesh)
{
if (mesh->runtime.bvh_cache) {
bvhcache_free(mesh->runtime.bvh_cache);
- mesh->runtime.bvh_cache = NULL;
+ mesh->runtime.bvh_cache = nullptr;
}
MEM_SAFE_FREE(mesh->runtime.looptris.array);
/* TODO(sergey): Does this really belong here? */
- if (mesh->runtime.subdiv_ccg != NULL) {
+ if (mesh->runtime.subdiv_ccg != nullptr) {
BKE_subdiv_ccg_destroy(mesh->runtime.subdiv_ccg);
- mesh->runtime.subdiv_ccg = NULL;
+ mesh->runtime.subdiv_ccg = nullptr;
}
BKE_shrinkwrap_discard_boundary_data(mesh);
}
@@ -269,8 +263,8 @@ void BKE_mesh_runtime_clear_geometry(Mesh *mesh)
* \{ */
/* Draw Engine */
-void (*BKE_mesh_batch_cache_dirty_tag_cb)(Mesh *me, eMeshBatchDirtyMode mode) = NULL;
-void (*BKE_mesh_batch_cache_free_cb)(Mesh *me) = NULL;
+void (*BKE_mesh_batch_cache_dirty_tag_cb)(Mesh *me, eMeshBatchDirtyMode mode) = nullptr;
+void (*BKE_mesh_batch_cache_free_cb)(Mesh *me) = nullptr;
void BKE_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode)
{
diff --git a/source/blender/blenkernel/intern/mesh_wrapper.c b/source/blender/blenkernel/intern/mesh_wrapper.cc
index f9fcaa0dceb..8291765c2ef 100644
--- a/source/blender/blenkernel/intern/mesh_wrapper.c
+++ b/source/blender/blenkernel/intern/mesh_wrapper.cc
@@ -27,7 +27,7 @@
#include "BLI_ghash.h"
#include "BLI_math.h"
-#include "BLI_task.h"
+#include "BLI_task.hh"
#include "BLI_threads.h"
#include "BLI_utildefines.h"
@@ -51,7 +51,7 @@ Mesh *BKE_mesh_wrapper_from_editmesh_with_coords(BMEditMesh *em,
const float (*vert_coords)[3],
const Mesh *me_settings)
{
- Mesh *me = BKE_id_new_nomain(ID_ME, NULL);
+ Mesh *me = static_cast<Mesh *>(BKE_id_new_nomain(ID_ME, nullptr));
BKE_mesh_copy_parameters_for_eval(me, me_settings);
BKE_mesh_runtime_ensure_edit_data(me);
@@ -63,10 +63,10 @@ Mesh *BKE_mesh_wrapper_from_editmesh_with_coords(BMEditMesh *em,
/* Use edit-mesh directly where possible. */
me->runtime.is_original = true;
- me->edit_mesh = MEM_dupallocN(em);
+ me->edit_mesh = static_cast<BMEditMesh *>(MEM_dupallocN(em));
me->edit_mesh->is_shallow_copy = true;
-/* Make sure, we crash if these are ever used. */
+ /* Make sure we crash if these are ever used. */
#ifdef DEBUG
me->totvert = INT_MAX;
me->totedge = INT_MAX;
@@ -88,55 +88,7 @@ Mesh *BKE_mesh_wrapper_from_editmesh(BMEditMesh *em,
const CustomData_MeshMasks *cd_mask_extra,
const Mesh *me_settings)
{
- return BKE_mesh_wrapper_from_editmesh_with_coords(em, cd_mask_extra, NULL, me_settings);
-}
-
-static void mesh_wrapper_ensure_mdata_isolated(void *userdata)
-{
- Mesh *me = userdata;
-
- const eMeshWrapperType geom_type_orig = me->runtime.wrapper_type;
- me->runtime.wrapper_type = ME_WRAPPER_TYPE_MDATA;
-
- switch (geom_type_orig) {
- case ME_WRAPPER_TYPE_MDATA:
- case ME_WRAPPER_TYPE_SUBD: {
- break; /* Quiet warning. */
- }
- case ME_WRAPPER_TYPE_BMESH: {
- me->totvert = 0;
- me->totedge = 0;
- me->totpoly = 0;
- me->totloop = 0;
-
- BLI_assert(me->edit_mesh != NULL);
- BLI_assert(me->runtime.edit_data != NULL);
-
- BMEditMesh *em = me->edit_mesh;
- BM_mesh_bm_to_me_for_eval(em->bm, me, &me->runtime.cd_mask_extra);
-
- /* Adding original index layers assumes that all BMesh mesh wrappers are created from
- * original edit mode meshes (the only case where adding original indices makes sense).
- * If that assumption is broken, the layers might be incorrect in that they might not
- * actually be "original".
- *
- * There is also a performance aspect, where this also assumes that original indices are
- * always needed when converting an edit mesh to a mesh. That might be wrong, but it's not
- * harmful. */
- BKE_mesh_ensure_default_orig_index_customdata(me);
-
- EditMeshData *edit_data = me->runtime.edit_data;
- if (edit_data->vertexCos) {
- BKE_mesh_vert_coords_apply(me, edit_data->vertexCos);
- me->runtime.is_original = false;
- }
- break;
- }
- }
-
- if (me->runtime.wrapper_type_finalize) {
- BKE_mesh_wrapper_deferred_finalize(me, &me->runtime.cd_mask_extra);
- }
+ return BKE_mesh_wrapper_from_editmesh_with_coords(em, cd_mask_extra, nullptr, me_settings);
}
void BKE_mesh_wrapper_ensure_mdata(Mesh *me)
@@ -150,7 +102,51 @@ void BKE_mesh_wrapper_ensure_mdata(Mesh *me)
}
/* Must isolate multithreaded tasks while holding a mutex lock. */
- BLI_task_isolate(mesh_wrapper_ensure_mdata_isolated, me);
+ blender::threading::isolate_task([&]() {
+ const eMeshWrapperType geom_type_orig = static_cast<eMeshWrapperType>(
+ me->runtime.wrapper_type);
+ me->runtime.wrapper_type = ME_WRAPPER_TYPE_MDATA;
+
+ switch (geom_type_orig) {
+ case ME_WRAPPER_TYPE_MDATA:
+ case ME_WRAPPER_TYPE_SUBD: {
+ break; /* Quiet warning. */
+ }
+ case ME_WRAPPER_TYPE_BMESH: {
+ me->totvert = 0;
+ me->totedge = 0;
+ me->totpoly = 0;
+ me->totloop = 0;
+
+ BLI_assert(me->edit_mesh != nullptr);
+ BLI_assert(me->runtime.edit_data != nullptr);
+
+ BMEditMesh *em = me->edit_mesh;
+ BM_mesh_bm_to_me_for_eval(em->bm, me, &me->runtime.cd_mask_extra);
+
+ /* Adding original index layers assumes that all BMesh mesh wrappers are created from
+ * original edit mode meshes (the only case where adding original indices makes sense).
+ * If that assumption is broken, the layers might be incorrect in that they might not
+ * actually be "original".
+ *
+ * There is also a performance aspect, where this also assumes that original indices are
+ * always needed when converting an edit mesh to a mesh. That might be wrong, but it's not
+ * harmful. */
+ BKE_mesh_ensure_default_orig_index_customdata(me);
+
+ EditMeshData *edit_data = me->runtime.edit_data;
+ if (edit_data->vertexCos) {
+ BKE_mesh_vert_coords_apply(me, edit_data->vertexCos);
+ me->runtime.is_original = false;
+ }
+ break;
+ }
+ }
+
+ if (me->runtime.wrapper_type_finalize) {
+ BKE_mesh_wrapper_deferred_finalize(me, &me->runtime.cd_mask_extra);
+ }
+ });
BLI_mutex_unlock(mesh_eval_mutex);
}
@@ -181,7 +177,7 @@ void BKE_mesh_wrapper_vert_coords_copy(const Mesh *me,
BMesh *bm = me->edit_mesh->bm;
BLI_assert(vert_coords_len <= bm->totvert);
EditMeshData *edit_data = me->runtime.edit_data;
- if (edit_data->vertexCos != NULL) {
+ if (edit_data->vertexCos != nullptr) {
for (int i = 0; i < vert_coords_len; i++) {
copy_v3_v3(vert_coords[i], edit_data->vertexCos[i]);
}
@@ -219,7 +215,7 @@ void BKE_mesh_wrapper_vert_coords_copy_with_mat4(const Mesh *me,
BMesh *bm = me->edit_mesh->bm;
BLI_assert(vert_coords_len == bm->totvert);
EditMeshData *edit_data = me->runtime.edit_data;
- if (edit_data->vertexCos != NULL) {
+ if (edit_data->vertexCos != nullptr) {
for (int i = 0; i < vert_coords_len; i++) {
mul_v3_m4v3(vert_coords[i], mat, edit_data->vertexCos[i]);
}
@@ -340,7 +336,7 @@ static Mesh *mesh_wrapper_ensure_subdivision(const Object *ob, Mesh *me)
SubsurfRuntimeData *runtime_data = BKE_subsurf_modifier_ensure_runtime(smd);
Subdiv *subdiv = BKE_subsurf_modifier_subdiv_descriptor_ensure(smd, &subdiv_settings, me, false);
- if (subdiv == NULL) {
+ if (subdiv == nullptr) {
/* Happens on bad topology, but also on empty input mesh. */
return me;
}
@@ -352,8 +348,8 @@ static Mesh *mesh_wrapper_ensure_subdivision(const Object *ob, Mesh *me)
}
if (subdiv_mesh != me) {
- if (me->runtime.mesh_eval != NULL) {
- BKE_id_free(NULL, me->runtime.mesh_eval);
+ if (me->runtime.mesh_eval != nullptr) {
+ BKE_id_free(nullptr, me->runtime.mesh_eval);
}
me->runtime.mesh_eval = subdiv_mesh;
me->runtime.wrapper_type = ME_WRAPPER_TYPE_SUBD;
@@ -362,20 +358,6 @@ static Mesh *mesh_wrapper_ensure_subdivision(const Object *ob, Mesh *me)
return me->runtime.mesh_eval;
}
-typedef struct SubdivisionWrapperIsolatedTaskData {
- const Object *ob;
- Mesh *me;
- Mesh *result;
-} SubdivisionWrapperIsolatedTaskData;
-
-static void mesh_wrapper_ensure_subdivision_isolated(void *userdata)
-{
- SubdivisionWrapperIsolatedTaskData *task_data = (SubdivisionWrapperIsolatedTaskData *)userdata;
- const Object *ob = task_data->ob;
- Mesh *me = task_data->me;
- task_data->result = mesh_wrapper_ensure_subdivision(ob, me);
-}
-
Mesh *BKE_mesh_wrapper_ensure_subdivision(const Object *ob, Mesh *me)
{
ThreadMutex *mesh_eval_mutex = (ThreadMutex *)me->runtime.eval_mutex;
@@ -386,15 +368,13 @@ Mesh *BKE_mesh_wrapper_ensure_subdivision(const Object *ob, Mesh *me)
return me->runtime.mesh_eval;
}
- SubdivisionWrapperIsolatedTaskData task_data;
- task_data.ob = ob;
- task_data.me = me;
+ Mesh *result;
/* Must isolate multithreaded tasks while holding a mutex lock. */
- BLI_task_isolate(mesh_wrapper_ensure_subdivision_isolated, &task_data);
+ blender::threading::isolate_task([&]() { result = mesh_wrapper_ensure_subdivision(ob, me); });
BLI_mutex_unlock(mesh_eval_mutex);
- return task_data.result;
+ return result;
}
/** \} */
diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c
index a94562a32ec..9b6d768b2d3 100644
--- a/source/blender/blenkernel/intern/nla.c
+++ b/source/blender/blenkernel/intern/nla.c
@@ -1977,8 +1977,11 @@ bool BKE_nla_tweakmode_enter(AnimData *adt)
/* go over all the tracks after AND INCLUDING the active one, tagging them as being disabled
* - the active track needs to also be tagged, otherwise, it'll overlap with the tweaks going on
*/
- for (nlt = activeTrack; nlt; nlt = nlt->next) {
- nlt->flag |= NLATRACK_DISABLED;
+ activeTrack->flag |= NLATRACK_DISABLED;
+ if ((adt->flag & ADT_NLA_EVAL_UPPER_TRACKS) == 0) {
+ for (nlt = activeTrack->next; nlt; nlt = nlt->next) {
+ nlt->flag |= NLATRACK_DISABLED;
+ }
}
/* handle AnimData level changes:
diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc
index 7efdd855a04..4acccca322a 100644
--- a/source/blender/blenkernel/intern/node.cc
+++ b/source/blender/blenkernel/intern/node.cc
@@ -719,7 +719,7 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree)
}
case SH_NODE_TEX_POINTDENSITY: {
NodeShaderTexPointDensity *npd = (NodeShaderTexPointDensity *)node->storage;
- memset(&npd->pd, 0, sizeof(npd->pd));
+ npd->pd = blender::dna::shallow_zero_initialize();
break;
}
case SH_NODE_TEX_IMAGE: {
diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc
index a54e2910b79..5ff1f6b950f 100644
--- a/source/blender/blenkernel/intern/object.cc
+++ b/source/blender/blenkernel/intern/object.cc
@@ -3087,12 +3087,12 @@ void BKE_object_matrix_local_get(struct Object *ob, float r_mat[4][4])
static bool ob_parcurve(Object *ob, Object *par, float r_mat[4][4])
{
Curve *cu = (Curve *)par->data;
- float vec[4], dir[3], quat[4], radius, ctime;
+ float vec[4], quat[4], radius, ctime;
/* NOTE: Curve cache is supposed to be evaluated here already, however there
* are cases where we can not guarantee that. This includes, for example,
* dependency cycles. We can't correct anything from here, since that would
- * cause a threading conflicts.
+ * cause threading conflicts.
*
* TODO(sergey): Some of the legit looking cases like T56619 need to be
* looked into, and maybe curve cache (and other dependencies) are to be
@@ -3125,7 +3125,7 @@ static bool ob_parcurve(Object *ob, Object *par, float r_mat[4][4])
/* vec: 4 items! */
if (BKE_where_on_path(
- par, ctime, vec, dir, (cu->flag & CU_FOLLOW) ? quat : nullptr, &radius, nullptr)) {
+ par, ctime, vec, nullptr, (cu->flag & CU_FOLLOW) ? quat : nullptr, &radius, nullptr)) {
if (cu->flag & CU_FOLLOW) {
quat_apply_track(quat, ob->trackflag, ob->upflag);
normalize_qt(quat);
@@ -3558,6 +3558,55 @@ void BKE_object_apply_mat4(Object *ob,
BKE_object_apply_mat4_ex(ob, mat, use_parent ? ob->parent : nullptr, ob->parentinv, use_compat);
}
+void BKE_object_apply_parent_inverse(struct Object *ob)
+{
+ /*
+ * Use parent's world transform as the child's origin.
+ *
+ * Let:
+ * local = identity
+ * world = orthonormalized(parent)
+ *
+ * Then:
+ * world = parent @ parentinv @ local
+ * inv(parent) @ world = parentinv
+ * parentinv = inv(parent) @ world
+ *
+ * NOTE: If ob->obmat has shear, then this `parentinv` is insufficient because
+ * parent @ parentinv => shearless result
+ *
+ * Thus, local will have shear which cannot be decomposed into TRS:
+ * local = inv(parent @ parentinv) @ world
+ *
+ * This is currently not supported for consistency in the handling of shear during the other
+ * parenting ops: Parent (Keep Transform), Clear [Parent] and Keep Transform.
+ */
+ float par_locrot[4][4], par_imat[4][4];
+ BKE_object_get_parent_matrix(ob, ob->parent, par_locrot);
+ invert_m4_m4(par_imat, par_locrot);
+
+ orthogonalize_m4_stable(par_locrot, 0, true);
+
+ mul_m4_m4m4(ob->parentinv, par_imat, par_locrot);
+
+ /* Now, preserve `world` given the new `parentinv`.
+ *
+ * world = parent @ parentinv @ local
+ * inv(parent) @ world = parentinv @ local
+ * inv(parentinv) @ inv(parent) @ world = local
+ *
+ * local = inv(parentinv) @ inv(parent) @ world
+ */
+ float ob_local[4][4];
+ copy_m4_m4(ob_local, ob->parentinv);
+ invert_m4(ob_local);
+ mul_m4_m4_post(ob_local, par_imat);
+ mul_m4_m4_post(ob_local, ob->obmat);
+
+ /* Send use_compat=False so the rotation is predictable. */
+ BKE_object_apply_mat4(ob, ob_local, false, false);
+}
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/blenkernel/intern/object_dupli.cc b/source/blender/blenkernel/intern/object_dupli.cc
index 0ae8f144583..407a2c8955c 100644
--- a/source/blender/blenkernel/intern/object_dupli.cc
+++ b/source/blender/blenkernel/intern/object_dupli.cc
@@ -787,7 +787,7 @@ static void make_duplis_geometry_set_impl(const DupliContext *ctx,
dupli->ob_data = (ID *)volume;
}
}
- if (!ELEM(ctx->object->type, OB_CURVES_LEGACY, OB_FONT) || geometry_set_is_instance) {
+ if (!ELEM(ctx->object->type, OB_CURVES_LEGACY, OB_FONT, OB_CURVES) || geometry_set_is_instance) {
if (const CurveComponent *component = geometry_set.get_component_for_read<CurveComponent>()) {
if (const Curve *curve = component->get_curve_for_render()) {
DupliObject *dupli = make_dupli(ctx, ctx->object, parent_transform, component_index++);
diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c
index eb3f47760fc..5fd7984ea90 100644
--- a/source/blender/blenkernel/intern/paint.c
+++ b/source/blender/blenkernel/intern/paint.c
@@ -1511,6 +1511,8 @@ void BKE_sculptsession_free(Object *ob)
BKE_sculptsession_free_vwpaint_data(ob->sculpt);
+ MEM_SAFE_FREE(ss->last_paint_canvas_key);
+
MEM_freeN(ss);
ob->sculpt = NULL;
@@ -1771,6 +1773,24 @@ static void sculpt_update_object(Depsgraph *depsgraph,
}
}
+ /*
+ * We should rebuild the PBVH_pixels when painting canvas changes.
+ *
+ * The relevant changes are stored/encoded in the paint canvas key.
+ * These include the active uv map, and resolutions.
+ */
+ if (U.experimental.use_sculpt_texture_paint && ss->pbvh) {
+ char *paint_canvas_key = BKE_paint_canvas_key_get(&scene->toolsettings->paint_mode, ob);
+ if (ss->last_paint_canvas_key == NULL || !STREQ(paint_canvas_key, ss->last_paint_canvas_key)) {
+ MEM_SAFE_FREE(ss->last_paint_canvas_key);
+ ss->last_paint_canvas_key = paint_canvas_key;
+ BKE_pbvh_mark_rebuild_pixels(ss->pbvh);
+ }
+ else {
+ MEM_freeN(paint_canvas_key);
+ }
+ }
+
/* We could be more precise when we have access to the active tool. */
const bool use_paint_slots = (ob->mode & OB_MODE_SCULPT) != 0;
if (use_paint_slots) {
@@ -1849,6 +1869,10 @@ void BKE_sculpt_color_layer_create_if_needed(struct Object *object)
BKE_id_attributes_active_color_set(&orig_me->id, layer);
DEG_id_tag_update(&orig_me->id, ID_RECALC_GEOMETRY_ALL_MODES);
+
+ if (object->sculpt && object->sculpt->pbvh) {
+ BKE_pbvh_update_active_vcol(object->sculpt->pbvh, orig_me);
+ }
}
void BKE_sculpt_update_object_for_edit(
diff --git a/source/blender/blenkernel/intern/paint_canvas.cc b/source/blender/blenkernel/intern/paint_canvas.cc
new file mode 100644
index 00000000000..b72418d88c0
--- /dev/null
+++ b/source/blender/blenkernel/intern/paint_canvas.cc
@@ -0,0 +1,131 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "BLI_compiler_compat.h"
+
+#include "DNA_material_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_scene_types.h"
+
+#include "BKE_customdata.h"
+#include "BKE_image.h"
+#include "BKE_material.h"
+#include "BKE_paint.h"
+
+#include "IMB_imbuf_types.h"
+
+namespace blender::bke::paint::canvas {
+static TexPaintSlot *get_active_slot(Object *ob)
+{
+ Material *mat = BKE_object_material_get(ob, ob->actcol);
+ if (mat == nullptr) {
+ return nullptr;
+ }
+ if (mat->texpaintslot == nullptr) {
+ return nullptr;
+ }
+ if (mat->paint_active_slot >= mat->tot_slots) {
+ return nullptr;
+ }
+
+ TexPaintSlot *slot = &mat->texpaintslot[mat->paint_active_slot];
+ return slot;
+}
+
+} // namespace blender::bke::paint::canvas
+
+extern "C" {
+
+using namespace blender::bke::paint::canvas;
+
+bool BKE_paint_canvas_image_get(PaintModeSettings *settings,
+ Object *ob,
+ Image **r_image,
+ ImageUser **r_image_user)
+{
+ *r_image = nullptr;
+ *r_image_user = nullptr;
+
+ switch (settings->canvas_source) {
+ case PAINT_CANVAS_SOURCE_COLOR_ATTRIBUTE:
+ break;
+
+ case PAINT_CANVAS_SOURCE_IMAGE:
+ *r_image = settings->canvas_image;
+ *r_image_user = &settings->image_user;
+ break;
+
+ case PAINT_CANVAS_SOURCE_MATERIAL: {
+ TexPaintSlot *slot = get_active_slot(ob);
+ if (slot == nullptr) {
+ break;
+ }
+
+ *r_image = slot->ima;
+ *r_image_user = slot->image_user;
+ break;
+ }
+ }
+ return *r_image != nullptr;
+}
+
+int BKE_paint_canvas_uvmap_layer_index_get(const struct PaintModeSettings *settings,
+ struct Object *ob)
+{
+ switch (settings->canvas_source) {
+ case PAINT_CANVAS_SOURCE_COLOR_ATTRIBUTE:
+ return -1;
+ case PAINT_CANVAS_SOURCE_IMAGE: {
+ /* Use active uv map of the object. */
+ if (ob->type != OB_MESH) {
+ return -1;
+ }
+
+ const Mesh *mesh = static_cast<Mesh *>(ob->data);
+ return CustomData_get_active_layer_index(&mesh->ldata, CD_MLOOPUV);
+ }
+ case PAINT_CANVAS_SOURCE_MATERIAL: {
+ /* Use uv map of the canvas. */
+ TexPaintSlot *slot = get_active_slot(ob);
+ if (slot == nullptr) {
+ break;
+ }
+
+ if (ob->type != OB_MESH) {
+ return -1;
+ }
+
+ if (slot->uvname == nullptr) {
+ return -1;
+ }
+
+ const Mesh *mesh = static_cast<Mesh *>(ob->data);
+ return CustomData_get_named_layer_index(&mesh->ldata, CD_MLOOPUV, slot->uvname);
+ }
+ }
+ return -1;
+}
+
+char *BKE_paint_canvas_key_get(struct PaintModeSettings *settings, struct Object *ob)
+{
+ std::stringstream ss;
+ int active_uv_map_layer_index = BKE_paint_canvas_uvmap_layer_index_get(settings, ob);
+ ss << "UV_MAP:" << active_uv_map_layer_index;
+
+ Image *image;
+ ImageUser *image_user;
+ if (BKE_paint_canvas_image_get(settings, ob, &image, &image_user)) {
+ ImageUser tile_user = *image_user;
+ LISTBASE_FOREACH (ImageTile *, image_tile, &image->tiles) {
+ tile_user.tile = image_tile->tile_number;
+ ImBuf *image_buffer = BKE_image_acquire_ibuf(image, &tile_user, nullptr);
+ if (!image_buffer) {
+ continue;
+ }
+ ss << ",TILE_" << image_tile->tile_number;
+ ss << "(" << image_buffer->x << "," << image_buffer->y << ")";
+ BKE_image_release_ibuf(image, image_buffer, nullptr);
+ }
+ }
+
+ return BLI_strdup(ss.str().c_str());
+}
+}
diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c
index 9ea1336a95a..cc4738b1faa 100644
--- a/source/blender/blenkernel/intern/particle.c
+++ b/source/blender/blenkernel/intern/particle.c
@@ -1249,7 +1249,9 @@ typedef struct ParticleInterpolationData {
PTCacheEditPoint *epoint;
PTCacheEditKey *ekey[2];
- float birthtime, dietime;
+ float birthtime;
+ /** Die on this frame, see #ParticleData.dietime for details. */
+ float dietime;
int bspline;
} ParticleInterpolationData;
/**
@@ -1311,15 +1313,15 @@ static void get_pointcache_keys_for_time(Object *UNUSED(ob),
}
static int get_pointcache_times_for_particle(PointCache *cache,
int index,
- float *start,
- float *end)
+ float *r_start,
+ float *r_dietime)
{
PTCacheMem *pm;
int ret = 0;
for (pm = cache->mem_cache.first; pm; pm = pm->next) {
if (BKE_ptcache_mem_index_find(pm, index) >= 0) {
- *start = pm->frame;
+ *r_start = pm->frame;
ret++;
break;
}
@@ -1327,7 +1329,8 @@ static int get_pointcache_times_for_particle(PointCache *cache,
for (pm = cache->mem_cache.last; pm; pm = pm->prev) {
if (BKE_ptcache_mem_index_find(pm, index) >= 0) {
- *end = pm->frame;
+ /* Die *after* the last available frame. */
+ *r_dietime = pm->frame + 1;
ret++;
break;
}
@@ -1343,7 +1346,9 @@ float psys_get_dietime_from_cache(PointCache *cache, int index)
for (pm = cache->mem_cache.last; pm; pm = pm->prev) {
if (BKE_ptcache_mem_index_find(pm, index) >= 0) {
- return (float)pm->frame;
+ /* Die *after* the last available frame. */
+ dietime = pm->frame + 1;
+ break;
}
}
@@ -1374,14 +1379,14 @@ static void init_particle_interpolation(Object *ob,
pind->dietime = (key + pa->totkey - 1)->time;
}
else if (pind->cache) {
- float start = 0.0f, end = 0.0f;
+ float start = 0.0f, dietime = 0.0f;
get_pointcache_keys_for_time(ob, pind->cache, &pind->pm, -1, 0.0f, NULL, NULL);
pind->birthtime = pa ? pa->time : pind->cache->startframe;
- pind->dietime = pa ? pa->dietime : pind->cache->endframe;
+ pind->dietime = pa ? pa->dietime : (pind->cache->endframe + 1);
- if (get_pointcache_times_for_particle(pind->cache, pa - psys->particles, &start, &end)) {
+ if (get_pointcache_times_for_particle(pind->cache, pa - psys->particles, &start, &dietime)) {
pind->birthtime = MAX2(pind->birthtime, start);
- pind->dietime = MIN2(pind->dietime, end);
+ pind->dietime = MIN2(pind->dietime, dietime);
}
}
else {
diff --git a/source/blender/blenkernel/intern/particle_child.c b/source/blender/blenkernel/intern/particle_child.c
index 5dba4d3f003..524ee31229b 100644
--- a/source/blender/blenkernel/intern/particle_child.c
+++ b/source/blender/blenkernel/intern/particle_child.c
@@ -85,7 +85,7 @@ static void do_kink_spiral_deform(ParticleKey *state,
* and goes up to the Golden Spiral for 1.0
* https://en.wikipedia.org/wiki/Golden_spiral
*/
- const float b = shape * (1.0f + sqrtf(5.0f)) / (float)M_PI_4;
+ const float b = shape * (1.0f + sqrtf(5.0f)) / (float)M_PI * 0.25f;
/* angle of the spiral against the curve (rotated opposite to make a smooth transition) */
const float start_angle = ((b != 0.0f) ? atanf(1.0f / b) : (float)-M_PI_2) +
(b > 0.0f ? -(float)M_PI_2 : (float)M_PI_2);
diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c
index 5d307697208..e91f360ef22 100644
--- a/source/blender/blenkernel/intern/pbvh.c
+++ b/source/blender/blenkernel/intern/pbvh.c
@@ -686,6 +686,8 @@ void BKE_pbvh_free(PBVH *pbvh)
if (node->bm_other_verts) {
BLI_gset_free(node->bm_other_verts, NULL);
}
+
+ pbvh_pixels_free(node);
}
}
@@ -1769,7 +1771,7 @@ BMesh *BKE_pbvh_get_bmesh(PBVH *pbvh)
void BKE_pbvh_node_mark_update(PBVHNode *node)
{
node->flag |= PBVH_UpdateNormals | PBVH_UpdateBB | PBVH_UpdateOriginalBB |
- PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw;
+ PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw | PBVH_RebuildPixels;
}
void BKE_pbvh_node_mark_update_mask(PBVHNode *node)
@@ -1782,6 +1784,16 @@ void BKE_pbvh_node_mark_update_color(PBVHNode *node)
node->flag |= PBVH_UpdateColor | PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw;
}
+void BKE_pbvh_mark_rebuild_pixels(PBVH *pbvh)
+{
+ for (int n = 0; n < pbvh->totnode; n++) {
+ PBVHNode *node = &pbvh->nodes[n];
+ if (node->flag & PBVH_Leaf) {
+ node->flag |= PBVH_RebuildPixels;
+ }
+ }
+}
+
void BKE_pbvh_node_mark_update_visibility(PBVHNode *node)
{
node->flag |= PBVH_UpdateVisibility | PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers |
diff --git a/source/blender/blenkernel/intern/pbvh_intern.h b/source/blender/blenkernel/intern/pbvh_intern.h
index 37f8dfd9b6b..77bd00da50a 100644
--- a/source/blender/blenkernel/intern/pbvh_intern.h
+++ b/source/blender/blenkernel/intern/pbvh_intern.h
@@ -6,6 +6,10 @@
* \ingroup bke
*/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* Axis-aligned bounding box */
typedef struct {
float bmin[3], bmax[3];
@@ -32,13 +36,18 @@ struct PBVHNode {
* 'nodes' array. */
int children_offset;
- /* Pointer into the PBVH prim_indices array and the number of
- * primitives used by this leaf node.
+ /* List of primitives for this node. Semantics depends on
+ * PBVH type:
+ *
+ * - PBVH_FACES: Indices into the PBVH.looptri array.
+ * - PBVH_GRIDS: Multires grid indices.
+ * - PBVH_BMESH: Unused. See PBVHNode.bm_faces.
*
- * Used for leaf nodes in both mesh- and multires-based PBVHs.
+ * NOTE: This is a pointer inside of PBVH.prim_indices; it
+ * is not allocated separately per node.
*/
int *prim_indices;
- unsigned int totprim;
+ unsigned int totprim; /* Number of primitives inside prim_indices. */
/* Array of indices into the mesh's MVert array. Contains the
* indices of all vertices used by faces that are within this
@@ -63,9 +72,8 @@ struct PBVHNode {
unsigned int uniq_verts, face_verts;
/* Array of indices into the Mesh's MLoop array.
- * PBVH_FACES only. The first part of the array
- * are loops unique to this node, see comment for
- * vert_indices for more details.*/
+ * PBVH_FACES only.
+ */
int *loop_indices;
unsigned int loop_indices_num;
@@ -93,6 +101,11 @@ struct PBVHNode {
PBVHProxyNode *proxies;
/* Dyntopo */
+
+ /* GSet of pointers to the BMFaces used by this node.
+ * NOTE: PBVH_BMESH only. Faces are always triangles
+ * (dynamic topology forcibly triangulates the mesh).
+ */
GSet *bm_faces;
GSet *bm_unique_verts;
GSet *bm_other_verts;
@@ -102,6 +115,7 @@ struct PBVHNode {
/* Used to store the brush color during a stroke and composite it over the original color */
PBVHColorBufferNode color_buffer;
+ PBVHPixelsNode pixels;
};
typedef enum {
@@ -117,6 +131,7 @@ struct PBVH {
PBVHNode *nodes;
int node_mem_count, totnode;
+ /* Memory backing for PBVHNode.prim_indices. */
int *prim_indices;
int totprim;
int totvert;
@@ -250,3 +265,11 @@ bool pbvh_bmesh_node_nearest_to_ray(PBVHNode *node,
bool use_original);
void pbvh_bmesh_normals_update(PBVHNode **nodes, int totnode);
+
+/* pbvh_pixels.hh */
+void pbvh_pixels_free(PBVHNode *node);
+void pbvh_pixels_free_brush_test(PBVHNode *node);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/blenkernel/intern/pbvh_pixels.cc b/source/blender/blenkernel/intern/pbvh_pixels.cc
new file mode 100644
index 00000000000..d8dd2f4b382
--- /dev/null
+++ b/source/blender/blenkernel/intern/pbvh_pixels.cc
@@ -0,0 +1,393 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+#include "BKE_customdata.h"
+#include "BKE_mesh_mapping.h"
+#include "BKE_pbvh.h"
+#include "BKE_pbvh_pixels.hh"
+
+#include "DNA_image_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+
+#include "BLI_math.h"
+#include "BLI_task.h"
+
+#include "BKE_image_wrappers.hh"
+
+#include "bmesh.h"
+
+#include "pbvh_intern.h"
+
+namespace blender::bke::pbvh::pixels {
+
+/** Durind debugging this check could be enabled. It will write to each image pixel that is covered
+ * by the pbvh. */
+constexpr bool USE_WATERTIGHT_CHECK = false;
+
+/**
+ * Calculate the delta of two neighbour uv coordinates in the given image buffer.
+ */
+static float2 calc_barycentric_delta(const float2 uvs[3],
+ const float2 start_uv,
+ const float2 end_uv)
+{
+
+ float3 start_barycentric;
+ barycentric_weights_v2(uvs[0], uvs[1], uvs[2], start_uv, start_barycentric);
+ float3 end_barycentric;
+ barycentric_weights_v2(uvs[0], uvs[1], uvs[2], end_uv, end_barycentric);
+ float3 barycentric = end_barycentric - start_barycentric;
+ return float2(barycentric.x, barycentric.y);
+}
+
+static float2 calc_barycentric_delta_x(const ImBuf *image_buffer,
+ const float2 uvs[3],
+ const int x,
+ const int y)
+{
+ const float2 start_uv(float(x) / image_buffer->x, float(y) / image_buffer->y);
+ const float2 end_uv(float(x + 1) / image_buffer->x, float(y) / image_buffer->y);
+ return calc_barycentric_delta(uvs, start_uv, end_uv);
+}
+
+static void extract_barycentric_pixels(UDIMTilePixels &tile_data,
+ const ImBuf *image_buffer,
+ const int triangle_index,
+ const float2 uvs[3],
+ const int minx,
+ const int miny,
+ const int maxx,
+ const int maxy)
+{
+ for (int y = miny; y < maxy; y++) {
+ bool start_detected = false;
+ PackedPixelRow pixel_row;
+ pixel_row.triangle_index = triangle_index;
+ pixel_row.num_pixels = 0;
+ int x;
+
+ for (x = minx; x < maxx; x++) {
+ float2 uv(float(x) / image_buffer->x, float(y) / image_buffer->y);
+ float3 barycentric_weights;
+ barycentric_weights_v2(uvs[0], uvs[1], uvs[2], uv, barycentric_weights);
+
+ const bool is_inside = barycentric_inside_triangle_v2(barycentric_weights);
+ if (!start_detected && is_inside) {
+ start_detected = true;
+ pixel_row.start_image_coordinate = ushort2(x, y);
+ pixel_row.start_barycentric_coord = float2(barycentric_weights.x, barycentric_weights.y);
+ }
+ else if (start_detected && !is_inside) {
+ break;
+ }
+ }
+
+ if (!start_detected) {
+ continue;
+ }
+ pixel_row.num_pixels = x - pixel_row.start_image_coordinate.x;
+ tile_data.pixel_rows.append(pixel_row);
+ }
+}
+
+static void init_triangles(PBVH *pbvh, PBVHNode *node, NodeData *node_data, const MLoop *mloop)
+{
+ for (int i = 0; i < node->totprim; i++) {
+ const MLoopTri *lt = &pbvh->looptri[node->prim_indices[i]];
+ node_data->triangles.append(
+ int3(mloop[lt->tri[0]].v, mloop[lt->tri[1]].v, mloop[lt->tri[2]].v));
+ }
+}
+
+struct EncodePixelsUserData {
+ Image *image;
+ ImageUser *image_user;
+ PBVH *pbvh;
+ Vector<PBVHNode *> *nodes;
+ MLoopUV *ldata_uv;
+};
+
+static void do_encode_pixels(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ EncodePixelsUserData *data = static_cast<EncodePixelsUserData *>(userdata);
+ Image *image = data->image;
+ ImageUser image_user = *data->image_user;
+ PBVH *pbvh = data->pbvh;
+ PBVHNode *node = (*data->nodes)[n];
+ NodeData *node_data = static_cast<NodeData *>(node->pixels.node_data);
+ LISTBASE_FOREACH (ImageTile *, tile, &data->image->tiles) {
+ image::ImageTileWrapper image_tile(tile);
+ image_user.tile = image_tile.get_tile_number();
+ ImBuf *image_buffer = BKE_image_acquire_ibuf(image, &image_user, nullptr);
+ if (image_buffer == nullptr) {
+ continue;
+ }
+
+ float2 tile_offset = float2(image_tile.get_tile_offset());
+ UDIMTilePixels tile_data;
+
+ Triangles &triangles = node_data->triangles;
+ for (int triangle_index = 0; triangle_index < triangles.size(); triangle_index++) {
+ const MLoopTri *lt = &pbvh->looptri[node->prim_indices[triangle_index]];
+ float2 uvs[3] = {
+ float2(data->ldata_uv[lt->tri[0]].uv) - tile_offset,
+ float2(data->ldata_uv[lt->tri[1]].uv) - tile_offset,
+ float2(data->ldata_uv[lt->tri[2]].uv) - tile_offset,
+ };
+
+ const float minv = clamp_f(min_fff(uvs[0].y, uvs[1].y, uvs[2].y), 0.0f, 1.0f);
+ const int miny = floor(minv * image_buffer->y);
+ const float maxv = clamp_f(max_fff(uvs[0].y, uvs[1].y, uvs[2].y), 0.0f, 1.0f);
+ const int maxy = min_ii(ceil(maxv * image_buffer->y), image_buffer->y);
+ const float minu = clamp_f(min_fff(uvs[0].x, uvs[1].x, uvs[2].x), 0.0f, 1.0f);
+ const int minx = floor(minu * image_buffer->x);
+ const float maxu = clamp_f(max_fff(uvs[0].x, uvs[1].x, uvs[2].x), 0.0f, 1.0f);
+ const int maxx = min_ii(ceil(maxu * image_buffer->x), image_buffer->x);
+
+ TrianglePaintInput &triangle = triangles.get_paint_input(triangle_index);
+ triangle.delta_barycentric_coord_u = calc_barycentric_delta_x(image_buffer, uvs, minx, miny);
+ extract_barycentric_pixels(
+ tile_data, image_buffer, triangle_index, uvs, minx, miny, maxx, maxy);
+ }
+
+ BKE_image_release_ibuf(image, image_buffer, nullptr);
+
+ if (tile_data.pixel_rows.is_empty()) {
+ continue;
+ }
+
+ tile_data.tile_number = image_tile.get_tile_number();
+ node_data->tiles.append(tile_data);
+ }
+}
+
+static bool should_pixels_be_updated(PBVHNode *node)
+{
+ if ((node->flag & PBVH_Leaf) == 0) {
+ return false;
+ }
+ if ((node->flag & PBVH_RebuildPixels) != 0) {
+ return true;
+ }
+ NodeData *node_data = static_cast<NodeData *>(node->pixels.node_data);
+ if (node_data != nullptr) {
+ return false;
+ }
+ return true;
+}
+
+static int64_t count_nodes_to_update(PBVH *pbvh)
+{
+ int64_t result = 0;
+ for (int n = 0; n < pbvh->totnode; n++) {
+ PBVHNode *node = &pbvh->nodes[n];
+ if (should_pixels_be_updated(node)) {
+ result++;
+ }
+ }
+ return result;
+}
+
+/**
+ * Find the nodes that needs to be updated.
+ *
+ * The nodes that require updated are added to the r_nodes_to_update parameter.
+ * Will fill in r_visited_polygons with polygons that are owned by nodes that do not require
+ * updates.
+ *
+ * returns if there were any nodes found (true).
+ */
+static bool find_nodes_to_update(PBVH *pbvh, Vector<PBVHNode *> &r_nodes_to_update)
+{
+ int64_t nodes_to_update_len = count_nodes_to_update(pbvh);
+ if (nodes_to_update_len == 0) {
+ return false;
+ }
+
+ r_nodes_to_update.reserve(nodes_to_update_len);
+
+ for (int n = 0; n < pbvh->totnode; n++) {
+ PBVHNode *node = &pbvh->nodes[n];
+ if (!should_pixels_be_updated(node)) {
+ continue;
+ }
+ r_nodes_to_update.append(node);
+ node->flag = static_cast<PBVHNodeFlags>(node->flag | PBVH_RebuildPixels);
+
+ if (node->pixels.node_data == nullptr) {
+ NodeData *node_data = MEM_new<NodeData>(__func__);
+ node->pixels.node_data = node_data;
+ }
+ else {
+ NodeData *node_data = static_cast<NodeData *>(node->pixels.node_data);
+ node_data->clear_data();
+ }
+ }
+
+ return true;
+}
+
+static void apply_watertight_check(PBVH *pbvh, Image *image, ImageUser *image_user)
+{
+ ImageUser watertight = *image_user;
+ LISTBASE_FOREACH (ImageTile *, tile_data, &image->tiles) {
+ image::ImageTileWrapper image_tile(tile_data);
+ watertight.tile = image_tile.get_tile_number();
+ ImBuf *image_buffer = BKE_image_acquire_ibuf(image, &watertight, nullptr);
+ if (image_buffer == nullptr) {
+ continue;
+ }
+ for (int n = 0; n < pbvh->totnode; n++) {
+ PBVHNode *node = &pbvh->nodes[n];
+ if ((node->flag & PBVH_Leaf) == 0) {
+ continue;
+ }
+ NodeData *node_data = static_cast<NodeData *>(node->pixels.node_data);
+ UDIMTilePixels *tile_node_data = node_data->find_tile_data(image_tile);
+ if (tile_node_data == nullptr) {
+ continue;
+ }
+
+ for (PackedPixelRow &pixel_row : tile_node_data->pixel_rows) {
+ int pixel_offset = pixel_row.start_image_coordinate.y * image_buffer->x +
+ pixel_row.start_image_coordinate.x;
+ for (int x = 0; x < pixel_row.num_pixels; x++) {
+ if (image_buffer->rect_float) {
+ copy_v4_fl(&image_buffer->rect_float[pixel_offset * 4], 1.0);
+ }
+ if (image_buffer->rect) {
+ uint8_t *dest = static_cast<uint8_t *>(
+ static_cast<void *>(&image_buffer->rect[pixel_offset]));
+ copy_v4_uchar(dest, 255);
+ }
+ pixel_offset += 1;
+ }
+ }
+ }
+ BKE_image_release_ibuf(image, image_buffer, nullptr);
+ }
+ BKE_image_partial_update_mark_full_update(image);
+}
+
+static void update_pixels(PBVH *pbvh,
+ const struct MLoop *mloop,
+ struct CustomData *ldata,
+ struct Image *image,
+ struct ImageUser *image_user)
+{
+ Vector<PBVHNode *> nodes_to_update;
+
+ if (!find_nodes_to_update(pbvh, nodes_to_update)) {
+ return;
+ }
+
+ MLoopUV *ldata_uv = static_cast<MLoopUV *>(CustomData_get_layer(ldata, CD_MLOOPUV));
+ if (ldata_uv == nullptr) {
+ return;
+ }
+
+ for (PBVHNode *node : nodes_to_update) {
+ NodeData *node_data = static_cast<NodeData *>(node->pixels.node_data);
+ init_triangles(pbvh, node, node_data, mloop);
+ }
+
+ EncodePixelsUserData user_data;
+ user_data.pbvh = pbvh;
+ user_data.image = image;
+ user_data.image_user = image_user;
+ user_data.ldata_uv = ldata_uv;
+ user_data.nodes = &nodes_to_update;
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, nodes_to_update.size());
+ BLI_task_parallel_range(0, nodes_to_update.size(), &user_data, do_encode_pixels, &settings);
+ if (USE_WATERTIGHT_CHECK) {
+ apply_watertight_check(pbvh, image, image_user);
+ }
+
+ /* Clear the UpdatePixels flag. */
+ for (PBVHNode *node : nodes_to_update) {
+ node->flag = static_cast<PBVHNodeFlags>(node->flag & ~PBVH_RebuildPixels);
+ }
+
+//#define DO_PRINT_STATISTICS
+#ifdef DO_PRINT_STATISTICS
+ /* Print some statistics about compression ratio. */
+ {
+ int64_t compressed_data_len = 0;
+ int64_t num_pixels = 0;
+ for (int n = 0; n < pbvh->totnode; n++) {
+ PBVHNode *node = &pbvh->nodes[n];
+ if ((node->flag & PBVH_Leaf) == 0) {
+ continue;
+ }
+ NodeData *node_data = static_cast<NodeData *>(node->pixels.node_data);
+ compressed_data_len += node_data->triangles.mem_size();
+ for (const UDIMTilePixels &tile_data : node_data->tiles) {
+ compressed_data_len += tile_data.encoded_pixels.size() * sizeof(PackedPixelRow);
+ for (const PackedPixelRow &encoded_pixels : tile_data.encoded_pixels) {
+ num_pixels += encoded_pixels.num_pixels;
+ }
+ }
+ }
+ printf("Encoded %lld pixels in %lld bytes (%f bytes per pixel)\n",
+ num_pixels,
+ compressed_data_len,
+ float(compressed_data_len) / num_pixels);
+ }
+#endif
+}
+
+NodeData &BKE_pbvh_pixels_node_data_get(PBVHNode &node)
+{
+ BLI_assert(node.pixels.node_data != nullptr);
+ NodeData *node_data = static_cast<NodeData *>(node.pixels.node_data);
+ return *node_data;
+}
+
+void BKE_pbvh_pixels_mark_image_dirty(PBVHNode &node, Image &image, ImageUser &image_user)
+{
+ BLI_assert(node.pixels.node_data != nullptr);
+ NodeData *node_data = static_cast<NodeData *>(node.pixels.node_data);
+ if (node_data->flags.dirty) {
+ ImageUser local_image_user = image_user;
+ LISTBASE_FOREACH (ImageTile *, tile, &image.tiles) {
+ image::ImageTileWrapper image_tile(tile);
+ local_image_user.tile = image_tile.get_tile_number();
+ ImBuf *image_buffer = BKE_image_acquire_ibuf(&image, &local_image_user, nullptr);
+ if (image_buffer == nullptr) {
+ continue;
+ }
+
+ node_data->mark_region(image, image_tile, *image_buffer);
+ BKE_image_release_ibuf(&image, image_buffer, nullptr);
+ }
+ node_data->flags.dirty = false;
+ }
+}
+
+} // namespace blender::bke::pbvh::pixels
+
+extern "C" {
+using namespace blender::bke::pbvh::pixels;
+
+void BKE_pbvh_build_pixels(PBVH *pbvh,
+ const struct MLoop *mloop,
+ struct CustomData *ldata,
+ struct Image *image,
+ struct ImageUser *image_user)
+{
+ update_pixels(pbvh, mloop, ldata, image, image_user);
+}
+
+void pbvh_pixels_free(PBVHNode *node)
+{
+ NodeData *node_data = static_cast<NodeData *>(node->pixels.node_data);
+ MEM_delete(node_data);
+ node->pixels.node_data = nullptr;
+}
+}
diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c
index 19abff19b77..d5d304343df 100644
--- a/source/blender/blenkernel/intern/pointcache.c
+++ b/source/blender/blenkernel/intern/pointcache.c
@@ -285,8 +285,10 @@ static int ptcache_particle_write(int index, void *psys_v, void **data, int cfra
}
}
else {
- /* Particles are only stored in their lifetime. */
- if (cfra < pa->time - step || cfra > pa->dietime + step) {
+ /* Inclusive ranges for particle lifetime (`dietime - 1` for an inclusive end-frame). */
+ const int pa_sfra = (int)pa->time - step;
+ const int pa_efra = ((int)pa->dietime - 1) + step;
+ if (!(cfra >= pa_sfra && cfra <= pa_efra)) {
return 0;
}
}
@@ -399,9 +401,12 @@ static void ptcache_particle_interpolate(int index,
pa = psys->particles + index;
- /* particle wasn't read from first cache so can't interpolate */
- if ((int)cfra1 < pa->time - psys->pointcache->step ||
- (int)cfra1 > pa->dietime + psys->pointcache->step) {
+ /* Inclusive ranges for particle lifetime (`dietime - 1` for an inclusive end-frame). */
+ const int pa_sfra = (int)pa->time - psys->pointcache->step;
+ const int pa_efra = ((int)pa->dietime - 1) + psys->pointcache->step;
+
+ /* Particle wasn't read from first cache so can't interpolate. */
+ if (!(cfra1 >= pa_sfra && cfra1 <= pa_efra)) {
return;
}
@@ -482,12 +487,16 @@ static int ptcache_particle_totwrite(void *psys_v, int cfra)
if (psys->part->flag & PART_DIED) {
/* Also store dead particles when they are displayed. */
for (p = 0; p < psys->totpart; p++, pa++) {
- totwrite += (cfra >= pa->time - step);
+ const int pa_sfra = (int)pa->time - step;
+ totwrite += (cfra >= pa_sfra);
}
}
else {
for (p = 0; p < psys->totpart; p++, pa++) {
- totwrite += (cfra >= pa->time - step && cfra <= pa->dietime + step);
+ /* Inclusive ranges for particle lifetime (`dietime - 1` for an inclusive end-frame). */
+ const int pa_sfra = (int)pa->time - step;
+ const int pa_efra = ((int)pa->dietime - 1) + step;
+ totwrite += (cfra >= pa_sfra) && (cfra <= pa_efra);
}
}
diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c
index 91deeda8381..5e7a0fc116b 100644
--- a/source/blender/blenkernel/intern/subsurf_ccg.c
+++ b/source/blender/blenkernel/intern/subsurf_ccg.c
@@ -1963,8 +1963,6 @@ static CCGDerivedMesh *getCCGDerivedMesh(CCGSubSurf *ss,
ccgSubSurf_getNumFinalFaces(ss) * 4,
ccgSubSurf_getNumFinalFaces(ss));
- CustomData_free_layer_active(&ccgdm->dm.polyData, CD_NORMAL, ccgdm->dm.numPolyData);
-
ccgdm->reverseFaceMap = MEM_callocN(sizeof(int) * ccgSubSurf_getNumFinalFaces(ss),
"reverseFaceMap");
diff --git a/source/blender/blenkernel/intern/vfont.c b/source/blender/blenkernel/intern/vfont.c
index 5f751da1ee1..9a6f861eae8 100644
--- a/source/blender/blenkernel/intern/vfont.c
+++ b/source/blender/blenkernel/intern/vfont.c
@@ -1372,7 +1372,7 @@ static bool vfont_to_curve(Object *ob,
ct = chartransdata;
for (i = 0; i <= slen; i++, ct++) {
- float ctime, dtime, vec[4], tvec[4], rotvec[3];
+ float ctime, dtime, vec[4], rotvec[3];
float si, co;
/* Rotate around center character. */
@@ -1392,9 +1392,9 @@ static bool vfont_to_curve(Object *ob,
CLAMP(ctime, 0.0f, 1.0f);
/* Calculate the right loc AND the right rot separately. */
- /* `vec`, `tvec` need 4 items. */
- BKE_where_on_path(cu->textoncurve, ctime, vec, tvec, NULL, NULL, NULL);
- BKE_where_on_path(cu->textoncurve, ctime + dtime, tvec, rotvec, NULL, NULL, NULL);
+ /* `vec` needs 4 items. */
+ BKE_where_on_path(cu->textoncurve, ctime, vec, NULL, NULL, NULL, NULL);
+ BKE_where_on_path(cu->textoncurve, ctime + dtime, NULL, rotvec, NULL, NULL, NULL);
mul_v3_fl(vec, sizefac);
diff --git a/source/blender/blenkernel/nla_private.h b/source/blender/blenkernel/nla_private.h
index 47cef2a53ec..41d1eef733c 100644
--- a/source/blender/blenkernel/nla_private.h
+++ b/source/blender/blenkernel/nla_private.h
@@ -142,7 +142,11 @@ typedef struct NlaKeyframingContext {
/* Data of the currently edited strip (copy, or fake strip for the main action). */
NlaStrip strip;
NlaEvalStrip *eval_strip;
+ /* Storage for the action track as a strip. */
+ NlaStrip action_track_strip;
+ /* Strips above tweaked strip. */
+ ListBase upper_estrips;
/* Evaluated NLA stack below the tweak strip. */
NlaEvalData lower_eval_data;
} NlaKeyframingContext;
@@ -173,7 +177,22 @@ NlaEvalStrip *nlastrips_ctime_get_strip(ListBase *list,
/**
* Evaluates the given evaluation strip.
*/
-void nlastrip_evaluate(PointerRNA *ptr,
+
+enum eNlaStripEvaluate_Mode {
+ /* Blend upper strip with lower stack. */
+ STRIP_EVAL_BLEND,
+ /* Given upper strip and blended snapshot, solve for lower stack. */
+ STRIP_EVAL_BLEND_GET_INVERTED_LOWER_SNAPSHOT,
+ /* Store strip fcurve values in snapshot, properly marking blend_domain values.
+ *
+ * Currently only used for transitions to distinguish fcurve sampled values from default or lower
+ * stack values.
+ */
+ STRIP_EVAL_NOBLEND,
+};
+
+void nlastrip_evaluate(const int evaluation_mode,
+ PointerRNA *ptr,
NlaEvalData *channels,
ListBase *modifiers,
NlaEvalStrip *nes,
@@ -222,6 +241,36 @@ void nlasnapshot_blend_get_inverted_upper_snapshot(NlaEvalData *eval_data,
float upper_influence,
NlaEvalSnapshot *r_upper_snapshot);
+void nlasnapshot_blend_get_inverted_lower_snapshot(NlaEvalData *eval_data,
+ NlaEvalSnapshot *blended_snapshot,
+ NlaEvalSnapshot *upper_snapshot,
+ const short upper_blendmode,
+ const float upper_influence,
+ NlaEvalSnapshot *r_lower_snapshot);
+
+void nlasnapshot_blend_strip(PointerRNA *ptr,
+ NlaEvalData *channels,
+ ListBase *modifiers,
+ NlaEvalStrip *nes,
+ NlaEvalSnapshot *snapshot,
+ const struct AnimationEvalContext *anim_eval_context,
+ const bool flush_to_original);
+
+void nlasnapshot_blend_strip_get_inverted_lower_snapshot(
+ PointerRNA *ptr,
+ NlaEvalData *channels,
+ ListBase *modifiers,
+ NlaEvalStrip *nes,
+ NlaEvalSnapshot *snapshot,
+ const struct AnimationEvalContext *anim_eval_context);
+
+void nlasnapshot_blend_strip_no_blend(PointerRNA *ptr,
+ NlaEvalData *channels,
+ ListBase *modifiers,
+ NlaEvalStrip *nes,
+ NlaEvalSnapshot *snapshot,
+ const struct AnimationEvalContext *anim_eval_context);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenlib/BLI_math_rotation.hh b/source/blender/blenlib/BLI_math_rotation.hh
new file mode 100644
index 00000000000..e8b746b34df
--- /dev/null
+++ b/source/blender/blenlib/BLI_math_rotation.hh
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+/** \file
+ * \ingroup bli
+ */
+
+#include "BLI_math_vec_types.hh"
+
+namespace blender::math {
+
+/**
+ * Rotate the unit-length \a direction around the unit-length \a axis by the \a angle.
+ */
+float3 rotate_direction_around_axis(const float3 &direction, const float3 &axis, float angle);
+
+} // namespace blender::math
diff --git a/source/blender/blenlib/BLI_vector.hh b/source/blender/blenlib/BLI_vector.hh
index da9ab9c313e..acf47f67168 100644
--- a/source/blender/blenlib/BLI_vector.hh
+++ b/source/blender/blenlib/BLI_vector.hh
@@ -387,6 +387,16 @@ class Vector {
}
/**
+ * Reset the size of the vector so that it contains new_size elements.
+ * All existing elements are destructed, and not copied if the data must be reallocated.
+ */
+ void reinitialize(const int64_t new_size)
+ {
+ this->clear();
+ this->resize(new_size);
+ }
+
+ /**
* Afterwards the vector has 0 elements, but will still have
* memory to be refilled again.
*/
diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt
index 99e07264276..e8a3851e082 100644
--- a/source/blender/blenlib/CMakeLists.txt
+++ b/source/blender/blenlib/CMakeLists.txt
@@ -94,6 +94,7 @@ set(SRC
intern/math_interp.c
intern/math_matrix.c
intern/math_rotation.c
+ intern/math_rotation.cc
intern/math_solvers.c
intern/math_statistics.c
intern/math_time.c
@@ -251,6 +252,7 @@ set(SRC
BLI_math_matrix.h
BLI_math_mpq.hh
BLI_math_rotation.h
+ BLI_math_rotation.hh
BLI_math_solvers.h
BLI_math_statistics.h
BLI_math_time.h
diff --git a/source/blender/blenlib/intern/math_base_inline.c b/source/blender/blenlib/intern/math_base_inline.c
index bb6bc0db00d..a983821f15e 100644
--- a/source/blender/blenlib/intern/math_base_inline.c
+++ b/source/blender/blenlib/intern/math_base_inline.c
@@ -524,6 +524,15 @@ MINLINE uint max_uu(uint a, uint b)
return (b < a) ? a : b;
}
+MINLINE unsigned long long min_ulul(unsigned long long a, unsigned long long b)
+{
+ return (a < b) ? a : b;
+}
+MINLINE unsigned long long max_ulul(unsigned long long a, unsigned long long b)
+{
+ return (b < a) ? a : b;
+}
+
MINLINE float min_fff(float a, float b, float c)
{
return min_ff(min_ff(a, b), c);
diff --git a/source/blender/blenlib/intern/math_rotation.cc b/source/blender/blenlib/intern/math_rotation.cc
new file mode 100644
index 00000000000..74300d55954
--- /dev/null
+++ b/source/blender/blenlib/intern/math_rotation.cc
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup bli
+ */
+
+#include "BLI_math_base.h"
+#include "BLI_math_rotation.hh"
+#include "BLI_math_vector.h"
+#include "BLI_math_vector.hh"
+
+namespace blender::math {
+
+float3 rotate_direction_around_axis(const float3 &direction, const float3 &axis, const float angle)
+{
+ BLI_ASSERT_UNIT_V3(direction);
+ BLI_ASSERT_UNIT_V3(axis);
+
+ const float3 axis_scaled = axis * math::dot(direction, axis);
+ const float3 diff = direction - axis_scaled;
+ const float3 cross = math::cross(axis, diff);
+
+ return axis_scaled + diff * std::cos(angle) + cross * std::sin(angle);
+}
+
+} // namespace blender::math
diff --git a/source/blender/blenlib/tests/BLI_math_rotation_test.cc b/source/blender/blenlib/tests/BLI_math_rotation_test.cc
index a10e441cfbe..a283118bea2 100644
--- a/source/blender/blenlib/tests/BLI_math_rotation_test.cc
+++ b/source/blender/blenlib/tests/BLI_math_rotation_test.cc
@@ -4,6 +4,8 @@
#include "BLI_math_base.h"
#include "BLI_math_rotation.h"
+#include "BLI_math_rotation.hh"
+#include "BLI_math_vector.hh"
#include <cmath>
@@ -147,3 +149,23 @@ TEST(math_rotation, quat_split_swing_and_twist_negative)
EXPECT_V4_NEAR(swing, expected_swing, FLT_EPSILON);
EXPECT_V4_NEAR(twist, expected_twist, FLT_EPSILON);
}
+
+namespace blender::math::tests {
+
+TEST(math_rotation, RotateDirectionAroundAxis)
+{
+ const float3 a = rotate_direction_around_axis({1, 0, 0}, {0, 0, 1}, M_PI_2);
+ EXPECT_NEAR(a.x, 0.0f, FLT_EPSILON);
+ EXPECT_NEAR(a.y, 1.0f, FLT_EPSILON);
+ EXPECT_NEAR(a.z, 0.0f, FLT_EPSILON);
+ const float3 b = rotate_direction_around_axis({1, 0, 0}, {0, 0, 1}, M_PI);
+ EXPECT_NEAR(b.x, -1.0f, FLT_EPSILON);
+ EXPECT_NEAR(b.y, 0.0f, FLT_EPSILON);
+ EXPECT_NEAR(b.z, 0.0f, FLT_EPSILON);
+ const float3 c = rotate_direction_around_axis({0, 0, 1}, {0, 0, 1}, 0.0f);
+ EXPECT_NEAR(c.x, 0.0f, FLT_EPSILON);
+ EXPECT_NEAR(c.y, 0.0f, FLT_EPSILON);
+ EXPECT_NEAR(c.z, 1.0f, FLT_EPSILON);
+}
+
+} // namespace blender::math::tests
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index aba0bfe84d2..27890a908ab 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -2619,7 +2619,7 @@ static void lib_link_workspace_layout_restore(struct IDNameLib_Map *id_map,
scpt->script = restore_pointer_by_name(id_map, (ID *)scpt->script, USER_REAL);
- /*screen->script = NULL; - 2.45 set to null, better re-run the script */
+ // screen->script = NULL; /* 2.45 set to null, better re-run the script. */
if (scpt->script) {
SCRIPT_SET_NULL(scpt->script);
}
diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c
index 95851c0e9ff..ec60183218c 100644
--- a/source/blender/blenloader/intern/versioning_300.c
+++ b/source/blender/blenloader/intern/versioning_300.c
@@ -2677,5 +2677,17 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
*/
{
/* Keep this block, even when empty. */
+
+ /* Enable named attributes overlay in node editor. */
+ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ LISTBASE_FOREACH (SpaceLink *, space, &area->spacedata) {
+ if (space->spacetype == SPACE_NODE) {
+ SpaceNode *snode = (SpaceNode *)space;
+ snode->overlay.flag |= SN_OVERLAY_SHOW_NAMED_ATTRIBUTES;
+ }
+ }
+ }
+ }
}
}
diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c
index 000f132adcb..f65976ee55f 100644
--- a/source/blender/blenloader/intern/versioning_defaults.c
+++ b/source/blender/blenloader/intern/versioning_defaults.c
@@ -532,11 +532,13 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template)
for (Mesh *mesh = bmain->meshes.first; mesh; mesh = mesh->id.next) {
/* Match default for new meshes. */
mesh->smoothresh = DEG2RADF(30);
+ /* Match voxel remesher options for all existing meshes in templates. */
+ mesh->flag |= ME_REMESH_REPROJECT_VOLUME | ME_REMESH_REPROJECT_PAINT_MASK |
+ ME_REMESH_REPROJECT_SCULPT_FACE_SETS | ME_REMESH_REPROJECT_VERTEX_COLORS;
/* For Sculpting template. */
if (app_template && STREQ(app_template, "Sculpting")) {
mesh->remesh_voxel_size = 0.035f;
- mesh->flag |= ME_REMESH_FIX_POLES | ME_REMESH_REPROJECT_VOLUME;
BKE_mesh_smooth_flag_set(mesh, false);
}
else {
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index 490328106ca..2ae660ab1b6 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -611,11 +611,14 @@ static void mywrite_id_begin(WriteData *wd, ID *id)
if (wd->use_memfile) {
wd->mem.current_id_session_uuid = id->session_uuid;
- /* If current next memchunk does not match the ID we are about to write, try to find the
- * correct memchunk in the mapping using ID's session_uuid. */
+ /* If current next memchunk does not match the ID we are about to write, or is not the _first_
+ * one for said ID, try to find the correct memchunk in the mapping using ID's session_uuid. */
+ MemFileChunk *curr_memchunk = wd->mem.reference_current_chunk;
+ MemFileChunk *prev_memchunk = curr_memchunk != NULL ? curr_memchunk->prev : NULL;
if (wd->mem.id_session_uuid_mapping != NULL &&
- (wd->mem.reference_current_chunk == NULL ||
- wd->mem.reference_current_chunk->id_session_uuid != id->session_uuid)) {
+ (curr_memchunk == NULL || curr_memchunk->id_session_uuid != id->session_uuid ||
+ (prev_memchunk != NULL &&
+ (prev_memchunk->id_session_uuid == curr_memchunk->id_session_uuid)))) {
void *ref = BLI_ghash_lookup(wd->mem.id_session_uuid_mapping,
POINTER_FROM_UINT(id->session_uuid));
if (ref != NULL) {
diff --git a/source/blender/bmesh/operators/bmo_smooth_laplacian.c b/source/blender/bmesh/operators/bmo_smooth_laplacian.c
index 67ab57fa403..08efe5383a8 100644
--- a/source/blender/bmesh/operators/bmo_smooth_laplacian.c
+++ b/source/blender/bmesh/operators/bmo_smooth_laplacian.c
@@ -37,7 +37,7 @@ struct BLaplacianSystem {
BMOperator *op;
LinearSolver *context;
- /*Data*/
+ /* Data. */
float min_area;
};
typedef struct BLaplacianSystem LaplacianSystem;
diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc
index 788686f3036..1fdec43eb3b 100644
--- a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc
+++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc
@@ -104,7 +104,7 @@ void NodeOperationBuilder::convert_to_operations(ExecutionSystem *system)
prune_operations();
/* ensure topological (link-based) order of nodes */
- /*sort_operations();*/ /* not needed yet */
+ // sort_operations(); /* not needed yet. */
if (context_->get_execution_model() == eExecutionModel::Tiled) {
/* create execution groups */
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
index b5945c06ad3..23147b63e27 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
@@ -699,7 +699,7 @@ void DepsgraphRelationBuilder::build_object(Object *object)
OperationKey ob_eval_key(&object->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_EVAL);
add_relation(init_transform_key, local_transform_key, "Transform Init");
/* Various flags, flushing from bases/collections. */
- build_object_from_layer_relations(object);
+ build_object_layer_component_relations(object);
/* Parenting. */
if (object->parent != nullptr) {
/* Make sure parent object's relations are built. */
@@ -787,7 +787,37 @@ void DepsgraphRelationBuilder::build_object(Object *object)
build_parameters(&object->id);
}
-void DepsgraphRelationBuilder::build_object_from_layer_relations(Object *object)
+/* NOTE: Implies that the object has base in the current view layer. */
+void DepsgraphRelationBuilder::build_object_from_view_layer_base(Object *object)
+{
+ /* It is possible to have situation when an object is pulled into the dependency graph in a
+ * few different ways:
+ *
+ * - Indirect driver dependency, which doesn't have a Base (or, Base is unknown).
+ * - Via a base from a view layer (view layer of the graph, or view layer of a set scene).
+ * - Possibly other ways, which are not important for decision making here.
+ *
+ * There needs to be a relation from view layer which has a base with the object so that the
+ * order of flags evaluation is correct (object-level base flags evaluation requires view layer
+ * to be evaluated first).
+ *
+ * This build call handles situation when object comes from a view layer, hence has a base, and
+ * needs a relation from the view layer. Do the relation prior to check of whether the object
+ * relations are built so that the relation is created from every view layer which has a base
+ * with this object. */
+
+ OperationKey view_layer_done_key(
+ &scene_->id, NodeType::LAYER_COLLECTIONS, OperationCode::VIEW_LAYER_EVAL);
+ OperationKey object_from_layer_entry_key(
+ &object->id, NodeType::OBJECT_FROM_LAYER, OperationCode::OBJECT_FROM_LAYER_ENTRY);
+
+ add_relation(view_layer_done_key, object_from_layer_entry_key, "View Layer flags to Object");
+
+ /* Regular object building. */
+ build_object(object);
+}
+
+void DepsgraphRelationBuilder::build_object_layer_component_relations(Object *object)
{
OperationKey object_from_layer_entry_key(
&object->id, NodeType::OBJECT_FROM_LAYER, OperationCode::OBJECT_FROM_LAYER_ENTRY);
@@ -810,10 +840,6 @@ void DepsgraphRelationBuilder::build_object_from_layer_relations(Object *object)
OperationKey synchronize_key(
&object->id, NodeType::SYNCHRONIZATION, OperationCode::SYNCHRONIZE_TO_ORIGINAL);
add_relation(object_from_layer_exit_key, synchronize_key, "Synchronize to Original");
-
- OperationKey view_layer_done_key(
- &scene_->id, NodeType::LAYER_COLLECTIONS, OperationCode::VIEW_LAYER_EVAL);
- add_relation(view_layer_done_key, object_from_layer_entry_key, "View Layer flags to Object");
}
void DepsgraphRelationBuilder::build_object_data(Object *object)
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h
index e1312c68cea..1ccecc9a3f2 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h
@@ -200,7 +200,8 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder {
Object *object,
Collection *collection);
virtual void build_object(Object *object);
- virtual void build_object_from_layer_relations(Object *object);
+ virtual void build_object_from_view_layer_base(Object *object);
+ virtual void build_object_layer_component_relations(Object *object);
virtual void build_object_data(Object *object);
virtual void build_object_data_camera(Object *object);
virtual void build_object_data_geometry(Object *object);
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc
index f9306620a9d..d723e5beb75 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc
@@ -81,7 +81,7 @@ void DepsgraphRelationBuilder::build_view_layer(Scene *scene,
* do nullptr-pointer check of the base, so it's fine to pass original one. */
LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
if (need_pull_base_into_graph(base)) {
- build_object(base->object);
+ build_object_from_view_layer_base(base->object);
}
}
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc
index 5597d496890..058f57e5a61 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc
@@ -163,7 +163,7 @@ const ID *nested_id_hack_get_discarded_pointers(NestedIDHackTempStorage *storage
switch (GS(id->name)) {
# define SPECIAL_CASE(id_type, dna_type, field, variable) \
case id_type: { \
- storage->variable = *(dna_type *)id; \
+ storage->variable = dna::shallow_copy(*(dna_type *)id); \
storage->variable.field = nullptr; \
return &storage->variable.id; \
}
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt
index aed527639c5..6d3c203b076 100644
--- a/source/blender/draw/CMakeLists.txt
+++ b/source/blender/draw/CMakeLists.txt
@@ -16,6 +16,7 @@ set(INC
../editors/space_view3d
../functions
../gpu
+ ../gpu/intern
../imbuf
../makesdna
../makesrna
@@ -123,6 +124,7 @@ set(SRC
engines/eevee/eevee_sampling.c
engines/eevee/eevee_screen_raytrace.c
engines/eevee/eevee_shaders.c
+ engines/eevee/eevee_shaders_extra.cc
engines/eevee/eevee_shadows.c
engines/eevee/eevee_shadows_cascade.c
engines/eevee/eevee_shadows_cube.c
@@ -266,9 +268,13 @@ set(GLSL_SRC
engines/eevee/shaders/closure_eval_lib.glsl
engines/eevee/shaders/closure_eval_diffuse_lib.glsl
engines/eevee/shaders/closure_eval_glossy_lib.glsl
+ engines/eevee/shaders/closure_eval_surface_lib.glsl
engines/eevee/shaders/closure_eval_refraction_lib.glsl
+ engines/eevee/shaders/closure_eval_volume_lib.glsl
engines/eevee/shaders/closure_eval_translucent_lib.glsl
engines/eevee/shaders/closure_type_lib.glsl
+ engines/eevee/shaders/eevee_empty.glsl
+ engines/eevee/shaders/eevee_empty_volume.glsl
engines/eevee/shaders/effect_bloom_frag.glsl
engines/eevee/shaders/effect_dof_bokeh_frag.glsl
engines/eevee/shaders/effect_dof_dilate_tiles_frag.glsl
@@ -302,7 +308,6 @@ set(GLSL_SRC
engines/eevee/shaders/object_motion_frag.glsl
engines/eevee/shaders/object_motion_vert.glsl
engines/eevee/shaders/prepass_frag.glsl
- engines/eevee/shaders/prepass_vert.glsl
engines/eevee/shaders/shadow_accum_frag.glsl
engines/eevee/shaders/shadow_frag.glsl
engines/eevee/shaders/shadow_vert.glsl
@@ -333,6 +338,7 @@ set(GLSL_SRC
engines/eevee/shaders/volumetric_resolve_frag.glsl
engines/eevee/shaders/volumetric_scatter_frag.glsl
engines/eevee/shaders/volumetric_integration_frag.glsl
+ engines/eevee/shaders/world_vert.glsl
engines/workbench/shaders/workbench_cavity_lib.glsl
engines/workbench/shaders/workbench_common_lib.glsl
@@ -364,6 +370,7 @@ set(GLSL_SRC
engines/workbench/workbench_shader_shared.h
+ intern/shaders/common_attribute_lib.glsl
intern/shaders/common_colormanagement_lib.glsl
intern/shaders/common_globals_lib.glsl
intern/shaders/common_gpencil_lib.glsl
diff --git a/source/blender/draw/engines/eevee/eevee_data.c b/source/blender/draw/engines/eevee/eevee_data.c
index ee70cebcfd2..0c56b67ca99 100644
--- a/source/blender/draw/engines/eevee/eevee_data.c
+++ b/source/blender/draw/engines/eevee/eevee_data.c
@@ -160,7 +160,7 @@ EEVEE_HairMotionData *EEVEE_motion_blur_hair_data_get(EEVEE_ObjectMotionData *mb
{
if (mb_data->hair_data == NULL) {
/* Ugly, we allocate for each modifiers and just fill based on modifier index in the list. */
- int psys_len = (ob->type != OB_CURVES) ? BLI_listbase_count(&ob->modifiers) : 1;
+ int psys_len = BLI_listbase_count(&ob->modifiers);
EEVEE_HairMotionData *hair_step = MEM_callocN(
sizeof(EEVEE_HairMotionData) + sizeof(hair_step->psys[0]) * psys_len, __func__);
hair_step->psys_len = psys_len;
@@ -170,6 +170,18 @@ EEVEE_HairMotionData *EEVEE_motion_blur_hair_data_get(EEVEE_ObjectMotionData *mb
return mb_data->hair_data;
}
+EEVEE_HairMotionData *EEVEE_motion_blur_curves_data_get(EEVEE_ObjectMotionData *mb_data)
+{
+ if (mb_data->hair_data == NULL) {
+ EEVEE_HairMotionData *hair_step = MEM_callocN(
+ sizeof(EEVEE_HairMotionData) + sizeof(hair_step->psys[0]), __func__);
+ hair_step->psys_len = 1;
+ hair_step->type = EEVEE_MOTION_DATA_HAIR;
+ mb_data->hair_data = hair_step;
+ }
+ return mb_data->hair_data;
+}
+
/* View Layer data. */
void EEVEE_view_layer_data_free(void *storage)
diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c
index 6cd1a31085f..81edee17c76 100644
--- a/source/blender/draw/engines/eevee/eevee_engine.c
+++ b/source/blender/draw/engines/eevee/eevee_engine.c
@@ -52,6 +52,8 @@ static void eevee_engine_init(void *ved)
stl->g_data->valid_taa_history = (txl->taa_history != NULL);
stl->g_data->queued_shaders_count = 0;
stl->g_data->render_timesteps = 1;
+ stl->g_data->disable_ligthprobes = v3d &&
+ (v3d->object_type_exclude_viewport & (1 << OB_LIGHTPROBE));
/* Main Buffer */
DRW_texture_ensure_fullscreen_2d(&txl->color, GPU_RGBA16F, DRW_TEX_FILTER);
@@ -111,7 +113,7 @@ void EEVEE_cache_populate(void *vedata, Object *ob)
EEVEE_materials_cache_populate(vedata, sldata, ob, &cast_shadow);
}
else if (ob->type == OB_CURVES) {
- EEVEE_object_hair_cache_populate(vedata, sldata, ob, &cast_shadow);
+ EEVEE_object_curves_cache_populate(vedata, sldata, ob, &cast_shadow);
}
else if (ob->type == OB_VOLUME) {
EEVEE_volumes_cache_object_add(sldata, vedata, draw_ctx->scene, ob);
@@ -253,6 +255,10 @@ 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;
+ if (stl->g_data->disable_ligthprobes) {
+ sldata->common_data.prb_num_render_cube = 1;
+ sldata->common_data.prb_num_render_grid = 1;
+ }
GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data);
GPU_framebuffer_bind(fbl->main_fb);
diff --git a/source/blender/draw/engines/eevee/eevee_lightprobes.c b/source/blender/draw/engines/eevee/eevee_lightprobes.c
index 219c44de9dc..94915180483 100644
--- a/source/blender/draw/engines/eevee/eevee_lightprobes.c
+++ b/source/blender/draw/engines/eevee/eevee_lightprobes.c
@@ -1193,6 +1193,11 @@ void EEVEE_lightprobes_refresh_planar(EEVEE_ViewLayerData *sldata, EEVEE_Data *v
common_data->ssrefract_toggle = false;
common_data->sss_toggle = false;
+ if (vedata->stl->g_data->disable_ligthprobes) {
+ sldata->common_data.prb_num_render_cube = 1;
+ sldata->common_data.prb_num_render_grid = 1;
+ }
+
common_data->ray_type = EEVEE_RAY_GLOSSY;
common_data->ray_depth = 1.0f;
/* Planar reflections are rendered at the `hiz` resolution, so no need to scaling. */
diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c
index 4b9ad693971..c46e5dd75d6 100644
--- a/source/blender/draw/engines/eevee/eevee_materials.c
+++ b/source/blender/draw/engines/eevee/eevee_materials.c
@@ -73,7 +73,12 @@ void EEVEE_material_bind_resources(DRWShadingGroup *shgrp,
bool use_diffuse = GPU_material_flag_get(gpumat, GPU_MATFLAG_DIFFUSE);
bool use_glossy = GPU_material_flag_get(gpumat, GPU_MATFLAG_GLOSSY);
bool use_refract = GPU_material_flag_get(gpumat, GPU_MATFLAG_REFRACT);
+ bool use_ao = GPU_material_flag_get(gpumat, GPU_MATFLAG_AO);
+#ifdef __APPLE__
+ /* NOTE: Some implementation do not optimize out the unused samplers. */
+ use_diffuse = use_glossy = use_refract = use_ao = true;
+#endif
LightCache *lcache = vedata->stl->g_data->light_cache;
EEVEE_EffectsInfo *effects = vedata->stl->effects;
EEVEE_PrivateData *pd = vedata->stl->g_data;
@@ -91,6 +96,8 @@ void EEVEE_material_bind_resources(DRWShadingGroup *shgrp,
if (use_diffuse || use_glossy || use_refract) {
DRW_shgroup_uniform_texture_ref(shgrp, "shadowCubeTexture", &sldata->shadow_cube_pool);
DRW_shgroup_uniform_texture_ref(shgrp, "shadowCascadeTexture", &sldata->shadow_cascade_pool);
+ }
+ if (use_diffuse || use_glossy || use_refract || use_ao) {
DRW_shgroup_uniform_texture_ref(shgrp, "maxzBuffer", &vedata->txl->maxzbuffer);
}
if ((use_diffuse || use_glossy) && !use_ssrefraction) {
@@ -374,6 +381,13 @@ void EEVEE_materials_cache_init(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_ref(grp, "renderpass_block", &stl->g_data->renderpass_ubo);
+ DRW_shgroup_uniform_texture(grp, "utilTex", e_data.util_tex);
+ DRW_shgroup_uniform_texture_ref(grp, "shadowCubeTexture", &sldata->shadow_cube_pool);
+ DRW_shgroup_uniform_texture_ref(grp, "shadowCascadeTexture", &sldata->shadow_cascade_pool);
+ DRW_shgroup_uniform_texture_ref(grp, "probePlanars", &vedata->txl->planar_pool);
+ DRW_shgroup_uniform_texture_ref(grp, "probeCubes", &stl->g_data->light_cache->cube_tx.tex);
+ DRW_shgroup_uniform_texture_ref(grp, "irradianceGrid", &stl->g_data->light_cache->grid_tx.tex);
+ DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &vedata->txl->maxzbuffer);
DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL);
}
@@ -578,7 +592,7 @@ static EeveeMaterialCache material_opaque(EEVEE_Data *vedata,
SET_FLAG_FROM_TEST(mat_options, use_ssrefract, VAR_MAT_REFRACT);
SET_FLAG_FROM_TEST(mat_options, is_hair, VAR_MAT_HAIR);
GPUMaterial *gpumat = EEVEE_material_get(vedata, scene, ma, NULL, mat_options);
- const bool use_sss = GPU_material_flag_get(gpumat, GPU_MATFLAG_SSS);
+ const bool use_sss = GPU_material_flag_get(gpumat, GPU_MATFLAG_SUBSURFACE);
int ssr_id = (((effects->enabled_effects & EFFECT_SSR) != 0) && !use_ssrefract) ? 1 : 0;
int option = (use_ssrefract ? 0 : (use_sss ? 1 : 2)) * 2 + do_cull;
@@ -740,34 +754,6 @@ BLI_INLINE EeveeMaterialCache eevee_material_cache_get(
return matcache;
}
-static void eevee_hair_cache_populate(EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata,
- Object *ob,
- ParticleSystem *psys,
- ModifierData *md,
- int matnr,
- bool *cast_shadow)
-{
- EeveeMaterialCache matcache = eevee_material_cache_get(vedata, sldata, ob, matnr - 1, true);
-
- if (matcache.depth_grp) {
- *matcache.depth_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.depth_grp, NULL);
- DRW_shgroup_add_material_resources(*matcache.depth_grp_p, matcache.shading_gpumat);
- }
- if (matcache.shading_grp) {
- *matcache.shading_grp_p = DRW_shgroup_hair_create_sub(
- ob, psys, md, matcache.shading_grp, matcache.shading_gpumat);
- DRW_shgroup_add_material_resources(*matcache.shading_grp_p, matcache.shading_gpumat);
- }
- if (matcache.shadow_grp) {
- *matcache.shadow_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.shadow_grp, NULL);
- DRW_shgroup_add_material_resources(*matcache.shadow_grp_p, matcache.shading_gpumat);
- *cast_shadow = true;
- }
-
- EEVEE_motion_blur_hair_cache_populate(sldata, vedata, ob, psys, md);
-}
-
#define ADD_SHGROUP_CALL(shgrp, ob, geom, oedata) \
do { \
if (oedata) { \
@@ -899,18 +885,56 @@ void EEVEE_particle_hair_cache_populate(EEVEE_Data *vedata,
if (draw_as != PART_DRAW_PATH) {
continue;
}
- eevee_hair_cache_populate(vedata, sldata, ob, psys, md, part->omat, cast_shadow);
+ EeveeMaterialCache matcache = eevee_material_cache_get(
+ vedata, sldata, ob, part->omat - 1, true);
+
+ if (matcache.depth_grp) {
+ *matcache.depth_grp_p = DRW_shgroup_hair_create_sub(
+ ob, psys, md, matcache.depth_grp, NULL);
+ DRW_shgroup_add_material_resources(*matcache.depth_grp_p, matcache.shading_gpumat);
+ }
+ if (matcache.shading_grp) {
+ *matcache.shading_grp_p = DRW_shgroup_hair_create_sub(
+ ob, psys, md, matcache.shading_grp, matcache.shading_gpumat);
+ DRW_shgroup_add_material_resources(*matcache.shading_grp_p, matcache.shading_gpumat);
+ }
+ if (matcache.shadow_grp) {
+ *matcache.shadow_grp_p = DRW_shgroup_hair_create_sub(
+ ob, psys, md, matcache.shadow_grp, NULL);
+ DRW_shgroup_add_material_resources(*matcache.shadow_grp_p, matcache.shading_gpumat);
+ *cast_shadow = true;
+ }
+
+ EEVEE_motion_blur_hair_cache_populate(sldata, vedata, ob, psys, md);
}
}
}
}
-void EEVEE_object_hair_cache_populate(EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata,
- Object *ob,
- bool *cast_shadow)
+void EEVEE_object_curves_cache_populate(EEVEE_Data *vedata,
+ EEVEE_ViewLayerData *sldata,
+ Object *ob,
+ bool *cast_shadow)
{
- eevee_hair_cache_populate(vedata, sldata, ob, NULL, NULL, CURVES_MATERIAL_NR, cast_shadow);
+ EeveeMaterialCache matcache = eevee_material_cache_get(
+ vedata, sldata, ob, CURVES_MATERIAL_NR - 1, true);
+
+ if (matcache.depth_grp) {
+ *matcache.depth_grp_p = DRW_shgroup_curves_create_sub(ob, matcache.depth_grp, NULL);
+ DRW_shgroup_add_material_resources(*matcache.depth_grp_p, matcache.shading_gpumat);
+ }
+ if (matcache.shading_grp) {
+ *matcache.shading_grp_p = DRW_shgroup_curves_create_sub(
+ ob, matcache.shading_grp, matcache.shading_gpumat);
+ DRW_shgroup_add_material_resources(*matcache.shading_grp_p, matcache.shading_gpumat);
+ }
+ if (matcache.shadow_grp) {
+ *matcache.shadow_grp_p = DRW_shgroup_curves_create_sub(ob, matcache.shadow_grp, NULL);
+ DRW_shgroup_add_material_resources(*matcache.shadow_grp_p, matcache.shading_gpumat);
+ *cast_shadow = true;
+ }
+
+ EEVEE_motion_blur_curves_cache_populate(sldata, vedata, ob);
}
void EEVEE_materials_cache_finish(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata)
diff --git a/source/blender/draw/engines/eevee/eevee_motion_blur.c b/source/blender/draw/engines/eevee/eevee_motion_blur.c
index fbc19a01a8b..e3342508a14 100644
--- a/source/blender/draw/engines/eevee/eevee_motion_blur.c
+++ b/source/blender/draw/engines/eevee/eevee_motion_blur.c
@@ -270,6 +270,57 @@ void EEVEE_motion_blur_hair_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata),
}
}
+void EEVEE_motion_blur_curves_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata),
+ EEVEE_Data *vedata,
+ Object *ob)
+{
+ EEVEE_PassList *psl = vedata->psl;
+ EEVEE_StorageList *stl = vedata->stl;
+ EEVEE_EffectsInfo *effects = stl->effects;
+
+ if (!DRW_state_is_scene_render() || psl->velocity_hair == NULL) {
+ return;
+ }
+
+ /* For now we assume curves objects are always moving. */
+ EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get(&effects->motion_blur, ob);
+ if (mb_data == NULL) {
+ return;
+ }
+
+ int mb_step = effects->motion_blur_step;
+ /* Store transform. */
+ copy_m4_m4(mb_data->obmat[mb_step], ob->obmat);
+
+ EEVEE_HairMotionData *mb_curves = EEVEE_motion_blur_curves_data_get(mb_data);
+
+ if (mb_step == MB_CURR) {
+ /* Fill missing matrices if the object was hidden in previous or next frame. */
+ if (is_zero_m4(mb_data->obmat[MB_PREV])) {
+ copy_m4_m4(mb_data->obmat[MB_PREV], mb_data->obmat[MB_CURR]);
+ }
+ if (is_zero_m4(mb_data->obmat[MB_NEXT])) {
+ copy_m4_m4(mb_data->obmat[MB_NEXT], mb_data->obmat[MB_CURR]);
+ }
+
+ GPUTexture *tex_prev = mb_curves->psys[0].step_data[MB_PREV].hair_pos_tx;
+ GPUTexture *tex_next = mb_curves->psys[0].step_data[MB_NEXT].hair_pos_tx;
+
+ DRWShadingGroup *grp = DRW_shgroup_curves_create_sub(ob, effects->motion_blur.hair_grp, NULL);
+ DRW_shgroup_uniform_mat4(grp, "prevModelMatrix", mb_data->obmat[MB_PREV]);
+ DRW_shgroup_uniform_mat4(grp, "currModelMatrix", mb_data->obmat[MB_CURR]);
+ DRW_shgroup_uniform_mat4(grp, "nextModelMatrix", mb_data->obmat[MB_NEXT]);
+ DRW_shgroup_uniform_texture(grp, "prvBuffer", tex_prev);
+ DRW_shgroup_uniform_texture(grp, "nxtBuffer", tex_next);
+ DRW_shgroup_uniform_bool(grp, "useDeform", &mb_curves->use_deform, 1);
+ }
+ else {
+ /* Store vertex position buffer. */
+ mb_curves->psys[0].step_data[mb_step].hair_pos = DRW_curves_pos_buffer_get(ob);
+ mb_curves->use_deform = true;
+ }
+}
+
void EEVEE_motion_blur_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata),
EEVEE_Data *vedata,
Object *ob)
diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h
index 5220bac4e3f..9f97dacf9fe 100644
--- a/source/blender/draw/engines/eevee/eevee_private.h
+++ b/source/blender/draw/engines/eevee/eevee_private.h
@@ -462,7 +462,7 @@ typedef struct EEVEE_RenderPassData {
int renderPassSSSColor;
int renderPassEnvironment;
int renderPassAOV;
- int renderPassAOVActive;
+ uint renderPassAOVActive;
int _pad[3];
} EEVEE_RenderPassData;
@@ -666,7 +666,7 @@ typedef struct EEVEE_HairMotionData {
/** Needs to be first to ensure casting. */
eEEVEEMotionData type;
int use_deform;
- /** Allocator will alloc enough slot for all particle systems. Or 1 if it's a hair object. */
+ /** Allocator will alloc enough slot for all particle systems. Or 1 if it's a curves object. */
int psys_len;
struct {
/* The vbos and textures are not owned. */
@@ -1012,6 +1012,7 @@ typedef struct EEVEE_PrivateData {
struct DRWCallBuffer *planar_display_shgrp;
struct GHash *material_hash;
float background_alpha; /* TODO: find a better place for this. */
+ bool disable_ligthprobes;
/* Chosen lightcache: can come from Lookdev or the viewlayer. */
struct LightCache *light_cache;
/* For planar probes */
@@ -1050,7 +1051,7 @@ typedef struct EEVEE_PrivateData {
/* Renderpasses */
/* Bitmask containing the active render_passes */
eViewLayerEEVEEPassType render_passes;
- int aov_hash;
+ uint aov_hash;
int num_aovs_used;
struct CryptomatteSession *cryptomatte_session;
bool cryptomatte_accurate_mode;
@@ -1095,6 +1096,7 @@ EEVEE_ObjectEngineData *EEVEE_object_data_ensure(Object *ob);
EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *mb, Object *ob);
EEVEE_GeometryMotionData *EEVEE_motion_blur_geometry_data_get(EEVEE_ObjectMotionData *mb_data);
EEVEE_HairMotionData *EEVEE_motion_blur_hair_data_get(EEVEE_ObjectMotionData *mb_data, Object *ob);
+EEVEE_HairMotionData *EEVEE_motion_blur_curves_data_get(EEVEE_ObjectMotionData *mb_data);
EEVEE_LightProbeEngineData *EEVEE_lightprobe_data_get(Object *ob);
EEVEE_LightProbeEngineData *EEVEE_lightprobe_data_ensure(Object *ob);
EEVEE_LightEngineData *EEVEE_light_data_get(Object *ob);
@@ -1120,10 +1122,10 @@ void EEVEE_particle_hair_cache_populate(EEVEE_Data *vedata,
EEVEE_ViewLayerData *sldata,
Object *ob,
bool *cast_shadow);
-void EEVEE_object_hair_cache_populate(EEVEE_Data *vedata,
- EEVEE_ViewLayerData *sldata,
- Object *ob,
- bool *cast_shadow);
+void EEVEE_object_curves_cache_populate(EEVEE_Data *vedata,
+ EEVEE_ViewLayerData *sldata,
+ Object *ob,
+ bool *cast_shadow);
void EEVEE_materials_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata);
void EEVEE_materials_free(void);
void EEVEE_update_noise(EEVEE_PassList *psl, EEVEE_FramebufferList *fbl, const double offsets[3]);
@@ -1290,6 +1292,20 @@ struct GPUMaterial *EEVEE_material_get(
EEVEE_Data *vedata, struct Scene *scene, Material *ma, World *wo, int options);
void EEVEE_shaders_free(void);
+void eevee_shader_extra_init(void);
+void eevee_shader_extra_exit(void);
+void eevee_shader_material_create_info_amend(GPUMaterial *gpumat,
+ GPUCodegenOutput *codegen,
+ char *frag,
+ char *vert,
+ char *geom,
+ char *defines);
+GPUShader *eevee_shaders_sh_create_helper(const char *name,
+ const char *vert_name,
+ const char *frag_name,
+ const char *defines,
+ bool use_layered_rendering);
+
/* eevee_lightprobes.c */
bool EEVEE_lightprobes_obj_visibility_cb(bool vis_in, void *user_data);
@@ -1463,6 +1479,9 @@ void EEVEE_motion_blur_hair_cache_populate(EEVEE_ViewLayerData *sldata,
Object *ob,
struct ParticleSystem *psys,
struct ModifierData *md);
+void EEVEE_motion_blur_curves_cache_populate(EEVEE_ViewLayerData *sldata,
+ EEVEE_Data *vedata,
+ Object *ob);
void EEVEE_motion_blur_swap_data(EEVEE_Data *vedata);
void EEVEE_motion_blur_cache_finish(EEVEE_Data *vedata);
void EEVEE_motion_blur_draw(EEVEE_Data *vedata);
@@ -1504,7 +1523,7 @@ bool EEVEE_renderpasses_only_first_sample_pass_active(EEVEE_Data *vedata);
* Calculate the hash for an AOV. The least significant bit is used to store the AOV
* type the rest of the bits are used for the name hash.
*/
-int EEVEE_renderpasses_aov_hash(const ViewLayerAOV *aov);
+uint EEVEE_renderpasses_aov_hash(const ViewLayerAOV *aov);
/* eevee_temporal_sampling.c */
diff --git a/source/blender/draw/engines/eevee/eevee_render.c b/source/blender/draw/engines/eevee/eevee_render.c
index e971edbaa44..47e2b95f367 100644
--- a/source/blender/draw/engines/eevee/eevee_render.c
+++ b/source/blender/draw/engines/eevee/eevee_render.c
@@ -230,7 +230,7 @@ void EEVEE_render_cache(void *vedata,
}
}
else if (ob->type == OB_CURVES) {
- EEVEE_object_hair_cache_populate(vedata, sldata, ob, &cast_shadow);
+ EEVEE_object_curves_cache_populate(vedata, sldata, ob, &cast_shadow);
if (do_cryptomatte) {
EEVEE_cryptomatte_object_curves_cache_populate(data, sldata, ob);
}
diff --git a/source/blender/draw/engines/eevee/eevee_renderpasses.c b/source/blender/draw/engines/eevee/eevee_renderpasses.c
index 3814cf44fd6..237c830d547 100644
--- a/source/blender/draw/engines/eevee/eevee_renderpasses.c
+++ b/source/blender/draw/engines/eevee/eevee_renderpasses.c
@@ -60,9 +60,9 @@ bool EEVEE_renderpasses_only_first_sample_pass_active(EEVEE_Data *vedata)
return (g_data->render_passes & ~EEVEE_RENDERPASSES_POST_PROCESS_ON_FIRST_SAMPLE) == 0;
}
-int EEVEE_renderpasses_aov_hash(const ViewLayerAOV *aov)
+uint EEVEE_renderpasses_aov_hash(const ViewLayerAOV *aov)
{
- int hash = BLI_hash_string(aov->name) << 1;
+ uint hash = BLI_hash_string(aov->name) << 1u;
SET_FLAG_FROM_TEST(hash, aov->type == AOV_TYPE_COLOR, EEVEE_AOV_HASH_COLOR_TYPE_MASK);
return hash;
}
diff --git a/source/blender/draw/engines/eevee/eevee_shaders.c b/source/blender/draw/engines/eevee/eevee_shaders.c
index ae5e6e94f74..4e4a2a9eb8e 100644
--- a/source/blender/draw/engines/eevee/eevee_shaders.c
+++ b/source/blender/draw/engines/eevee/eevee_shaders.c
@@ -170,6 +170,7 @@ extern char datatoc_common_math_lib_glsl[];
extern char datatoc_common_math_geom_lib_glsl[];
extern char datatoc_common_view_lib_glsl[];
extern char datatoc_gpu_shader_common_obinfos_lib_glsl[];
+extern char datatoc_gpu_shader_codegen_lib_glsl[];
extern char datatoc_ambient_occlusion_lib_glsl[];
extern char datatoc_background_vert_glsl[];
@@ -178,6 +179,7 @@ extern char datatoc_bsdf_lut_frag_glsl[];
extern char datatoc_bsdf_sampling_lib_glsl[];
extern char datatoc_btdf_lut_frag_glsl[];
extern char datatoc_closure_type_lib_glsl[];
+extern char datatoc_closure_eval_volume_lib_glsl[];
extern char datatoc_common_uniforms_lib_glsl[];
extern char datatoc_common_utiltex_lib_glsl[];
extern char datatoc_cryptomatte_frag_glsl[];
@@ -230,6 +232,7 @@ extern char datatoc_lightprobe_planar_downsample_vert_glsl[];
extern char datatoc_lightprobe_vert_glsl[];
extern char datatoc_lights_lib_glsl[];
extern char datatoc_closure_eval_lib_glsl[];
+extern char datatoc_closure_eval_surface_lib_glsl[];
extern char datatoc_closure_eval_diffuse_lib_glsl[];
extern char datatoc_closure_eval_glossy_lib_glsl[];
extern char datatoc_closure_eval_refraction_lib_glsl[];
@@ -239,7 +242,6 @@ extern char datatoc_object_motion_frag_glsl[];
extern char datatoc_object_motion_vert_glsl[];
extern char datatoc_octahedron_lib_glsl[];
extern char datatoc_prepass_frag_glsl[];
-extern char datatoc_prepass_vert_glsl[];
extern char datatoc_random_lib_glsl[];
extern char datatoc_raytrace_lib_glsl[];
extern char datatoc_renderpass_lib_glsl[];
@@ -261,6 +263,7 @@ extern char datatoc_volumetric_lib_glsl[];
extern char datatoc_volumetric_resolve_frag_glsl[];
extern char datatoc_volumetric_scatter_frag_glsl[];
extern char datatoc_volumetric_vert_glsl[];
+extern char datatoc_world_vert_glsl[];
/* *********** FUNCTIONS *********** */
@@ -275,6 +278,7 @@ static void eevee_shader_library_ensure(void)
DRW_SHADER_LIB_ADD(e_data.lib, common_view_lib);
DRW_SHADER_LIB_ADD(e_data.lib, common_uniforms_lib);
DRW_SHADER_LIB_ADD(e_data.lib, gpu_shader_common_obinfos_lib);
+ DRW_SHADER_LIB_ADD(e_data.lib, gpu_shader_codegen_lib);
DRW_SHADER_LIB_ADD(e_data.lib, random_lib);
DRW_SHADER_LIB_ADD(e_data.lib, renderpass_lib);
DRW_SHADER_LIB_ADD(e_data.lib, bsdf_common_lib);
@@ -299,6 +303,8 @@ static void eevee_shader_library_ensure(void)
DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_glossy_lib);
DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_translucent_lib);
DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_refraction_lib);
+ DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_surface_lib);
+ DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_volume_lib);
e_data.surface_lit_frag = DRW_shader_library_create_shader_string(e_data.lib,
datatoc_surface_frag_glsl);
@@ -313,6 +319,7 @@ static void eevee_shader_library_ensure(void)
void EEVEE_shaders_material_shaders_init(void)
{
+ eevee_shader_extra_init();
eevee_shader_library_ensure();
}
@@ -828,6 +835,7 @@ struct GPUShader *EEVEE_shaders_volumes_clear_sh_get()
datatoc_volumetric_frag_glsl,
e_data.lib,
SHADER_DEFINES
+ "#define STANDALONE\n"
"#define VOLUMETRICS\n"
"#define CLEAR\n");
}
@@ -842,6 +850,7 @@ struct GPUShader *EEVEE_shaders_volumes_scatter_sh_get()
datatoc_volumetric_scatter_frag_glsl,
e_data.lib,
SHADER_DEFINES
+ "#define STANDALONE\n"
"#define VOLUMETRICS\n"
"#define VOLUME_SHADOW\n");
}
@@ -857,6 +866,7 @@ struct GPUShader *EEVEE_shaders_volumes_scatter_with_lights_sh_get()
datatoc_volumetric_scatter_frag_glsl,
e_data.lib,
SHADER_DEFINES
+ "#define STANDALONE\n"
"#define VOLUMETRICS\n"
"#define VOLUME_LIGHTING\n"
"#define VOLUME_SHADOW\n");
@@ -872,7 +882,9 @@ struct GPUShader *EEVEE_shaders_volumes_integration_sh_get()
datatoc_volumetric_geom_glsl,
datatoc_volumetric_integration_frag_glsl,
e_data.lib,
- USE_VOLUME_OPTI ? "#define USE_VOLUME_OPTI\n" SHADER_DEFINES : SHADER_DEFINES);
+ USE_VOLUME_OPTI ? "#define USE_VOLUME_OPTI\n"
+ "#define STANDALONE\n" SHADER_DEFINES :
+ "#define STANDALONE\n" SHADER_DEFINES);
}
return e_data.volumetric_integration_sh;
}
@@ -1232,7 +1244,7 @@ Material *EEVEE_material_default_glossy_get(void)
Material *EEVEE_material_default_error_get(void)
{
if (!e_data.error_mat) {
- Material *ma = BKE_id_new_nomain(ID_MA, "EEVEEE default metal");
+ Material *ma = BKE_id_new_nomain(ID_MA, "EEVEEE default error");
bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname);
ma->nodetree = ntree;
@@ -1375,7 +1387,7 @@ static char *eevee_get_vert(int options)
str = DRW_shader_library_create_shader_string(e_data.lib, datatoc_volumetric_vert_glsl);
}
else if ((options & (VAR_WORLD_PROBE | VAR_WORLD_BACKGROUND)) != 0) {
- str = DRW_shader_library_create_shader_string(e_data.lib, datatoc_background_vert_glsl);
+ str = DRW_shader_library_create_shader_string(e_data.lib, datatoc_world_vert_glsl);
}
else {
str = DRW_shader_library_create_shader_string(e_data.lib, datatoc_surface_vert_glsl);
@@ -1412,68 +1424,43 @@ static char *eevee_get_frag(int options)
return str;
}
-static void eevee_material_post_eval(GPUMaterial *mat,
- int options,
- const char **UNUSED(vert_code),
- const char **geom_code,
- const char **UNUSED(frag_lib),
- const char **UNUSED(defines))
+static void eevee_material_post_eval(void *UNUSED(thunk),
+ GPUMaterial *mat,
+ GPUCodegenOutput *codegen)
{
- const bool is_hair = (options & VAR_MAT_HAIR) != 0;
- const bool is_mesh = (options & VAR_MAT_MESH) != 0;
+ uint64_t options = GPU_material_uuid_get(mat);
- /* Force geometry usage if GPU_BARYCENTRIC_DIST or GPU_BARYCENTRIC_TEXCO are used.
- * NOTE: GPU_BARYCENTRIC_TEXCO only requires it if the shader is not drawing hairs. */
- if (!is_hair && is_mesh && GPU_material_flag_get(mat, GPU_MATFLAG_BARYCENTRIC) &&
- *geom_code == NULL) {
- *geom_code = e_data.surface_geom_barycentric;
- }
+ char *vert = eevee_get_vert(options);
+ char *geom = eevee_get_geom(options);
+ char *frag = eevee_get_frag(options);
+ char *defines = eevee_get_defines(options);
+
+ eevee_shader_material_create_info_amend(mat, codegen, frag, vert, geom, defines);
+
+ MEM_SAFE_FREE(defines);
+ MEM_SAFE_FREE(vert);
+ MEM_SAFE_FREE(geom);
+ MEM_SAFE_FREE(frag);
}
static struct GPUMaterial *eevee_material_get_ex(
- struct Scene *scene, Material *ma, World *wo, int options, bool deferred)
+ struct Scene *UNUSED(scene), Material *ma, World *wo, int options, bool deferred)
{
BLI_assert(ma || wo);
const bool is_volume = (options & VAR_MAT_VOLUME) != 0;
const bool is_default = (options & VAR_DEFAULT) != 0;
- const void *engine = &DRW_engine_viewport_eevee_type;
GPUMaterial *mat = NULL;
+ GPUCodegenCallbackFn cbfn = &eevee_material_post_eval;
if (ma) {
- mat = DRW_shader_find_from_material(ma, engine, options, deferred);
- }
- else {
- mat = DRW_shader_find_from_world(wo, engine, options, deferred);
- }
-
- if (mat) {
- return mat;
- }
-
- char *defines = eevee_get_defines(options);
- char *vert = eevee_get_vert(options);
- char *geom = eevee_get_geom(options);
- char *frag = eevee_get_frag(options);
-
- if (ma) {
- GPUMaterialEvalCallbackFn cbfn = &eevee_material_post_eval;
-
bNodeTree *ntree = !is_default ? ma->nodetree : EEVEE_shader_default_surface_nodetree(ma);
- mat = DRW_shader_create_from_material(
- scene, ma, ntree, engine, options, is_volume, vert, geom, frag, defines, deferred, cbfn);
+ mat = DRW_shader_from_material(ma, ntree, options, is_volume, deferred, cbfn, NULL);
}
else {
bNodeTree *ntree = !is_default ? wo->nodetree : EEVEE_shader_default_world_nodetree(wo);
- mat = DRW_shader_create_from_world(
- scene, wo, ntree, engine, options, is_volume, vert, geom, frag, defines, deferred, NULL);
+ mat = DRW_shader_from_world(wo, ntree, options, is_volume, deferred, cbfn, NULL);
}
-
- MEM_SAFE_FREE(defines);
- MEM_SAFE_FREE(vert);
- MEM_SAFE_FREE(geom);
- MEM_SAFE_FREE(frag);
-
return mat;
}
@@ -1520,6 +1507,7 @@ struct GPUMaterial *EEVEE_material_get(
void EEVEE_shaders_free(void)
{
+ eevee_shader_extra_exit();
MEM_SAFE_FREE(e_data.surface_prepass_frag);
MEM_SAFE_FREE(e_data.surface_lit_frag);
MEM_SAFE_FREE(e_data.surface_geom_barycentric);
diff --git a/source/blender/draw/engines/eevee/eevee_shaders_extra.cc b/source/blender/draw/engines/eevee/eevee_shaders_extra.cc
new file mode 100644
index 00000000000..bb1a0b0abe4
--- /dev/null
+++ b/source/blender/draw/engines/eevee/eevee_shaders_extra.cc
@@ -0,0 +1,173 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. */
+
+/** \file
+ * \ingroup EEVEE
+ *
+ * This file is only there to handle ShaderCreateInfos.
+ */
+
+#include "GPU_shader.h"
+
+#include "BLI_string_ref.hh"
+
+#include "gpu_shader_create_info.hh"
+
+#include "eevee_private.h"
+
+using blender::gpu::shader::StageInterfaceInfo;
+
+static StageInterfaceInfo *stage_interface = nullptr;
+
+void eevee_shader_extra_init()
+{
+ if (stage_interface != nullptr) {
+ return;
+ }
+
+ using namespace blender::gpu::shader;
+ stage_interface = new StageInterfaceInfo("ShaderStageInterface", "");
+ stage_interface->smooth(Type::VEC3, "worldPosition");
+ stage_interface->smooth(Type::VEC3, "viewPosition");
+ stage_interface->smooth(Type::VEC3, "worldNormal");
+ stage_interface->smooth(Type::VEC3, "viewNormal");
+ stage_interface->flat(Type::INT, "resourceIDFrag");
+}
+
+void eevee_shader_extra_exit()
+{
+ delete stage_interface;
+}
+
+void eevee_shader_material_create_info_amend(GPUMaterial *gpumat,
+ GPUCodegenOutput *codegen_,
+ char *frag,
+ char *vert,
+ char *geom,
+ char *defines)
+{
+ using namespace blender::gpu::shader;
+
+ uint64_t options = GPU_material_uuid_get(gpumat);
+ const bool is_background = (options & (VAR_WORLD_PROBE | VAR_WORLD_BACKGROUND)) != 0;
+ const bool is_volume = (options & (VAR_MAT_VOLUME)) != 0;
+ const bool is_hair = (options & (VAR_MAT_HAIR)) != 0;
+ const bool is_mesh = (options & (VAR_MAT_MESH)) != 0;
+ const bool is_point_cloud = (options & (VAR_MAT_POINTCLOUD)) != 0;
+
+ GPUCodegenOutput &codegen = *codegen_;
+ ShaderCreateInfo &info = *reinterpret_cast<ShaderCreateInfo *>(codegen.create_info);
+
+ info.legacy_resource_location(true);
+ info.auto_resource_location(true);
+
+ if (GPU_material_flag_get(gpumat, GPU_MATFLAG_SUBSURFACE)) {
+ info.define("USE_SSS");
+ }
+ if (GPU_material_flag_get(gpumat, GPU_MATFLAG_SHADER_TO_RGBA)) {
+ info.define("USE_SHADER_TO_RGBA");
+ }
+ if (GPU_material_flag_get(gpumat, GPU_MATFLAG_BARYCENTRIC) && !is_volume && !is_hair &&
+ !is_point_cloud && !is_background) {
+ info.define("USE_BARYCENTRICS");
+ info.builtins(BuiltinBits::BARYCENTRIC_COORD);
+ }
+ if (GPU_material_flag_get(gpumat, GPU_MATFLAG_BARYCENTRIC) && is_hair) {
+ info.define("USE_BARYCENTRICS");
+ }
+
+ std::stringstream attr_load;
+
+ const bool do_fragment_attrib_load = is_background || is_volume;
+
+ if (is_hair && !info.vertex_out_interfaces_.is_empty()) {
+ /** Hair attributes comme from sampler buffer. Transfer attributes to sampler. */
+ for (auto &input : info.vertex_inputs_) {
+ info.sampler(0, ImageType::FLOAT_BUFFER, input.name, Frequency::BATCH);
+ }
+ info.vertex_inputs_.clear();
+ }
+ else if (do_fragment_attrib_load && !info.vertex_out_interfaces_.is_empty()) {
+ /* Codegen outputs only one interface. */
+ const StageInterfaceInfo &iface = *info.vertex_out_interfaces_.first();
+ /* Globals the attrib_load() can write to when it is in the fragment shader. */
+ attr_load << "struct " << iface.name << " {\n";
+ for (const auto &inout : iface.inouts) {
+ attr_load << " " << inout.type << " " << inout.name << ";\n";
+ }
+ attr_load << "};\n";
+ attr_load << iface.name << " " << iface.instance_name << ";\n";
+ /* Global vars just to make code valid. Only Orco is supported. */
+ for (const ShaderCreateInfo::VertIn &in : info.vertex_inputs_) {
+ attr_load << in.type << " " << in.name << ";\n";
+ }
+ info.vertex_out_interfaces_.clear();
+ }
+
+ if (!is_volume) {
+ info.define("EEVEE_GENERATED_INTERFACE");
+ info.vertex_out(*stage_interface);
+ }
+
+ attr_load << "void attrib_load()\n";
+ attr_load << "{\n";
+ attr_load << ((codegen.attr_load) ? codegen.attr_load : "");
+ attr_load << "}\n\n";
+
+ std::stringstream vert_gen, frag_gen, geom_gen;
+
+ if (do_fragment_attrib_load) {
+ frag_gen << attr_load.str();
+ }
+ else {
+ vert_gen << attr_load.str();
+ }
+
+ {
+ vert_gen << vert;
+ info.vertex_source_generated = vert_gen.str();
+ /* Everything is in generated source. */
+ info.vertex_source(is_volume ? "eevee_empty_volume.glsl" : "eevee_empty.glsl");
+ }
+
+ {
+ frag_gen << frag;
+ if (codegen.material_functions) {
+ frag_gen << codegen.material_functions;
+ }
+ frag_gen << "Closure nodetree_exec()\n";
+ frag_gen << "{\n";
+ if (GPU_material_is_volume_shader(gpumat)) {
+ frag_gen << ((codegen.volume) ? codegen.volume : "return CLOSURE_DEFAULT;\n");
+ }
+ else {
+ frag_gen << ((codegen.surface) ? codegen.surface : "return CLOSURE_DEFAULT;\n");
+ }
+ frag_gen << "}\n\n";
+
+ if (codegen.displacement && (is_hair || is_mesh)) {
+ info.define("EEVEE_DISPLACEMENT_BUMP");
+
+ frag_gen << "vec3 displacement_exec()\n";
+ frag_gen << "{\n";
+ frag_gen << codegen.displacement;
+ frag_gen << "}\n\n";
+ }
+
+ info.fragment_source_generated = frag_gen.str();
+ /* Everything is in generated source. */
+ info.fragment_source(is_volume ? "eevee_empty_volume.glsl" : "eevee_empty.glsl");
+ }
+
+ if (geom) {
+ geom_gen << geom;
+ info.geometry_source_generated = geom_gen.str();
+ info.geometry_layout(PrimitiveIn::TRIANGLES, PrimitiveOut::TRIANGLE_STRIP, 3);
+ /* Everything is in generated source. */
+ info.geometry_source("eevee_empty.glsl");
+ }
+
+ if (defines) {
+ info.typedef_source_generated += blender::StringRefNull(defines);
+ }
+}
diff --git a/source/blender/draw/engines/eevee/eevee_volumes.c b/source/blender/draw/engines/eevee/eevee_volumes.c
index 7d210f15d8b..6a0c9fb36df 100644
--- a/source/blender/draw/engines/eevee/eevee_volumes.c
+++ b/source/blender/draw/engines/eevee/eevee_volumes.c
@@ -604,6 +604,7 @@ void EEVEE_volumes_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
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_uniform_block(grp, "shadow_block", sldata->shadow_ubo);
DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
}
diff --git a/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl b/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl
index 1c7ef775ac2..d8adf302e37 100644
--- a/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl
@@ -26,6 +26,10 @@
# endif
#endif
+#ifndef GPU_FRAGMENT_SHADER
+# define gl_FragCoord vec4(0.0)
+#endif
+
uniform sampler2D horizonBuffer;
/* aoSettings flags */
@@ -40,6 +44,15 @@ struct OcclusionData {
vec4 horizons;
/* Custom large scale occlusion. */
float custom_occlusion;
+
+#ifdef GPU_METAL
+ /* Constructors required for OcclusionData(..) syntax. */
+ inline OcclusionData() = default;
+ inline OcclusionData(vec4 in_horizons, float in_custom_occlusion)
+ : horizons(in_horizons), custom_occlusion(in_custom_occlusion)
+ {
+ }
+#endif
};
vec4 pack_occlusion_data(OcclusionData data)
@@ -415,3 +428,34 @@ OcclusionData occlusion_load(vec3 vP, float custom_occlusion)
return data;
}
+
+#ifndef GPU_FRAGMENT_SHADER
+# undef gl_FragCoord
+#endif
+
+float ambient_occlusion_eval(vec3 normal,
+ float max_distance,
+ const float inverted,
+ const float sample_count)
+{
+ /* Avoid multiline define causing compiler issues. */
+ /* clang-format off */
+#if defined(GPU_FRAGMENT_SHADER) && (defined(MESH_SHADER) || defined(HAIR_SHADER)) && !defined(DEPTH_SHADER) && !defined(VOLUMETRICS)
+ /* clang-format on */
+ vec3 bent_normal;
+ vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy);
+ OcclusionData data = occlusion_search(
+ viewPosition, maxzBuffer, max_distance, inverted, sample_count);
+
+ vec3 V = cameraVec(worldPosition);
+ vec3 N = normalize(normal);
+ vec3 Ng = safe_normalize(cross(dFdx(worldPosition), dFdy(worldPosition)));
+
+ float unused_error, visibility;
+ vec3 unused;
+ occlusion_eval(data, V, N, Ng, inverted, visibility, unused_error, unused);
+ return visibility;
+#else
+ return 1.0;
+#endif
+}
diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl
index 5bf20fe6979..574b24b3650 100644
--- a/source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/closure_eval_diffuse_lib.glsl
@@ -1,13 +1,23 @@
#pragma BLENDER_REQUIRE(lights_lib.glsl)
#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl)
+#pragma BLENDER_REQUIRE(closure_eval_lib.glsl)
+#pragma BLENDER_REQUIRE(renderpass_lib.glsl)
struct ClosureInputDiffuse {
vec3 N; /** Shading normal. */
vec3 albedo; /** Used for multibounce GTAO approximation. Not applied to final radiance. */
};
-#define CLOSURE_INPUT_Diffuse_DEFAULT ClosureInputDiffuse(vec3(0.0), vec3(0.0))
+#ifdef GPU_METAL
+/* C++ struct initialization. */
+# define CLOSURE_INPUT_Diffuse_DEFAULT \
+ { \
+ vec3(0.0), vec3(0.0) \
+ }
+#else
+# define CLOSURE_INPUT_Diffuse_DEFAULT ClosureInputDiffuse(vec3(0.0), vec3(0.0))
+#endif
struct ClosureEvalDiffuse {
vec3 probe_sampling_dir; /** Direction to sample probes from. */
@@ -80,6 +90,7 @@ void closure_Diffuse_eval_end(ClosureInputDiffuse cl_in,
ClosureEvalCommon cl_common,
inout ClosureOutputDiffuse cl_out)
{
+ cl_out.radiance = render_pass_diffuse_mask(cl_out.radiance);
#if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND)
/* This makes shader resources become unused and avoid issues with samplers. (see T59747) */
cl_out.radiance = vec3(0.0);
diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl
index ddc6a0b9661..0deaf4054d2 100644
--- a/source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/closure_eval_glossy_lib.glsl
@@ -4,13 +4,22 @@
#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl)
#pragma BLENDER_REQUIRE(bsdf_common_lib.glsl)
+#pragma BLENDER_REQUIRE(closure_eval_lib.glsl)
+#pragma BLENDER_REQUIRE(renderpass_lib.glsl)
struct ClosureInputGlossy {
vec3 N; /** Shading normal. */
float roughness; /** Input roughness, not squared. */
};
-#define CLOSURE_INPUT_Glossy_DEFAULT ClosureInputGlossy(vec3(0.0), 0.0)
+#ifdef GPU_METAL
+# define CLOSURE_INPUT_Glossy_DEFAULT \
+ { \
+ vec3(0.0), 0.0 \
+ }
+#else
+# define CLOSURE_INPUT_Glossy_DEFAULT ClosureInputGlossy(vec3(0.0), 0.0)
+#endif
struct ClosureEvalGlossy {
vec4 ltc_mat; /** LTC matrix values. */
@@ -136,6 +145,7 @@ void closure_Glossy_eval_end(ClosureInputGlossy cl_in,
ClosureEvalCommon cl_common,
inout ClosureOutputGlossy cl_out)
{
+ cl_out.radiance = render_pass_glossy_mask(cl_out.radiance);
#if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND)
/* This makes shader resources become unused and avoid issues with samplers. (see T59747) */
cl_out.radiance = vec3(0.0);
diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl
index 311887cf2f5..3f07f80571a 100644
--- a/source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/closure_eval_lib.glsl
@@ -1,8 +1,14 @@
#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
+// #pragma (gpu_shader_codegen_lib.glsl)
#pragma BLENDER_REQUIRE(lights_lib.glsl)
#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
+#ifndef GPU_FRAGMENT_SHADER
+# define gl_FragCoord vec4(0.0)
+# define gl_FrontFacing true
+#endif
+
/**
* Extensive use of Macros to be able to change the maximum amount of evaluated closure easily.
* NOTE: GLSL does not support variadic macros.
@@ -157,7 +163,15 @@
#define ClosureInputDummy ClosureOutput
#define ClosureOutputDummy ClosureOutput
#define ClosureEvalDummy ClosureOutput
-#define CLOSURE_EVAL_DUMMY ClosureOutput(vec3(0))
+#ifdef GPU_METAL
+/* C++ struct initialization. */
+# define CLOSURE_EVAL_DUMMY \
+ { \
+ vec3(0) \
+ }
+#else
+# define CLOSURE_EVAL_DUMMY ClosureOutput(vec3(0))
+#endif
#define CLOSURE_INPUT_Dummy_DEFAULT CLOSURE_EVAL_DUMMY
#define closure_Dummy_eval_init(cl_in, cl_common, cl_out) CLOSURE_EVAL_DUMMY
#define closure_Dummy_planar_eval(cl_in, cl_eval, cl_common, data, cl_out)
@@ -180,8 +194,15 @@ struct ClosureInputCommon {
/** Custom occlusion value set by the user. */
float occlusion;
};
-
-#define CLOSURE_INPUT_COMMON_DEFAULT ClosureInputCommon(1.0)
+#ifdef GPU_METAL
+/* C++ struct initialization. */
+# define CLOSURE_INPUT_COMMON_DEFAULT \
+ { \
+ 1.0 \
+ }
+#else
+# define CLOSURE_INPUT_COMMON_DEFAULT ClosureInputCommon(1.0)
+#endif
struct ClosureEvalCommon {
/** Result of SSAO. */
@@ -225,7 +246,11 @@ ClosureEvalCommon closure_Common_eval_init(ClosureInputCommon cl_in)
cl_eval.N = safe_normalize(gl_FrontFacing ? worldNormal : -worldNormal);
cl_eval.vN = safe_normalize(gl_FrontFacing ? viewNormal : -viewNormal);
cl_eval.vP = viewPosition;
+#ifdef GPU_FRAGMENT_SHADER
cl_eval.Ng = safe_normalize(cross(dFdx(cl_eval.P), dFdy(cl_eval.P)));
+#else
+ cl_eval.Ng = cl_eval.N;
+#endif
cl_eval.vNg = transform_direction(ViewMatrix, cl_eval.Ng);
cl_eval.occlusion_data = occlusion_load(cl_eval.vP, cl_in.occlusion);
@@ -322,3 +347,8 @@ ClosureGridData closure_grid_eval_init(int id, inout ClosureEvalCommon cl_common
}
/** \} */
+
+#ifndef GPU_FRAGMENT_SHADER
+# undef gl_FragCoord
+# undef gl_FrontFacing
+#endif
diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_refraction_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_refraction_lib.glsl
index 9011eea07c4..5c6769b185a 100644
--- a/source/blender/draw/engines/eevee/shaders/closure_eval_refraction_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/closure_eval_refraction_lib.glsl
@@ -4,14 +4,23 @@
#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl)
#pragma BLENDER_REQUIRE(ssr_lib.glsl)
+#pragma BLENDER_REQUIRE(closure_eval_lib.glsl)
+#pragma BLENDER_REQUIRE(renderpass_lib.glsl)
struct ClosureInputRefraction {
vec3 N; /** Shading normal. */
float roughness; /** Input roughness, not squared. */
float ior; /** Index of refraction ratio. */
};
-
-#define CLOSURE_INPUT_Refraction_DEFAULT ClosureInputRefraction(vec3(0.0), 0.0, 0.0)
+#ifdef GPU_METAL
+/* C++ struct initialization. */
+# define CLOSURE_INPUT_Refraction_DEFAULT \
+ { \
+ vec3(0.0), 0.0, 0.0 \
+ }
+#else
+# define CLOSURE_INPUT_Refraction_DEFAULT ClosureInputRefraction(vec3(0.0), 0.0, 0.0)
+#endif
struct ClosureEvalRefraction {
vec3 P; /** LTC matrix values. */
@@ -116,6 +125,7 @@ void closure_Refraction_eval_end(ClosureInputRefraction cl_in,
ClosureEvalCommon cl_common,
inout ClosureOutputRefraction cl_out)
{
+ cl_out.radiance = render_pass_glossy_mask(cl_out.radiance);
#if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND)
/* This makes shader resources become unused and avoid issues with samplers. (see T59747) */
cl_out.radiance = vec3(0.0);
diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_surface_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_surface_lib.glsl
new file mode 100644
index 00000000000..fa94b5ed272
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/closure_eval_surface_lib.glsl
@@ -0,0 +1,325 @@
+
+#pragma BLENDER_REQUIRE(closure_eval_diffuse_lib.glsl)
+#pragma BLENDER_REQUIRE(closure_eval_glossy_lib.glsl)
+#pragma BLENDER_REQUIRE(closure_eval_refraction_lib.glsl)
+#pragma BLENDER_REQUIRE(closure_eval_translucent_lib.glsl)
+#pragma BLENDER_REQUIRE(renderpass_lib.glsl)
+
+#ifdef USE_SHADER_TO_RGBA
+bool do_sss = false;
+bool do_ssr = false;
+#else
+bool do_sss = true;
+bool do_ssr = true;
+#endif
+
+vec3 out_sss_radiance;
+vec3 out_sss_color;
+float out_sss_radius;
+
+float out_ssr_roughness;
+vec3 out_ssr_color;
+vec3 out_ssr_N;
+
+bool aov_is_valid = false;
+vec3 out_aov;
+
+bool output_sss(ClosureDiffuse diffuse, ClosureOutputDiffuse diffuse_out)
+{
+ if (diffuse.sss_id == 0u || !do_sss || !sssToggle || outputSssId == 0) {
+ return false;
+ }
+ if (renderPassSSSColor) {
+ return false;
+ }
+ out_sss_radiance = diffuse_out.radiance;
+ out_sss_color = diffuse.color * diffuse.weight;
+ out_sss_radius = avg(diffuse.sss_radius);
+ do_sss = false;
+ return true;
+}
+
+bool output_ssr(ClosureReflection reflection)
+{
+ if (!do_ssr || !ssrToggle || outputSsrId == 0) {
+ return false;
+ }
+ out_ssr_roughness = reflection.roughness;
+ out_ssr_color = reflection.color * reflection.weight;
+ out_ssr_N = reflection.N;
+ do_ssr = false;
+ return true;
+}
+
+void output_aov(vec4 color, float value, uint hash)
+{
+ /* Keep in sync with `render_pass_aov_hash` and `EEVEE_renderpasses_aov_hash`. */
+ hash <<= 1u;
+
+ if (renderPassAOV && !aov_is_valid && hash == render_pass_aov_hash()) {
+ aov_is_valid = true;
+ if (render_pass_aov_is_color()) {
+ out_aov = color.rgb;
+ }
+ else {
+ out_aov = vec3(value);
+ }
+ }
+}
+
+/* Single BSDFs. */
+CLOSURE_EVAL_FUNCTION_DECLARE_1(DiffuseBSDF, Diffuse)
+Closure closure_eval(ClosureDiffuse diffuse)
+{
+ /* Glue with the old system. */
+ CLOSURE_VARS_DECLARE_1(Diffuse);
+
+ in_Diffuse_0.N = diffuse.N;
+ in_Diffuse_0.albedo = diffuse.color;
+
+ CLOSURE_EVAL_FUNCTION_1(DiffuseBSDF, Diffuse);
+
+ Closure closure = CLOSURE_DEFAULT;
+ if (!output_sss(diffuse, out_Diffuse_0)) {
+ closure.radiance += out_Diffuse_0.radiance * diffuse.color * diffuse.weight;
+ }
+ return closure;
+}
+
+CLOSURE_EVAL_FUNCTION_DECLARE_1(TranslucentBSDF, Translucent)
+Closure closure_eval(ClosureTranslucent translucent)
+{
+ /* Glue with the old system. */
+ CLOSURE_VARS_DECLARE_1(Translucent);
+
+ in_Translucent_0.N = translucent.N;
+
+ CLOSURE_EVAL_FUNCTION_1(TranslucentBSDF, Translucent);
+
+ Closure closure = CLOSURE_DEFAULT;
+ closure.radiance += out_Translucent_0.radiance * translucent.color * translucent.weight;
+ return closure;
+}
+
+CLOSURE_EVAL_FUNCTION_DECLARE_1(GlossyBSDF, Glossy)
+Closure closure_eval(ClosureReflection reflection)
+{
+ /* Glue with the old system. */
+ CLOSURE_VARS_DECLARE_1(Glossy);
+
+ in_Glossy_0.N = reflection.N;
+ in_Glossy_0.roughness = reflection.roughness;
+
+ CLOSURE_EVAL_FUNCTION_1(GlossyBSDF, Glossy);
+
+ Closure closure = CLOSURE_DEFAULT;
+ if (!output_ssr(reflection)) {
+ closure.radiance += out_Glossy_0.radiance * reflection.color * reflection.weight;
+ }
+ return closure;
+}
+
+CLOSURE_EVAL_FUNCTION_DECLARE_1(RefractionBSDF, Refraction)
+Closure closure_eval(ClosureRefraction refraction)
+{
+ /* Glue with the old system. */
+ CLOSURE_VARS_DECLARE_1(Refraction);
+
+ in_Refraction_0.N = refraction.N;
+ in_Refraction_0.roughness = refraction.roughness;
+ in_Refraction_0.ior = refraction.ior;
+
+ CLOSURE_EVAL_FUNCTION_1(RefractionBSDF, Refraction);
+
+ Closure closure = CLOSURE_DEFAULT;
+ closure.radiance += out_Refraction_0.radiance * refraction.color * refraction.weight;
+ return closure;
+}
+
+Closure closure_eval(ClosureEmission emission)
+{
+ Closure closure = CLOSURE_DEFAULT;
+ closure.radiance += render_pass_emission_mask(emission.emission) * emission.weight;
+ return closure;
+}
+
+Closure closure_eval(ClosureTransparency transparency)
+{
+ Closure closure = CLOSURE_DEFAULT;
+ closure.transmittance += transparency.transmittance * transparency.weight;
+ closure.holdout += transparency.holdout * transparency.weight;
+ return closure;
+}
+
+/* Glass BSDF. */
+CLOSURE_EVAL_FUNCTION_DECLARE_2(GlassBSDF, Glossy, Refraction)
+Closure closure_eval(ClosureReflection reflection, ClosureRefraction refraction)
+{
+ /* Glue with the old system. */
+ CLOSURE_VARS_DECLARE_2(Glossy, Refraction);
+
+ in_Glossy_0.N = reflection.N;
+ in_Glossy_0.roughness = reflection.roughness;
+ in_Refraction_1.N = refraction.N;
+ in_Refraction_1.roughness = refraction.roughness;
+ in_Refraction_1.ior = refraction.ior;
+
+ CLOSURE_EVAL_FUNCTION_2(GlassBSDF, Glossy, Refraction);
+
+ Closure closure = CLOSURE_DEFAULT;
+ closure.radiance += out_Refraction_1.radiance * refraction.color * refraction.weight;
+ if (!output_ssr(reflection)) {
+ closure.radiance += out_Glossy_0.radiance * reflection.color * reflection.weight;
+ }
+ return closure;
+}
+
+/* Dielectric BSDF */
+CLOSURE_EVAL_FUNCTION_DECLARE_2(DielectricBSDF, Diffuse, Glossy)
+Closure closure_eval(ClosureDiffuse diffuse, ClosureReflection reflection)
+{
+ /* Glue with the old system. */
+ CLOSURE_VARS_DECLARE_2(Diffuse, Glossy);
+
+ in_Diffuse_0.N = diffuse.N;
+ in_Diffuse_0.albedo = diffuse.color;
+ in_Glossy_1.N = reflection.N;
+ in_Glossy_1.roughness = reflection.roughness;
+
+ CLOSURE_EVAL_FUNCTION_2(DielectricBSDF, Diffuse, Glossy);
+
+ Closure closure = CLOSURE_DEFAULT;
+ if (!output_sss(diffuse, out_Diffuse_0)) {
+ closure.radiance += out_Diffuse_0.radiance * diffuse.color * diffuse.weight;
+ }
+ if (!output_ssr(reflection)) {
+ closure.radiance += out_Glossy_1.radiance * reflection.color * reflection.weight;
+ }
+ return closure;
+}
+
+/* Specular BSDF */
+CLOSURE_EVAL_FUNCTION_DECLARE_3(SpecularBSDF, Diffuse, Glossy, Glossy)
+Closure closure_eval(ClosureDiffuse diffuse,
+ ClosureReflection reflection,
+ ClosureReflection clearcoat)
+{
+ /* Glue with the old system. */
+ CLOSURE_VARS_DECLARE_3(Diffuse, Glossy, Glossy);
+
+ in_Diffuse_0.N = diffuse.N;
+ in_Diffuse_0.albedo = diffuse.color;
+ in_Glossy_1.N = reflection.N;
+ in_Glossy_1.roughness = reflection.roughness;
+ in_Glossy_2.N = clearcoat.N;
+ in_Glossy_2.roughness = clearcoat.roughness;
+
+ CLOSURE_EVAL_FUNCTION_3(SpecularBSDF, Diffuse, Glossy, Glossy);
+
+ Closure closure = CLOSURE_DEFAULT;
+ if (!output_sss(diffuse, out_Diffuse_0)) {
+ closure.radiance += out_Diffuse_0.radiance * diffuse.color * diffuse.weight;
+ }
+ closure.radiance += out_Glossy_2.radiance * clearcoat.color * clearcoat.weight;
+ if (!output_ssr(reflection)) {
+ closure.radiance += out_Glossy_1.radiance * reflection.color * reflection.weight;
+ }
+ return closure;
+}
+
+/* Principled BSDF */
+CLOSURE_EVAL_FUNCTION_DECLARE_4(PrincipledBSDF, Diffuse, Glossy, Glossy, Refraction)
+Closure closure_eval(ClosureDiffuse diffuse,
+ ClosureReflection reflection,
+ ClosureReflection clearcoat,
+ ClosureRefraction refraction)
+{
+ /* Glue with the old system. */
+ CLOSURE_VARS_DECLARE_4(Diffuse, Glossy, Glossy, Refraction);
+
+ in_Diffuse_0.N = diffuse.N;
+ in_Diffuse_0.albedo = diffuse.color;
+ in_Glossy_1.N = reflection.N;
+ in_Glossy_1.roughness = reflection.roughness;
+ in_Glossy_2.N = clearcoat.N;
+ in_Glossy_2.roughness = clearcoat.roughness;
+ in_Refraction_3.N = refraction.N;
+ in_Refraction_3.roughness = refraction.roughness;
+ in_Refraction_3.ior = refraction.ior;
+
+ CLOSURE_EVAL_FUNCTION_4(PrincipledBSDF, Diffuse, Glossy, Glossy, Refraction);
+
+ Closure closure = CLOSURE_DEFAULT;
+ closure.radiance += out_Glossy_2.radiance * clearcoat.color * clearcoat.weight;
+ closure.radiance += out_Refraction_3.radiance * refraction.color * refraction.weight;
+ if (!output_sss(diffuse, out_Diffuse_0)) {
+ closure.radiance += out_Diffuse_0.radiance * diffuse.color * diffuse.weight;
+ }
+ if (!output_ssr(reflection)) {
+ closure.radiance += out_Glossy_1.radiance * reflection.color * reflection.weight;
+ }
+ return closure;
+}
+
+CLOSURE_EVAL_FUNCTION_DECLARE_2(PrincipledBSDFMetalClearCoat, Glossy, Glossy)
+Closure closure_eval(ClosureReflection reflection, ClosureReflection clearcoat)
+{
+ /* Glue with the old system. */
+ CLOSURE_VARS_DECLARE_2(Glossy, Glossy);
+
+ in_Glossy_0.N = reflection.N;
+ in_Glossy_0.roughness = reflection.roughness;
+ in_Glossy_1.N = clearcoat.N;
+ in_Glossy_1.roughness = clearcoat.roughness;
+
+ CLOSURE_EVAL_FUNCTION_2(PrincipledBSDFMetalClearCoat, Glossy, Glossy);
+
+ Closure closure = CLOSURE_DEFAULT;
+ closure.radiance += out_Glossy_1.radiance * clearcoat.color * clearcoat.weight;
+ if (!output_ssr(reflection)) {
+ closure.radiance += out_Glossy_0.radiance * reflection.color * reflection.weight;
+ }
+ return closure;
+}
+
+/* Not supported for surface shaders. */
+Closure closure_eval(ClosureVolumeScatter volume_scatter)
+{
+ return CLOSURE_DEFAULT;
+}
+Closure closure_eval(ClosureVolumeAbsorption volume_absorption)
+{
+ return CLOSURE_DEFAULT;
+}
+Closure closure_eval(ClosureVolumeScatter volume_scatter,
+ ClosureVolumeAbsorption volume_absorption,
+ ClosureEmission emission)
+{
+ return CLOSURE_DEFAULT;
+}
+
+/* Not implemented yet. */
+Closure closure_eval(ClosureHair hair)
+{
+ return CLOSURE_DEFAULT;
+}
+
+vec4 closure_to_rgba(Closure closure)
+{
+ return vec4(closure.radiance, 1.0 - saturate(avg(closure.transmittance)));
+}
+
+Closure closure_add(Closure cl1, Closure cl2)
+{
+ Closure cl;
+ cl.radiance = cl1.radiance + cl2.radiance;
+ cl.transmittance = cl1.transmittance + cl2.transmittance;
+ cl.holdout = cl1.holdout + cl2.holdout;
+ return cl;
+}
+
+Closure closure_mix(Closure cl1, Closure cl2, float fac)
+{
+ /* Weights have already been applied. */
+ return closure_add(cl1, cl2);
+}
diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_translucent_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_translucent_lib.glsl
index 183219c9088..89a6f10e634 100644
--- a/source/blender/draw/engines/eevee/shaders/closure_eval_translucent_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/closure_eval_translucent_lib.glsl
@@ -3,13 +3,21 @@
#pragma BLENDER_REQUIRE(lights_lib.glsl)
#pragma BLENDER_REQUIRE(lightprobe_lib.glsl)
#pragma BLENDER_REQUIRE(ambient_occlusion_lib.glsl)
+#pragma BLENDER_REQUIRE(closure_eval_lib.glsl)
+#pragma BLENDER_REQUIRE(renderpass_lib.glsl)
struct ClosureInputTranslucent {
vec3 N; /** Shading normal. */
};
-
-#define CLOSURE_INPUT_Translucent_DEFAULT ClosureInputTranslucent(vec3(0.0))
-
+#ifdef GPU_METAL
+/* C++ struct initialization. */
+# define CLOSURE_INPUT_Translucent_DEFAULT \
+ { \
+ vec3(0.0) \
+ }
+#else
+# define CLOSURE_INPUT_Translucent_DEFAULT ClosureInputTranslucent(vec3(0.0))
+#endif
/* Stubs. */
#define ClosureEvalTranslucent ClosureEvalDummy
#define ClosureOutputTranslucent ClosureOutput
@@ -63,6 +71,7 @@ void closure_Translucent_eval_end(ClosureInputTranslucent cl_in,
ClosureEvalCommon cl_common,
inout ClosureOutputTranslucent cl_out)
{
+ cl_out.radiance = render_pass_diffuse_mask(cl_out.radiance);
#if defined(DEPTH_SHADER) || defined(WORLD_BACKGROUND)
/* This makes shader resources become unused and avoid issues with samplers. (see T59747) */
cl_out.radiance = vec3(0.0);
diff --git a/source/blender/draw/engines/eevee/shaders/closure_eval_volume_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_eval_volume_lib.glsl
new file mode 100644
index 00000000000..e450b8ad3c8
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/closure_eval_volume_lib.glsl
@@ -0,0 +1,113 @@
+
+void output_aov(vec4 color, float value, uint hash)
+{
+ /* Unsupported. */
+}
+
+/* Surface BSDFs. */
+Closure closure_eval(ClosureDiffuse diffuse)
+{
+ return CLOSURE_DEFAULT;
+}
+Closure closure_eval(ClosureTranslucent translucent)
+{
+ return CLOSURE_DEFAULT;
+}
+Closure closure_eval(ClosureReflection reflection)
+{
+ return CLOSURE_DEFAULT;
+}
+Closure closure_eval(ClosureRefraction refraction)
+{
+ return CLOSURE_DEFAULT;
+}
+Closure closure_eval(ClosureEmission emission)
+{
+ Closure closure = CLOSURE_DEFAULT;
+ closure.emission = emission.emission;
+ return closure;
+}
+Closure closure_eval(ClosureTransparency transparency)
+{
+ return CLOSURE_DEFAULT;
+}
+Closure closure_eval(ClosureReflection reflection, ClosureRefraction refraction)
+{
+ return CLOSURE_DEFAULT;
+}
+Closure closure_eval(ClosureDiffuse diffuse, ClosureReflection reflection)
+{
+ return CLOSURE_DEFAULT;
+}
+Closure closure_eval(ClosureDiffuse diffuse,
+ ClosureReflection reflection,
+ ClosureReflection clearcoat)
+{
+ return CLOSURE_DEFAULT;
+}
+Closure closure_eval(ClosureDiffuse diffuse,
+ ClosureReflection reflection,
+ ClosureReflection clearcoat,
+ ClosureRefraction refraction)
+{
+ return CLOSURE_DEFAULT;
+}
+Closure closure_eval(ClosureReflection reflection, ClosureReflection clearcoat)
+{
+ return CLOSURE_DEFAULT;
+}
+Closure closure_eval(ClosureHair hair)
+{
+ return CLOSURE_DEFAULT;
+}
+
+Closure closure_eval(ClosureVolumeScatter volume_scatter)
+{
+ Closure closure = CLOSURE_DEFAULT;
+ closure.scatter = volume_scatter.scattering;
+ closure.anisotropy = volume_scatter.anisotropy;
+ return closure;
+}
+Closure closure_eval(ClosureVolumeAbsorption volume_absorption)
+{
+ Closure closure = CLOSURE_DEFAULT;
+ closure.absorption = volume_absorption.absorption;
+ return closure;
+}
+Closure closure_eval(ClosureVolumeScatter volume_scatter,
+ ClosureVolumeAbsorption volume_absorption,
+ ClosureEmission emission)
+{
+ Closure closure = CLOSURE_DEFAULT;
+ closure.absorption = volume_absorption.absorption;
+ closure.scatter = volume_scatter.scattering;
+ closure.anisotropy = volume_scatter.anisotropy;
+ closure.emission = emission.emission;
+ return closure;
+}
+
+vec4 closure_to_rgba(Closure closure)
+{
+ /* Not supported */
+ return vec4(0.0);
+}
+
+Closure closure_mix(Closure cl1, Closure cl2, float fac)
+{
+ Closure cl;
+ cl.absorption = mix(cl1.absorption, cl2.absorption, fac);
+ cl.scatter = mix(cl1.scatter, cl2.scatter, fac);
+ cl.emission = mix(cl1.emission, cl2.emission, fac);
+ cl.anisotropy = mix(cl1.anisotropy, cl2.anisotropy, fac);
+ return cl;
+}
+
+Closure closure_add(Closure cl1, Closure cl2)
+{
+ Closure cl;
+ cl.absorption = cl1.absorption + cl2.absorption;
+ cl.scatter = cl1.scatter + cl2.scatter;
+ cl.emission = cl1.emission + cl2.emission;
+ cl.anisotropy = (cl1.anisotropy + cl2.anisotropy) / 2.0; /* Average phase (no multi lobe) */
+ return cl;
+}
diff --git a/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl
index fefc8743691..0096cd1747f 100644
--- a/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl
@@ -1,6 +1,8 @@
-#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
-#pragma BLENDER_REQUIRE(renderpass_lib.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_codegen_lib.glsl)
+/* #pragma (common_math_geom_lib.glsl) */
+/* #pragma (common_uniforms_lib.glsl) */
+/* #pragma (renderpass_lib.glsl) */
#ifndef VOLUMETRICS
@@ -20,172 +22,92 @@ struct Closure {
vec3 radiance;
vec3 transmittance;
float holdout;
- vec4 ssr_data;
- vec2 ssr_normal;
- int flag;
-# ifdef USE_SSS
- vec3 sss_irradiance;
- vec3 sss_albedo;
- float sss_radius;
-# endif
-
#endif
+
+/* Metal Default Constructor - Requred for C++ constructor syntax. */
+#ifdef GPU_METAL
+ inline Closure() = default;
+# ifdef VOLUMETRICS
+ /* Explicit Closure constructors -- To support GLSL syntax */
+ inline Closure(vec3 in_absorption, vec3 in_scatter, vec3 in_emission, float in_anisotropy)
+ : absorption(in_absorption),
+ scatter(in_scatter),
+ emission(in_emission),
+ anisotropy(in_anisotropy)
+ {
+ }
+# else
+ /* Explicit Closure constructors -- To support GLSL syntax */
+ inline Closure(vec3 in_radiance, vec3 in_transmittance, float in_holdout)
+ : radiance(in_radiance), transmittance(in_transmittance), holdout(in_holdout)
+ {
+ }
+# endif /* VOLUMETRICS */
+#endif /* GPU_METAL */
};
#ifndef GPU_METAL
/* Prototype */
-Closure nodetree_exec(void);
+Closure nodetree_exec();
+vec4 closure_to_rgba(Closure);
+void output_aov(vec4 color, float value, uint hash);
+vec3 coordinate_camera(vec3 P);
+vec3 coordinate_screen(vec3 P);
+vec3 coordinate_reflect(vec3 P, vec3 N);
+vec3 coordinate_incoming(vec3 P);
+
+/* Single BSDFs. */
+Closure closure_eval(ClosureDiffuse diffuse);
+Closure closure_eval(ClosureTranslucent translucent);
+Closure closure_eval(ClosureReflection reflection);
+Closure closure_eval(ClosureRefraction refraction);
+Closure closure_eval(ClosureEmission emission);
+Closure closure_eval(ClosureTransparency transparency);
+Closure closure_eval(ClosureVolumeScatter volume_scatter);
+Closure closure_eval(ClosureVolumeAbsorption volume_absorption);
+Closure closure_eval(ClosureHair hair);
+
+/* Glass BSDF. */
+Closure closure_eval(ClosureReflection reflection, ClosureRefraction refraction);
+/* Dielectric BSDF. */
+Closure closure_eval(ClosureDiffuse diffuse, ClosureReflection reflection);
+/* ClearCoat BSDF. */
+Closure closure_eval(ClosureReflection reflection, ClosureReflection clearcoat);
+/* Volume BSDF. */
+Closure closure_eval(ClosureVolumeScatter volume_scatter,
+ ClosureVolumeAbsorption volume_absorption,
+ ClosureEmission emission);
+/* Specular BSDF. */
+Closure closure_eval(ClosureDiffuse diffuse,
+ ClosureReflection reflection,
+ ClosureReflection clearcoat);
+/* Principled BSDF. */
+Closure closure_eval(ClosureDiffuse diffuse,
+ ClosureReflection reflection,
+ ClosureReflection clearcoat,
+ ClosureRefraction refraction);
+
+Closure closure_add(Closure cl1, Closure cl2);
+Closure closure_mix(Closure cl1, Closure cl2, float fac);
+
+float ambient_occlusion_eval(vec3 normal,
+ float distance,
+ const float inverted,
+ const float sample_count);
+
+/* WORKAROUND: Included later with libs. This is because we are mixing include systems. */
+vec3 safe_normalize(vec3 N);
+float fast_sqrt(float a);
+vec3 cameraVec(vec3 P);
+vec2 btdf_lut(float a, float b, float c);
+vec2 brdf_lut(float a, float b);
+vec3 F_brdf_multi_scatter(vec3 a, vec3 b, vec2 c);
+vec3 F_brdf_single_scatter(vec3 a, vec3 b, vec2 c);
+float F_eta(float a, float b);
#endif
-/* clang-format off */
-/* Avoid multi-line defines. */
#ifdef VOLUMETRICS
# define CLOSURE_DEFAULT Closure(vec3(0), vec3(0), vec3(0), 0.0)
-#elif !defined(USE_SSS)
-# define CLOSURE_DEFAULT Closure(vec3(0), vec3(0), 0.0, vec4(0), vec2(0), 0)
#else
-# define CLOSURE_DEFAULT Closure(vec3(0), vec3(0), 0.0, vec4(0), vec2(0), 0, vec3(0), vec3(0), 0.0)
-#endif
-/* clang-format on */
-
-#define FLAG_TEST(flag, val) (((flag) & (val)) != 0)
-
-#define CLOSURE_SSR_FLAG 1
-#define CLOSURE_SSS_FLAG 2
-#define CLOSURE_HOLDOUT_FLAG 4
-
-#ifdef VOLUMETRICS
-Closure closure_mix(Closure cl1, Closure cl2, float fac)
-{
- Closure cl;
- cl.absorption = mix(cl1.absorption, cl2.absorption, fac);
- cl.scatter = mix(cl1.scatter, cl2.scatter, fac);
- cl.emission = mix(cl1.emission, cl2.emission, fac);
- cl.anisotropy = mix(cl1.anisotropy, cl2.anisotropy, fac);
- return cl;
-}
-
-Closure closure_add(Closure cl1, Closure cl2)
-{
- Closure cl;
- cl.absorption = cl1.absorption + cl2.absorption;
- cl.scatter = cl1.scatter + cl2.scatter;
- cl.emission = cl1.emission + cl2.emission;
- cl.anisotropy = (cl1.anisotropy + cl2.anisotropy) / 2.0; /* Average phase (no multi lobe) */
- return cl;
-}
-
-Closure closure_emission(vec3 rgb)
-{
- Closure cl = CLOSURE_DEFAULT;
- cl.emission = rgb;
- return cl;
-}
-
-#else /* SURFACE */
-
-Closure closure_mix(Closure cl1, Closure cl2, float fac)
-{
- Closure cl;
- cl.holdout = mix(cl1.holdout, cl2.holdout, fac);
-
- if (FLAG_TEST(cl1.flag, CLOSURE_HOLDOUT_FLAG)) {
- fac = 1.0;
- }
- else if (FLAG_TEST(cl2.flag, CLOSURE_HOLDOUT_FLAG)) {
- fac = 0.0;
- }
-
- cl.transmittance = mix(cl1.transmittance, cl2.transmittance, fac);
- cl.radiance = mix(cl1.radiance, cl2.radiance, fac);
- cl.flag = cl1.flag | cl2.flag;
- cl.ssr_data = mix(cl1.ssr_data, cl2.ssr_data, fac);
- bool use_cl1_ssr = FLAG_TEST(cl1.flag, CLOSURE_SSR_FLAG);
- /* When mixing SSR don't blend roughness and normals but only specular (ssr_data.xyz). */
- cl.ssr_data.w = (use_cl1_ssr) ? cl1.ssr_data.w : cl2.ssr_data.w;
- cl.ssr_normal = (use_cl1_ssr) ? cl1.ssr_normal : cl2.ssr_normal;
-
-# ifdef USE_SSS
- cl.sss_albedo = mix(cl1.sss_albedo, cl2.sss_albedo, fac);
- bool use_cl1_sss = FLAG_TEST(cl1.flag, CLOSURE_SSS_FLAG);
- /* It also does not make sense to mix SSS radius or irradiance. */
- cl.sss_radius = (use_cl1_sss) ? cl1.sss_radius : cl2.sss_radius;
- cl.sss_irradiance = (use_cl1_sss) ? cl1.sss_irradiance : cl2.sss_irradiance;
-# endif
- return cl;
-}
-
-Closure closure_add(Closure cl1, Closure cl2)
-{
- Closure cl;
- cl.transmittance = cl1.transmittance + cl2.transmittance;
- cl.radiance = cl1.radiance + cl2.radiance;
- cl.holdout = cl1.holdout + cl2.holdout;
- cl.flag = cl1.flag | cl2.flag;
- cl.ssr_data = cl1.ssr_data + cl2.ssr_data;
- bool use_cl1_ssr = FLAG_TEST(cl1.flag, CLOSURE_SSR_FLAG);
- /* When mixing SSR don't blend roughness and normals. */
- cl.ssr_data.w = (use_cl1_ssr) ? cl1.ssr_data.w : cl2.ssr_data.w;
- cl.ssr_normal = (use_cl1_ssr) ? cl1.ssr_normal : cl2.ssr_normal;
-
-# ifdef USE_SSS
- cl.sss_albedo = cl1.sss_albedo + cl2.sss_albedo;
- bool use_cl1_sss = FLAG_TEST(cl1.flag, CLOSURE_SSS_FLAG);
- /* It also does not make sense to mix SSS radius or irradiance. */
- cl.sss_radius = (use_cl1_sss) ? cl1.sss_radius : cl2.sss_radius;
- cl.sss_irradiance = (use_cl1_sss) ? cl1.sss_irradiance : cl2.sss_irradiance;
-# endif
- return cl;
-}
-
-Closure closure_emission(vec3 rgb)
-{
- Closure cl = CLOSURE_DEFAULT;
- cl.radiance = rgb;
- return cl;
-}
-
-#endif
-
-#ifndef VOLUMETRICS
-
-/* Let radiance passthrough or replace it to get the BRDF and color
- * to applied to the SSR result. */
-vec3 closure_mask_ssr_radiance(vec3 radiance, float ssr_id)
-{
- return (ssrToggle && int(ssr_id) == outputSsrId) ? vec3(1.0) : radiance;
-}
-
-void closure_load_ssr_data(
- vec3 ssr_radiance, float roughness, vec3 N, float ssr_id, inout Closure cl)
-{
- /* Still encode to avoid artifacts in the SSR pass. */
- vec3 vN = normalize(mat3(ViewMatrix) * N);
- cl.ssr_normal = normal_encode(vN, viewCameraVec(viewPosition));
-
- if (ssrToggle && int(ssr_id) == outputSsrId) {
- cl.ssr_data = vec4(ssr_radiance, roughness);
- cl.flag |= CLOSURE_SSR_FLAG;
- }
- else {
- cl.radiance += ssr_radiance;
- }
-}
-
-void closure_load_sss_data(
- float radius, vec3 sss_irradiance, vec3 sss_albedo, int sss_id, inout Closure cl)
-{
-# ifdef USE_SSS
- if (sss_id == outputSssId) {
- cl.sss_irradiance = sss_irradiance;
- cl.sss_radius = radius;
- cl.sss_albedo = sss_albedo;
- cl.flag |= CLOSURE_SSS_FLAG;
- /* Irradiance will be convolved by SSSS pass. Do not add to radiance. */
- sss_irradiance = vec3(0);
- }
-# endif
- cl.radiance += render_pass_diffuse_mask(vec3(1), sss_irradiance) * sss_albedo;
-}
-
+# define CLOSURE_DEFAULT Closure(vec3(0), vec3(0), 0.0)
#endif
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_empty.glsl b/source/blender/draw/engines/eevee/shaders/eevee_empty.glsl
new file mode 100644
index 00000000000..e00bcc4e557
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_empty.glsl
@@ -0,0 +1,7 @@
+
+/* Empty GLSL source to satisfy the GPUShaderCreateInfo requirements. */
+/* Needed includes for shader nodes. */
+#pragma BLENDER_REQUIRE(closure_type_lib.glsl)
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(common_attribute_lib.glsl)
diff --git a/source/blender/draw/engines/eevee/shaders/eevee_empty_volume.glsl b/source/blender/draw/engines/eevee/shaders/eevee_empty_volume.glsl
new file mode 100644
index 00000000000..a748c0092b6
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/eevee_empty_volume.glsl
@@ -0,0 +1,8 @@
+
+/* Empty GLSL source to satisfy the GPUShaderCreateInfo requirements. */
+/* Needed includes for shader nodes. */
+#pragma BLENDER_REQUIRE(closure_type_lib.glsl)
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(common_attribute_lib.glsl)
+#pragma BLENDER_REQUIRE(closure_eval_volume_lib.glsl)
diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl
index e288e1a55ea..9ed6ffa90c7 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/effect_dof_lib.glsl
@@ -334,7 +334,15 @@ struct DofGatherData {
float layer_opacity;
};
-#define GATHER_DATA_INIT DofGatherData(vec4(0.0), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
+#ifdef GPU_METAL
+/* C++ struct initialization. */
+# define GATHER_DATA_INIT \
+ { \
+ vec4(0.0), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 \
+ }
+#else
+# define GATHER_DATA_INIT DofGatherData(vec4(0.0), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
+#endif
void dof_gather_ammend_weight(inout DofGatherData sample_data, float weight)
{
diff --git a/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl
index 1aff93e01f8..e86c5e06c99 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/effect_subsurface_frag.glsl
@@ -11,6 +11,7 @@ layout(std140) uniform sssProfile
{
vec4 sss_kernel[MAX_SSS_SAMPLES];
vec4 radii_max_radius;
+ float avg_inv_radius;
int sss_samples;
};
@@ -26,7 +27,7 @@ void main(void)
vec2 pixel_size = 1.0 / vec2(textureSize(depthBuffer, 0).xy); /* TODO: precompute. */
vec2 uvs = gl_FragCoord.xy * pixel_size;
vec3 sss_irradiance = texture(sssIrradiance, uvs).rgb;
- float sss_radius = texture(sssRadius, uvs).r * radii_max_radius.w;
+ float sss_radius = texture(sssRadius, uvs).r * radii_max_radius.w * avg_inv_radius;
float depth = texture(depthBuffer, uvs).r;
float depth_view = get_view_z_from_depth(depth);
diff --git a/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl
index 3cf21dc32d1..5e1725ace97 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl
@@ -23,12 +23,13 @@ layout(std140) uniform sssProfile
{
vec4 sss_kernel[MAX_SSS_SAMPLES];
vec4 radii_max_radius;
+ float avg_inv_radius;
int sss_samples;
};
vec3 sss_profile(float s)
{
- s /= radii_max_radius.w;
+ s /= radii_max_radius.w * avg_inv_radius;
return texture(sssTexProfile, saturate(s) * SSS_LUT_SCALE + SSS_LUT_BIAS).rgb;
}
diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl
index d25ef23a706..681e69ae384 100644
--- a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_visibility_frag.glsl
@@ -15,7 +15,6 @@ uniform float visibilityRange;
uniform float visibilityBlur;
uniform float sampleCount;
-uniform float;
out vec4 FragColor;
diff --git a/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl b/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl
index fd08dfda060..c8eea8d7860 100644
--- a/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl
@@ -2,15 +2,12 @@
/* Required by some nodes. */
#pragma BLENDER_REQUIRE(common_hair_lib.glsl)
#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(common_uniforms_lib.glsl)
-#pragma BLENDER_REQUIRE(closure_type_lib.glsl)
-#pragma BLENDER_REQUIRE(closure_eval_lib.glsl)
-#pragma BLENDER_REQUIRE(closure_eval_diffuse_lib.glsl)
-#pragma BLENDER_REQUIRE(closure_eval_glossy_lib.glsl)
-#pragma BLENDER_REQUIRE(closure_eval_translucent_lib.glsl)
-#pragma BLENDER_REQUIRE(closure_eval_refraction_lib.glsl)
+#pragma BLENDER_REQUIRE(closure_eval_surface_lib.glsl)
#pragma BLENDER_REQUIRE(surface_lib.glsl)
#ifdef USE_ALPHA_HASH
@@ -73,6 +70,7 @@ float hashed_alpha_threshold(vec3 co)
void main()
{
#if defined(USE_ALPHA_HASH)
+ g_data = init_globals();
Closure cl = nodetree_exec();
diff --git a/source/blender/draw/engines/eevee/shaders/prepass_vert.glsl b/source/blender/draw/engines/eevee/shaders/prepass_vert.glsl
deleted file mode 100644
index f650e2eeb8c..00000000000
--- a/source/blender/draw/engines/eevee/shaders/prepass_vert.glsl
+++ /dev/null
@@ -1,35 +0,0 @@
-
-#pragma BLENDER_REQUIRE(common_hair_lib.glsl)
-#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-
-#ifndef HAIR_SHADER
-in vec3 pos;
-#endif
-
-void main()
-{
- GPU_INTEL_VERTEX_SHADER_WORKAROUND
-
-#ifdef HAIR_SHADER
- float time, thick_time, thickness;
- vec3 worldPosition, tan, binor;
- hair_get_pos_tan_binor_time((ProjectionMatrix[3][3] == 0.0),
- ModelMatrixInverse,
- ViewMatrixInverse[3].xyz,
- ViewMatrixInverse[2].xyz,
- worldPosition,
- tan,
- binor,
- time,
- thickness,
- thick_time);
-#else
- vec3 worldPosition = point_object_to_world(pos);
-#endif
-
- gl_Position = point_world_to_ndc(worldPosition);
-
-#ifdef CLIP_PLANES
- gl_ClipDistance[0] = dot(vec4(worldPosition.xyz, 1.0), clipPlanes[0]);
-#endif
-}
diff --git a/source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl b/source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl
index 3e0a5e76d00..f276e4f26ca 100644
--- a/source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/renderpass_lib.glsl
@@ -1,4 +1,4 @@
-#define EEVEE_AOV_HASH_COLOR_TYPE_MASK 1
+#define EEVEE_AOV_HASH_COLOR_TYPE_MASK 1u
/* ---------------------------------------------------------------------- */
/** \name Resources
@@ -14,7 +14,7 @@ layout(std140) uniform renderpass_block
bool renderPassSSSColor;
bool renderPassEnvironment;
bool renderPassAOV;
- int renderPassAOVActive;
+ uint renderPassAOVActive;
};
/** \} */
@@ -23,19 +23,14 @@ layout(std140) uniform renderpass_block
/** \name Functions
* \{ */
-vec3 render_pass_diffuse_mask(vec3 diffuse_color, vec3 diffuse_light)
+vec3 render_pass_diffuse_mask(vec3 diffuse_light)
{
- return renderPassDiffuse ? (renderPassDiffuseLight ? diffuse_light : diffuse_color) : vec3(0.0);
+ return renderPassDiffuse ? (renderPassDiffuseLight ? diffuse_light : vec3(1.0)) : vec3(0.0);
}
-vec3 render_pass_sss_mask(vec3 sss_color)
+vec3 render_pass_glossy_mask(vec3 specular_light)
{
- return renderPassSSSColor ? sss_color : vec3(0.0);
-}
-
-vec3 render_pass_glossy_mask(vec3 specular_color, vec3 specular_light)
-{
- return renderPassGlossy ? (renderPassGlossyLight ? specular_light : specular_color) : vec3(0.0);
+ return renderPassGlossy ? (renderPassGlossyLight ? specular_light : vec3(1.0)) : vec3(0.0);
}
vec3 render_pass_emission_mask(vec3 emission_light)
@@ -45,10 +40,10 @@ vec3 render_pass_emission_mask(vec3 emission_light)
bool render_pass_aov_is_color()
{
- return (renderPassAOVActive & EEVEE_AOV_HASH_COLOR_TYPE_MASK) != 0;
+ return (renderPassAOVActive & EEVEE_AOV_HASH_COLOR_TYPE_MASK) != 0u;
}
-int render_pass_aov_hash()
+uint render_pass_aov_hash()
{
return renderPassAOVActive & ~EEVEE_AOV_HASH_COLOR_TYPE_MASK;
}
diff --git a/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl b/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl
index cbfa9737a84..0e8e8dd9d01 100644
--- a/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl
+++ b/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl
@@ -1,5 +1,6 @@
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
#pragma BLENDER_REQUIRE(common_hair_lib.glsl)
#pragma BLENDER_REQUIRE(surface_lib.glsl)
@@ -12,6 +13,7 @@ void main()
#ifdef HAIR_SHADER
hairStrandID = hair_get_strand_id();
+ hairBary = hair_get_barycentric();
vec3 pos, binor;
hair_get_pos_tan_binor_time((ProjectionMatrix[3][3] == 0.0),
ModelMatrixInverse,
@@ -52,3 +54,93 @@ void main()
# endif
#endif
}
+
+#ifdef HAIR_SHADER
+# ifdef OBINFO_LIB
+vec3 attr_load_orco(samplerBuffer cd_buf)
+{
+ vec3 P = hair_get_strand_pos();
+ vec3 lP = transform_point(ModelMatrixInverse, P);
+ return OrcoTexCoFactors[0].xyz + lP * OrcoTexCoFactors[1].xyz;
+}
+# endif
+
+vec4 attr_load_tangent(samplerBuffer cd_buf)
+{
+ /* Not supported. */
+ return vec4(0.0, 0.0, 0.0, 1.0);
+}
+
+vec3 attr_load_uv(samplerBuffer cd_buf)
+{
+ return texelFetch(cd_buf, hairStrandID).rgb;
+}
+
+vec4 attr_load_color(samplerBuffer cd_buf)
+{
+ return texelFetch(cd_buf, hairStrandID).rgba;
+}
+
+vec4 attr_load_vec4(samplerBuffer cd_buf)
+{
+ return texelFetch(cd_buf, hairStrandID).rgba;
+}
+
+vec3 attr_load_vec3(samplerBuffer cd_buf)
+{
+ return texelFetch(cd_buf, hairStrandID).rgb;
+}
+
+vec2 attr_load_vec2(samplerBuffer cd_buf)
+{
+ return texelFetch(cd_buf, hairStrandID).rg;
+}
+
+float attr_load_float(samplerBuffer cd_buf)
+{
+ return texelFetch(cd_buf, hairStrandID).r;
+}
+
+#else
+
+# ifdef OBINFO_LIB
+vec3 attr_load_orco(samplerBuffer cd_buf)
+{
+ vec3 P = hair_get_strand_pos();
+ vec3 lP = transform_point(ModelMatrixInverse, P);
+ return OrcoTexCoFactors[0].xyz + lP * OrcoTexCoFactors[1].xyz;
+}
+# endif
+
+vec4 attr_load_tangent(vec4 tangent)
+{
+ tangent.xyz = safe_normalize(normal_object_to_world(tangent.xyz));
+ return tangent;
+}
+
+/* Simple passthrough. */
+vec4 attr_load_vec4(vec4 attr)
+{
+ return attr;
+}
+vec3 attr_load_vec3(vec3 attr)
+{
+ return attr;
+}
+vec2 attr_load_vec2(vec2 attr)
+{
+ return attr;
+}
+vec2 attr_load_float(vec2 attr)
+{
+ return attr;
+}
+vec4 attr_load_color(vec4 attr)
+{
+ return attr;
+}
+vec3 attr_load_uv(vec3 attr)
+{
+ return attr;
+}
+#endif
diff --git a/source/blender/draw/engines/eevee/shaders/surface_frag.glsl b/source/blender/draw/engines/eevee/shaders/surface_frag.glsl
index 889bf439d5f..9ad7a4fdbc1 100644
--- a/source/blender/draw/engines/eevee/shaders/surface_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/surface_frag.glsl
@@ -2,16 +2,14 @@
/* Required by some nodes. */
#pragma BLENDER_REQUIRE(common_hair_lib.glsl)
#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
-#pragma BLENDER_REQUIRE(closure_type_lib.glsl)
-#pragma BLENDER_REQUIRE(closure_eval_lib.glsl)
-#pragma BLENDER_REQUIRE(closure_eval_diffuse_lib.glsl)
-#pragma BLENDER_REQUIRE(closure_eval_glossy_lib.glsl)
-#pragma BLENDER_REQUIRE(closure_eval_translucent_lib.glsl)
-#pragma BLENDER_REQUIRE(closure_eval_refraction_lib.glsl)
+#pragma BLENDER_REQUIRE(closure_eval_surface_lib.glsl)
#pragma BLENDER_REQUIRE(surface_lib.glsl)
#pragma BLENDER_REQUIRE(volumetric_lib.glsl)
+#pragma BLENDER_REQUIRE(renderpass_lib.glsl)
#ifdef USE_ALPHA_BLEND
/* Use dual source blending to be able to make a whole range of effects. */
@@ -22,18 +20,74 @@ layout(location = 0, index = 1) out vec4 outTransmittance;
layout(location = 0) out vec4 outRadiance;
layout(location = 1) out vec2 ssrNormals;
layout(location = 2) out vec4 ssrData;
-# ifdef USE_SSS
layout(location = 3) out vec3 sssIrradiance;
layout(location = 4) out float sssRadius;
layout(location = 5) out vec3 sssAlbedo;
+
+#endif
+
+uniform float backgroundAlpha;
+
+#ifdef EEVEE_DISPLACEMENT_BUMP
+
+# ifndef GPU_METAL
+/* Prototype. */
+vec3 displacement_exec();
# endif
+/* Return new shading normal. */
+vec3 displacement_bump()
+{
+ vec2 dHd;
+ dF_branch(dot(displacement_exec(), g_data.N + dF_impl(g_data.N)), dHd);
+
+ vec3 dPdx = dFdx(g_data.P);
+ vec3 dPdy = dFdy(g_data.P);
+
+ /* Get surface tangents from normal. */
+ vec3 Rx = cross(dPdy, g_data.N);
+ vec3 Ry = cross(g_data.N, dPdx);
+
+ /* Compute surface gradient and determinant. */
+ float det = dot(dPdx, Rx);
+
+ vec3 surfgrad = dHd.x * Rx + dHd.y * Ry;
+
+ float facing = FrontFacing ? 1.0 : -1.0;
+ return normalize(abs(det) * g_data.N - facing * sign(det) * surfgrad);
+}
+
#endif
void main()
{
+ g_data = init_globals();
+
+#ifdef EEVEE_DISPLACEMENT_BUMP
+ g_data.N = displacement_bump();
+#endif
+
+#if defined(WORLD_BACKGROUND) || defined(PROBE_CAPTURE)
+ attrib_load();
+#endif
+
+ out_ssr_color = vec3(0.0);
+ out_ssr_roughness = 0.0;
+ out_ssr_N = g_data.N;
+
+ out_sss_radiance = vec3(0.0);
+ out_sss_radius = 0.0;
+ out_sss_color = vec3(0.0);
+
Closure cl = nodetree_exec();
+#ifdef WORLD_BACKGROUND
+ if (!renderPassEnvironment) {
+ cl.holdout += 1.0 - backgroundAlpha;
+ cl.radiance *= backgroundAlpha;
+ }
+#endif
+
float holdout = saturate(1.0 - cl.holdout);
float transmit = saturate(avg(cl.transmittance));
float alpha = 1.0 - transmit;
@@ -53,38 +107,40 @@ void main()
outTransmittance = vec4(cl.transmittance, transmit) * holdout;
#else
outRadiance = vec4(cl.radiance, holdout);
- ssrNormals = cl.ssr_normal;
- ssrData = cl.ssr_data;
-# ifdef USE_SSS
- sssIrradiance = cl.sss_irradiance;
- sssRadius = cl.sss_radius;
- sssAlbedo = cl.sss_albedo;
-# endif
+ ssrNormals = normal_encode(normalize(mat3(ViewMatrix) * out_ssr_N), vec3(0.0));
+ ssrData = vec4(out_ssr_color, out_ssr_roughness);
+ sssIrradiance = out_sss_radiance;
+ sssRadius = out_sss_radius;
+ sssAlbedo = out_sss_color;
#endif
- /* For Probe capture */
-#ifdef USE_SSS
- float fac = float(!sssToggle);
-
- /* TODO(fclem): we shouldn't need this.
- * Just disable USE_SSS when USE_REFRACTION is enabled. */
-# ifdef USE_REFRACTION
+#ifdef USE_REFRACTION
/* SSRefraction pass is done after the SSS pass.
* In order to not lose the diffuse light totally we
* need to merge the SSS radiance to the main radiance. */
- fac = 1.0;
-# endif
-
- outRadiance.rgb += cl.sss_irradiance.rgb * cl.sss_albedo.rgb * fac;
+ const bool use_refraction = true;
+#else
+ const bool use_refraction = false;
#endif
+ /* For Probe capture */
+ if (!sssToggle || use_refraction) {
+ outRadiance.rgb += out_sss_radiance * out_sss_color;
+ }
#ifndef USE_ALPHA_BLEND
float alpha_div = safe_rcp(alpha);
outRadiance.rgb *= alpha_div;
ssrData.rgb *= alpha_div;
-# ifdef USE_SSS
sssAlbedo.rgb *= alpha_div;
-# endif
+
+ if (renderPassAOV) {
+ if (aov_is_valid) {
+ outRadiance = vec4(out_aov, 1.0);
+ }
+ else {
+ outRadiance = vec4(0.0);
+ }
+ }
#endif
#ifdef LOOKDEV
@@ -92,3 +148,34 @@ void main()
gl_FragDepth = 0.0;
#endif
}
+
+/* Only supported attrib for world/background shaders. */
+vec3 attr_load_orco(vec4 orco)
+{
+ return g_data.P;
+}
+/* Unsupported. */
+vec4 attr_load_tangent(vec4 tangent)
+{
+ return vec4(0);
+}
+vec4 attr_load_vec4(vec4 attr)
+{
+ return vec4(0);
+}
+vec3 attr_load_vec3(vec3 attr)
+{
+ return vec3(0);
+}
+vec2 attr_load_vec2(vec2 attr)
+{
+ return vec2(0);
+}
+vec4 attr_load_color(vec4 attr)
+{
+ return vec4(0);
+}
+vec3 attr_load_uv(vec3 attr)
+{
+ return vec3(0);
+}
diff --git a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl
index d7fc5e0b52a..1f2f7cb65cc 100644
--- a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl
@@ -1,12 +1,23 @@
/** This describe the entire interface of the shader. */
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+
#define SURFACE_INTERFACE \
vec3 worldPosition; \
vec3 viewPosition; \
vec3 worldNormal; \
vec3 viewNormal;
-#if defined(STEP_RESOLVE) || defined(STEP_RAYTRACE)
+#ifndef IN_OUT
+# if defined(GPU_VERTEX_SHADER)
+# define IN_OUT out
+# elif defined(GPU_FRAGMENT_SHADER)
+# define IN_OUT in
+# endif
+#endif
+
+#ifndef EEVEE_GENERATED_INTERFACE
+# if defined(STEP_RESOLVE) || defined(STEP_RAYTRACE)
/* SSR will set these global variables itself.
* Also make false positive compiler warnings disappear by setting values. */
vec3 worldPosition = vec3(0);
@@ -14,22 +25,23 @@ vec3 viewPosition = vec3(0);
vec3 worldNormal = vec3(0);
vec3 viewNormal = vec3(0);
-#elif defined(GPU_GEOMETRY_SHADER)
+# elif defined(GPU_GEOMETRY_SHADER)
in ShaderStageInterface{SURFACE_INTERFACE} dataIn[];
out ShaderStageInterface{SURFACE_INTERFACE} dataOut;
-# define PASS_SURFACE_INTERFACE(vert) \
- dataOut.worldPosition = dataIn[vert].worldPosition; \
- dataOut.viewPosition = dataIn[vert].viewPosition; \
- dataOut.worldNormal = dataIn[vert].worldNormal; \
- dataOut.viewNormal = dataIn[vert].viewNormal;
+# define PASS_SURFACE_INTERFACE(vert) \
+ dataOut.worldPosition = dataIn[vert].worldPosition; \
+ dataOut.viewPosition = dataIn[vert].viewPosition; \
+ dataOut.worldNormal = dataIn[vert].worldNormal; \
+ dataOut.viewNormal = dataIn[vert].viewNormal;
-#else /* GPU_VERTEX_SHADER || GPU_FRAGMENT_SHADER*/
+# else /* GPU_VERTEX_SHADER || GPU_FRAGMENT_SHADER*/
IN_OUT ShaderStageInterface{SURFACE_INTERFACE};
-#endif
+# endif
+#endif /* EEVEE_GENERATED_INTERFACE */
#ifdef HAIR_SHADER
IN_OUT ShaderHairInterface
@@ -40,6 +52,7 @@ IN_OUT ShaderHairInterface
float hairThickness;
float hairTime;
flat int hairStrandID;
+ vec2 hairBary;
};
#endif
@@ -52,3 +65,138 @@ IN_OUT ShaderPointCloudInterface
flat int pointID;
};
#endif
+
+#if defined(GPU_FRAGMENT_SHADER) && defined(CODEGEN_LIB)
+
+# if defined(USE_BARYCENTRICS) && !defined(HAIR_SHADER)
+vec3 barycentric_distances_get()
+{
+ /* NOTE: No need to undo perspective divide since it is not applied yet. */
+ vec3 pos0 = (ProjectionMatrixInverse * gpu_position_at_vertex(0)).xyz;
+ vec3 pos1 = (ProjectionMatrixInverse * gpu_position_at_vertex(1)).xyz;
+ vec3 pos2 = (ProjectionMatrixInverse * gpu_position_at_vertex(2)).xyz;
+ vec3 edge21 = pos2 - pos1;
+ vec3 edge10 = pos1 - pos0;
+ vec3 edge02 = pos0 - pos2;
+ vec3 d21 = safe_normalize(edge21);
+ vec3 d10 = safe_normalize(edge10);
+ vec3 d02 = safe_normalize(edge02);
+ vec3 dists;
+ float d = dot(d21, edge02);
+ dists.x = sqrt(dot(edge02, edge02) - d * d);
+ d = dot(d02, edge10);
+ dists.y = sqrt(dot(edge10, edge10) - d * d);
+ d = dot(d10, edge21);
+ dists.z = sqrt(dot(edge21, edge21) - d * d);
+ return dists.xyz;
+}
+# endif
+
+GlobalData init_globals(void)
+{
+ GlobalData surf;
+
+# if defined(WORLD_BACKGROUND) || defined(PROBE_CAPTURE)
+ surf.P = -cameraVec(worldPosition);
+ surf.N = surf.Ng = -surf.P;
+ surf.ray_length = 0.0;
+# else
+ surf.P = worldPosition;
+ surf.N = safe_normalize(worldNormal);
+ surf.Ng = safe_normalize(cross(dFdx(surf.P), dFdy(surf.P)));
+ surf.ray_length = distance(surf.P, cameraPos);
+# endif
+ surf.barycentric_coords = vec2(0.0);
+ surf.barycentric_dists = vec3(0.0);
+ if (!FrontFacing) {
+ surf.N = -surf.N;
+ }
+# ifdef HAIR_SHADER
+ /* Shade as a cylinder. */
+ vec3 B = normalize(cross(worldNormal, hairTangent));
+ float cos_theta;
+ if (hairThicknessRes == 1) {
+ /* Random cosine normal distribution on the hair surface. */
+ cos_theta = texelfetch_noise_tex(gl_FragCoord.xy).x * 2.0 - 1.0;
+ }
+ else {
+ /* Shade as a cylinder. */
+ cos_theta = hairThickTime / hairThickness;
+ }
+ float sin_theta = sqrt(max(0.0, 1.0 - cos_theta * cos_theta));
+ surf.N = safe_normalize(worldNormal * sin_theta + B * cos_theta);
+ surf.T = hairTangent;
+ surf.is_strand = true;
+ surf.hair_time = hairTime;
+ surf.hair_thickness = hairThickness;
+ surf.hair_strand_id = hairStrandID;
+# ifdef USE_BARYCENTRICS
+ surf.barycentric_coords = hair_resolve_barycentric(hairBary);
+# endif
+# else
+ surf.T = vec3(0.0);
+ surf.is_strand = false;
+ surf.hair_time = 0.0;
+ surf.hair_thickness = 0.0;
+ surf.hair_strand_id = 0;
+# ifdef USE_BARYCENTRICS
+ surf.barycentric_coords = gpu_BaryCoord.xy;
+ surf.barycentric_dists = barycentric_distances_get();
+# endif
+# endif
+ surf.ray_type = rayType;
+ surf.ray_depth = 0.0;
+ return surf;
+}
+#endif
+
+vec3 coordinate_camera(vec3 P)
+{
+ vec3 vP;
+#if defined(PROBE_CAPTURE)
+ /* Unsupported. It would make the probe camera-dependent. */
+ vP = P;
+#elif defined(WORLD_BACKGROUND)
+ vP = transform_direction(ViewMatrix, P);
+#else
+ vP = transform_point(ViewMatrix, P);
+#endif
+ vP.z = -vP.z;
+ return vP;
+}
+
+vec3 coordinate_screen(vec3 P)
+{
+ vec3 window = vec3(0.0);
+#if defined(PROBE_CAPTURE)
+ /* Unsupported. It would make the probe camera-dependent. */
+ window.xy = vec2(0.5);
+
+#elif defined(WORLD_BACKGROUND)
+ window.xy = project_point(ProjectionMatrix, viewPosition).xy * 0.5 + 0.5;
+ window.xy = window.xy * CameraTexCoFactors.xy + CameraTexCoFactors.zw;
+
+#else /* MESH */
+ window.xy = project_point(ViewProjectionMatrix, P).xy * 0.5 + 0.5;
+ window.xy = window.xy * CameraTexCoFactors.xy + CameraTexCoFactors.zw;
+#endif
+ return window;
+}
+
+vec3 coordinate_reflect(vec3 P, vec3 N)
+{
+#if defined(WORLD_BACKGROUND) || defined(PROBE_CAPTURE)
+ return N;
+#else
+ return -reflect(cameraVec(P), N);
+#endif
+}
+
+vec3 coordinate_incoming(vec3 P)
+{
+#if defined(WORLD_BACKGROUND) || defined(PROBE_CAPTURE)
+ return -P;
+#else
+ return cameraVec(P);
+#endif
+} \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee/shaders/surface_vert.glsl b/source/blender/draw/engines/eevee/shaders/surface_vert.glsl
index 51e9eda6cc2..6c6b810422b 100644
--- a/source/blender/draw/engines/eevee/shaders/surface_vert.glsl
+++ b/source/blender/draw/engines/eevee/shaders/surface_vert.glsl
@@ -1,6 +1,9 @@
#pragma BLENDER_REQUIRE(common_hair_lib.glsl)
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
+#pragma BLENDER_REQUIRE(closure_eval_surface_lib.glsl)
#pragma BLENDER_REQUIRE(surface_lib.glsl)
#ifndef HAIR_SHADER
@@ -18,6 +21,7 @@ void main()
#ifdef HAIR_SHADER
hairStrandID = hair_get_strand_id();
+ hairBary = hair_get_barycentric();
vec3 pos, binor;
hair_get_pos_tan_binor_time((ProjectionMatrix[3][3] == 0.0),
ModelMatrixInverse,
@@ -53,11 +57,103 @@ void main()
/* No need to normalize since this is just a rotation. */
viewNormal = normal_world_to_view(worldNormal);
-# ifdef USE_ATTR
-# ifdef HAIR_SHADER
- pos = hair_get_strand_pos();
-# endif
- pass_attr(pos, NormalMatrix, ModelMatrixInverse);
-# endif
+
+ attrib_load();
#endif
}
+
+#ifdef HAIR_SHADER
+# ifdef OBINFO_LIB
+vec3 attr_load_orco(samplerBuffer cd_buf)
+{
+ vec3 P = hair_get_strand_pos();
+ vec3 lP = transform_point(ModelMatrixInverse, P);
+ return OrcoTexCoFactors[0].xyz + lP * OrcoTexCoFactors[1].xyz;
+}
+# endif
+
+vec4 attr_load_tangent(samplerBuffer cd_buf)
+{
+ return vec4(hairTangent, 1.0);
+}
+
+vec3 attr_load_uv(samplerBuffer cd_buf)
+{
+ return texelFetch(cd_buf, hairStrandID).rgb;
+}
+
+vec4 attr_load_color(samplerBuffer cd_buf)
+{
+ return texelFetch(cd_buf, hairStrandID).rgba;
+}
+
+vec4 attr_load_vec4(samplerBuffer cd_buf)
+{
+ return texelFetch(cd_buf, hairStrandID).rgba;
+}
+
+vec3 attr_load_vec3(samplerBuffer cd_buf)
+{
+ return texelFetch(cd_buf, hairStrandID).rgb;
+}
+
+vec2 attr_load_vec2(samplerBuffer cd_buf)
+{
+ return texelFetch(cd_buf, hairStrandID).rg;
+}
+
+float attr_load_float(samplerBuffer cd_buf)
+{
+ return texelFetch(cd_buf, hairStrandID).r;
+}
+
+#else
+
+# ifdef OBINFO_LIB
+vec3 attr_load_orco(vec4 orco)
+{
+ /* We know when there is no orco layer when orco.w is 1.0 because it uses the generic vertex
+ * attrib (which is [0,0,0,1]). */
+ if (orco.w == 0.0) {
+ return orco.xyz * 0.5 + 0.5;
+ }
+ else {
+ /* If the object does not have any deformation, the orco layer calculation is done on the fly
+ * using the orco_madd factors. */
+ return OrcoTexCoFactors[0].xyz + pos * OrcoTexCoFactors[1].xyz;
+ }
+}
+# endif
+
+vec4 attr_load_tangent(vec4 tangent)
+{
+ tangent.xyz = normal_object_to_world(tangent.xyz);
+ return tangent;
+}
+
+/* Simple passthrough. */
+vec4 attr_load_vec4(vec4 attr)
+{
+ return attr;
+}
+vec3 attr_load_vec3(vec3 attr)
+{
+ return attr;
+}
+vec2 attr_load_vec2(vec2 attr)
+{
+ return attr;
+}
+float attr_load_float(float attr)
+{
+ return attr;
+}
+vec4 attr_load_color(vec4 attr)
+{
+ return attr;
+}
+vec3 attr_load_uv(vec3 attr)
+{
+ return attr;
+}
+#endif
diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl
index 9f1afc4767c..e0a79872928 100644
--- a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl
@@ -1,6 +1,5 @@
#pragma BLENDER_REQUIRE(volumetric_lib.glsl)
-#pragma BLENDER_REQUIRE(closure_type_lib.glsl)
/* Based on Frosbite Unified Volumetric.
* https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */
@@ -18,9 +17,7 @@ flat in int slice;
vec3 worldPosition = vec3(0.0);
vec3 viewPosition = vec3(0.0);
vec3 viewNormal = vec3(0.0);
-#ifdef MESH_SHADER
-vec3 volumeObjectLocalCoord = vec3(0.0);
-#endif
+vec3 volumeOrco = vec3(0.0);
layout(location = 0) out vec4 volumeScattering;
layout(location = 1) out vec4 volumeExtinction;
@@ -29,6 +26,52 @@ layout(location = 3) out vec4 volumePhase;
/* Store volumetric properties into the froxel textures. */
+#ifdef MESH_SHADER
+GlobalData init_globals(void)
+{
+ GlobalData surf;
+ surf.P = worldPosition;
+ surf.N = vec3(0.0);
+ surf.Ng = vec3(0.0);
+ surf.is_strand = false;
+ surf.hair_time = 0.0;
+ surf.hair_thickness = 0.0;
+ surf.hair_strand_id = 0;
+ surf.barycentric_coords = vec2(0.0);
+ surf.barycentric_dists = vec3(0.0);
+ surf.ray_type = RAY_TYPE_CAMERA;
+ surf.ray_depth = 0.0;
+ surf.ray_length = distance(surf.P, cameraPos);
+ return surf;
+}
+
+vec3 coordinate_camera(vec3 P)
+{
+ vec3 vP;
+ vP = transform_point(ViewMatrix, P);
+ vP.z = -vP.z;
+ return vP;
+}
+
+vec3 coordinate_screen(vec3 P)
+{
+ vec3 window = vec3(0.0);
+ window.xy = project_point(ViewProjectionMatrix, P).xy * 0.5 + 0.5;
+ window.xy = window.xy * CameraTexCoFactors.xy + CameraTexCoFactors.zw;
+ return window;
+}
+
+vec3 coordinate_reflect(vec3 P, vec3 N)
+{
+ return vec3(0.0);
+}
+
+vec3 coordinate_incoming(vec3 P)
+{
+ return cameraVec(P);
+}
+#endif
+
void main()
{
ivec3 volume_cell = ivec3(ivec2(gl_FragCoord.xy), slice);
@@ -37,33 +80,40 @@ void main()
viewPosition = get_view_space_from_depth(ndc_cell.xy, ndc_cell.z);
worldPosition = point_view_to_world(viewPosition);
#ifdef MESH_SHADER
- volumeObjectLocalCoord = point_world_to_object(worldPosition);
+ volumeOrco = point_world_to_object(worldPosition);
/* TODO: redundant transform */
- volumeObjectLocalCoord = (volumeObjectLocalCoord - volumeOrcoLoc + volumeOrcoSize) /
- (volumeOrcoSize * 2.0);
- volumeObjectLocalCoord = (volumeObjectToTexture * vec4(volumeObjectLocalCoord, 1.0)).xyz;
+ volumeOrco = (volumeOrco - volumeOrcoLoc + volumeOrcoSize) / (volumeOrcoSize * 2.0);
+ volumeOrco = (volumeObjectToTexture * vec4(volumeOrco, 1.0)).xyz;
- if (any(lessThan(volumeObjectLocalCoord, vec3(0.0))) ||
- any(greaterThan(volumeObjectLocalCoord, vec3(1.0))))
+ if (any(lessThan(volumeOrco, vec3(0.0))) || any(greaterThan(volumeOrco, vec3(1.0)))) {
+ /* Note: Discard is not an explicit return in Metal prior to versions 2.3.
+ * adding return after discard ensures consistent behaviour and avoids GPU
+ * side-effects where control flow continues with undefined values. */
discard;
+ return;
+ }
#endif
#ifdef CLEAR
- Closure cl = CLOSURE_DEFAULT;
+ volumeScattering = vec4(0.0, 0.0, 0.0, 1.0);
+ volumeExtinction = vec4(0.0, 0.0, 0.0, 1.0);
+ volumeEmissive = vec4(0.0, 0.0, 0.0, 1.0);
+ volumePhase = vec4(0.0, 0.0, 0.0, 0.0);
#else
+# ifdef MESH_SHADER
+ g_data = init_globals();
+ attrib_load();
+# endif
Closure cl = nodetree_exec();
-#endif
-
-#ifdef MESH_SHADER
+# ifdef MESH_SHADER
cl.scatter *= volumeDensityScale;
cl.absorption *= volumeDensityScale;
cl.emission *= volumeDensityScale;
-#endif
+# endif
volumeScattering = vec4(cl.scatter, 1.0);
volumeExtinction = vec4(cl.absorption + cl.scatter, 1.0);
volumeEmissive = vec4(cl.emission, 1.0);
-
/* Do not add phase weight if no scattering. */
if (all(equal(cl.scatter, vec3(0.0)))) {
volumePhase = vec4(0.0);
@@ -71,4 +121,38 @@ void main()
else {
volumePhase = vec4(cl.anisotropy, vec3(1.0));
}
+#endif
+}
+
+vec3 attr_load_orco(vec4 orco)
+{
+ return volumeOrco;
+}
+vec4 attr_load_tangent(vec4 tangent)
+{
+ return vec4(0);
+}
+vec4 attr_load_vec4(vec4 attr)
+{
+ return vec4(0);
+}
+vec3 attr_load_vec3(vec3 attr)
+{
+ return vec3(0);
+}
+vec2 attr_load_vec2(vec2 attr)
+{
+ return vec2(0);
+}
+float attr_load_float(float attr)
+{
+ return 0.0;
+}
+vec4 attr_load_color(vec4 attr)
+{
+ return vec4(0);
+}
+vec3 attr_load_uv(vec3 attr)
+{
+ return vec3(0);
}
diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl
index 5226da57a06..1269761ffa4 100644
--- a/source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl
+++ b/source/blender/draw/engines/eevee/shaders/volumetric_geom.glsl
@@ -1,11 +1,7 @@
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-#ifdef MESH_SHADER
-/* TODO: tight slices. */
-layout(triangles) in;
-layout(triangle_strip, max_vertices = 3) out;
-#else /* World */
+#ifdef STANDALONE
layout(triangles) in;
layout(triangle_strip, max_vertices = 3) out;
#endif
diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl
index 11f57c0a82e..26b60c992e1 100644
--- a/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/volumetric_integration_frag.glsl
@@ -11,8 +11,10 @@ uniform sampler3D volumeScattering; /* Result of the scatter step */
uniform sampler3D volumeExtinction;
#ifdef USE_VOLUME_OPTI
-uniform layout(r11f_g11f_b10f) writeonly restrict image3D finalScattering_img;
-uniform layout(r11f_g11f_b10f) writeonly restrict image3D finalTransmittance_img;
+uniform layout(r11f_g11f_b10f)
+writeonly restrict image3D finalScattering_img;
+uniform layout(r11f_g11f_b10f)
+writeonly restrict image3D finalTransmittance_img;
vec3 finalScattering;
vec3 finalTransmittance;
@@ -70,7 +72,11 @@ void main()
vec3 Tr = exp(-s_extinction * s_len);
/* integrate along the current step segment */
- Lscat = (Lscat - Lscat * Tr) / max(vec3(1e-8), s_extinction);
+ /* Note: Original calculation carries precision issues when compiling for AMD GPUs
+ * and running Metal. This version of the equation retains precision well for all
+ * macOS HW configurations. */
+ Lscat = (Lscat * (1.0f - Tr)) / max(vec3(1e-8), s_extinction);
+
/* accumulate and also take into account the transmittance from previous steps */
finalScattering += finalTransmittance * Lscat;
diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl
index b70747ecec3..b574e8cdb4c 100644
--- a/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl
+++ b/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl
@@ -30,8 +30,30 @@ void main()
vPos.w = 1.0;
PASS_RESOURCE_ID
+}
+
+/* Stubs */
+vec2 btdf_lut(float a, float b, float c)
+{
+ return vec2(0.0);
+}
+
+vec2 brdf_lut(float a, float b)
+{
+ return vec2(0.0);
+}
-#ifdef USE_ATTR
- pass_attr(vec3(0.0), mat3(1), mat4(1));
-#endif
+vec3 F_brdf_multi_scatter(vec3 a, vec3 b, vec2 c)
+{
+ return vec3(0.0);
+}
+
+vec3 F_brdf_single_scatter(vec3 a, vec3 b, vec2 c)
+{
+ return vec3(0.0);
+}
+
+float F_eta(float a, float b)
+{
+ return 0.0;
}
diff --git a/source/blender/draw/engines/eevee/shaders/world_vert.glsl b/source/blender/draw/engines/eevee/shaders/world_vert.glsl
new file mode 100644
index 00000000000..29892a7ffb4
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/world_vert.glsl
@@ -0,0 +1,24 @@
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(common_utiltex_lib.glsl)
+
+#pragma BLENDER_REQUIRE(closure_eval_surface_lib.glsl)
+
+in vec2 pos;
+
+RESOURCE_ID_VARYING
+
+void main()
+{
+ GPU_INTEL_VERTEX_SHADER_WORKAROUND
+
+ PASS_RESOURCE_ID
+
+ gl_Position = vec4(pos, 1.0, 1.0);
+ viewPosition = project_point(ProjectionMatrixInverse, vec3(pos, 0.0));
+ worldPosition = project_point(ViewProjectionMatrixInverse, vec3(pos, 0.0));
+ /* Not usable. */
+ viewNormal = vec3(0.0);
+ worldNormal = vec3(0.0);
+}
diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl
index cf38c1fc12c..75bd3d30d68 100644
--- a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl
+++ b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl
@@ -116,7 +116,7 @@ void blend_mode_output(
color.a *= opacity;
frag_revealage = frag_color = clamp(1.0 / max(vec4(1e-6), 1.0 - color * color.a), 0.0, 1e18);
break;
- case MODE_HARDLIGHT:
+ case MODE_HARDLIGHT: {
/* Reminder: Blending func is multiply blend (dst.rgba * src.rgba). */
/**
* We need to separate the overlay equation into 2 term (one mul and one add).
@@ -134,6 +134,7 @@ void blend_mode_output(
frag_revealage = frag_color = 2.0 * s + 2.0 * color * (1.0 - s * 2.0);
frag_revealage = max(vec4(0.0), frag_revealage);
break;
+ }
case MODE_HARDLIGHT_SECOND_PASS:
/* Reminder: Blending func is additive blend (dst.rgba + src.rgba). */
color = mix(vec4(0.5), color, color.a * opacity);
diff --git a/source/blender/draw/engines/overlay/overlay_extra.c b/source/blender/draw/engines/overlay/overlay_extra.c
index 73450db8eea..abcca5525c7 100644
--- a/source/blender/draw/engines/overlay/overlay_extra.c
+++ b/source/blender/draw/engines/overlay/overlay_extra.c
@@ -528,13 +528,13 @@ static void OVERLAY_forcefield(OVERLAY_ExtraCallBuffers *cb, Object *ob, ViewLay
case PFIELD_GUIDE:
if (cu && (cu->flag & CU_PATH) && ob->runtime.curve_cache->anim_path_accum_length) {
instdata.size_x = instdata.size_y = instdata.size_z = pd->f_strength;
- float pos[4], tmp[3];
- BKE_where_on_path(ob, 0.0f, pos, tmp, NULL, NULL, NULL);
+ float pos[4];
+ BKE_where_on_path(ob, 0.0f, pos, NULL, NULL, NULL, NULL);
copy_v3_v3(instdata.pos, ob->obmat[3]);
translate_m4(instdata.mat, pos[0], pos[1], pos[2]);
DRW_buffer_add_entry(cb->field_curve, color, &instdata);
- BKE_where_on_path(ob, 1.0f, pos, tmp, NULL, NULL, NULL);
+ BKE_where_on_path(ob, 1.0f, pos, NULL, NULL, NULL, NULL);
copy_v3_v3(instdata.pos, ob->obmat[3]);
translate_m4(instdata.mat, pos[0], pos[1], pos[2]);
DRW_buffer_add_entry(cb->field_sphere_limit, color, &instdata);
diff --git a/source/blender/draw/engines/overlay/shaders/background_frag.glsl b/source/blender/draw/engines/overlay/shaders/background_frag.glsl
index 19313c0415b..6b45b341ca4 100644
--- a/source/blender/draw/engines/overlay/shaders/background_frag.glsl
+++ b/source/blender/draw/engines/overlay/shaders/background_frag.glsl
@@ -57,13 +57,13 @@ void main()
/* XXX do interpolation in a non-linear space to have a better visual result. */
col_high = pow(colorBackground.rgb, vec3(1.0 / 2.2));
col_low = pow(colorBackgroundGradient.rgb, vec3(1.0 / 2.2));
- bg_col = mix(col_low, col_high, uvcoordsvar.t);
+ bg_col = mix(col_low, col_high, uvcoordsvar.y);
/* Convert back to linear. */
bg_col = pow(bg_col, vec3(2.2));
/* Dither to hide low precision buffer. (Could be improved) */
bg_col += dither();
break;
- case BG_RADIAL:
+ case BG_RADIAL: {
/* Do interpolation in a non-linear space to have a better visual result. */
col_high = pow(colorBackground.rgb, vec3(1.0 / 2.2));
col_low = pow(colorBackgroundGradient.rgb, vec3(1.0 / 2.2));
@@ -76,12 +76,14 @@ void main()
/* Dither to hide low precision buffer. (Could be improved) */
bg_col += dither();
break;
- case BG_CHECKER:
+ }
+ case BG_CHECKER: {
float size = sizeChecker * sizePixel;
ivec2 p = ivec2(floor(gl_FragCoord.xy / size));
bool check = mod(p.x, 2) == mod(p.y, 2);
bg_col = (check) ? colorCheckerPrimary.rgb : colorCheckerSecondary.rgb;
break;
+ }
case BG_MASK:
fragColor = vec4(vec3(1.0 - alpha), 0.0);
return;
diff --git a/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl b/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl
index 19d54a57479..ba0a4c0da81 100644
--- a/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl
+++ b/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl
@@ -358,6 +358,12 @@ void main()
line_end = vec2(0.0, 0.5);
break;
default:
+ /* Ensure values are assigned to, avoids undefined behaviour for
+ * divergent control-flow. This can occur if discard is called
+ * as discard is not treated as a return in Metal 2.2. So
+ * side-effects can still cause problems. */
+ line_start = vec2(0.0);
+ line_end = vec2(0.0);
break;
}
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_prepass_hair_vert.glsl b/source/blender/draw/engines/workbench/shaders/workbench_prepass_hair_vert.glsl
index 71cf08b7e8c..cfc94ef7c9a 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_prepass_hair_vert.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_prepass_hair_vert.glsl
@@ -8,8 +8,10 @@
/* From http://libnoise.sourceforge.net/noisegen/index.html */
float integer_noise(int n)
{
- n = (n >> 13) ^ n;
- int nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff;
+ /* Integer bit-shifts cause precision issues due to overflow
+ * in a number of workbench tests. Use uint instead. */
+ uint nn = (uint(n) >> 13u) ^ uint(n);
+ nn = (nn * (nn * nn * 60493u + 19990303u) + 1376312589u) & 0x7fffffffu;
return (float(nn) / 1073741824.0);
}
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl
index 4ff281ccd29..36059b6076f 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl
@@ -218,7 +218,15 @@ void main()
/* Manual depth test. TODO: remove. */
float depth = texelFetch(depthBuffer, ivec2(gl_FragCoord.xy), 0).r;
if (gl_FragCoord.z >= depth) {
+ /* Note: In the Metal API, prior to Metal 2.3, Discard is not an explicit return and can
+ * produce undefined behaviour. This is especially prominent with derivatives if control-flow
+ * divergence is present.
+ *
+ * Adding a return call eliminates undefined behaviour and a later out-of-bounds read causing
+ * a crash on AMD platforms.
+ * This behaviour can also affect OpenGL on certain devices. */
discard;
+ return;
}
vec3 Lscat;
@@ -268,6 +276,7 @@ void main()
/* Start is further away than the end.
* That means no volume is intersected. */
discard;
+ return;
}
fragColor = volume_integration(ls_ray_ori,
diff --git a/source/blender/draw/engines/workbench/shaders/workbench_world_light_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_world_light_lib.glsl
index 531ed461057..20053b8917c 100644
--- a/source/blender/draw/engines/workbench/shaders/workbench_world_light_lib.glsl
+++ b/source/blender/draw/engines/workbench/shaders/workbench_world_light_lib.glsl
@@ -64,22 +64,35 @@ vec3 get_world_lighting(vec3 base_color, float roughness, float metallic, vec3 N
if (world_data.use_specular) {
/* Prepare Specular computation. Eval 4 lights at once. */
vec3 R = -reflect(I, N);
+
+#ifdef GPU_METAL
+ /* Split vectors into arrays of floats. Partial vector references are unsupported by MSL. */
+ float spec_angle[4], spec_NL[4], wrap_NL[4];
+# define AS_VEC4(a) vec4(a[0], a[1], a[2], a[3])
+#else
vec4 spec_angle, spec_NL, wrap_NL;
- prep_specular(world_data.lights[0].direction.xyz, I, N, R, spec_NL.x, wrap_NL.x, spec_angle.x);
- prep_specular(world_data.lights[1].direction.xyz, I, N, R, spec_NL.y, wrap_NL.y, spec_angle.y);
- prep_specular(world_data.lights[2].direction.xyz, I, N, R, spec_NL.z, wrap_NL.z, spec_angle.z);
- prep_specular(world_data.lights[3].direction.xyz, I, N, R, spec_NL.w, wrap_NL.w, spec_angle.w);
+# define AS_VEC4(a) a
+#endif
+ prep_specular(
+ world_data.lights[0].direction.xyz, I, N, R, spec_NL[0], wrap_NL[0], spec_angle[0]);
+ prep_specular(
+ world_data.lights[1].direction.xyz, I, N, R, spec_NL[1], wrap_NL[1], spec_angle[1]);
+ prep_specular(
+ world_data.lights[2].direction.xyz, I, N, R, spec_NL[2], wrap_NL[2], spec_angle[2]);
+ prep_specular(
+ world_data.lights[3].direction.xyz, I, N, R, spec_NL[3], wrap_NL[3], spec_angle[3]);
vec4 gloss = vec4(1.0 - roughness);
/* Reduce gloss for smooth light. (simulate bigger light) */
gloss *= 1.0 - wrap;
vec4 shininess = exp2(10.0 * gloss + 1.0);
- vec4 spec_light = blinn_specular(shininess, spec_angle, spec_NL);
+ vec4 spec_light = blinn_specular(shininess, AS_VEC4(spec_angle), AS_VEC4(spec_NL));
/* Simulate Env. light. */
vec4 w = mix(wrap, vec4(1.0), roughness);
- vec4 spec_env = wrapped_lighting(wrap_NL, w);
+ vec4 spec_env = wrapped_lighting(AS_VEC4(wrap_NL), w);
+#undef AS_VEC4
spec_light = mix(spec_light, spec_env, wrap * wrap);
diff --git a/source/blender/draw/engines/workbench/workbench_engine.c b/source/blender/draw/engines/workbench/workbench_engine.c
index 85c8f9c420a..566fd30096d 100644
--- a/source/blender/draw/engines/workbench/workbench_engine.c
+++ b/source/blender/draw/engines/workbench/workbench_engine.c
@@ -425,7 +425,8 @@ void workbench_cache_populate(void *ved, Object *ob)
}
else if (ob->type == OB_CURVES) {
int color_type = workbench_color_type_get(wpd, ob, NULL, NULL, NULL);
- workbench_cache_hair_populate(wpd, ob, NULL, NULL, color_type, false, CURVES_MATERIAL_NR);
+ DRWShadingGroup *grp = workbench_material_hair_setup(wpd, ob, CURVES_MATERIAL_NR, color_type);
+ DRW_shgroup_curves_create_sub(ob, grp, NULL);
}
else if (ob->type == OB_VOLUME) {
if (wpd->shading.type != OB_WIRE) {
diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h
index 1bf67a4f315..712118e8282 100644
--- a/source/blender/draw/intern/DRW_render.h
+++ b/source/blender/draw/intern/DRW_render.h
@@ -28,6 +28,7 @@
#include "DNA_world_types.h"
#include "GPU_framebuffer.h"
+#include "GPU_material.h"
#include "GPU_primitive.h"
#include "GPU_shader.h"
#include "GPU_storage_buffer.h"
@@ -197,13 +198,6 @@ void DRW_texture_free(struct GPUTexture *tex);
/* Shaders */
-typedef void (*GPUMaterialEvalCallbackFn)(struct GPUMaterial *mat,
- int options,
- const char **vert_code,
- const char **geom_code,
- const char **frag_lib,
- const char **defines);
-
struct GPUShader *DRW_shader_create_ex(
const char *vert, const char *geom, const char *frag, const char *defines, const char *name);
struct GPUShader *DRW_shader_create_with_lib_ex(const char *vert,
@@ -242,38 +236,20 @@ struct GPUShader *DRW_shader_create_fullscreen_with_shaderlib_ex(const char *fra
#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,
- int options,
- bool deferred);
-struct GPUMaterial *DRW_shader_find_from_material(struct Material *ma,
- const void *engine_type,
- int options,
- bool deferred);
-struct GPUMaterial *DRW_shader_create_from_world(struct Scene *scene,
- struct World *wo,
- struct bNodeTree *ntree,
- const void *engine_type,
- int options,
- bool is_volume_shader,
- const char *vert,
- const char *geom,
- const char *frag_lib,
- const char *defines,
- bool deferred,
- GPUMaterialEvalCallbackFn callback);
-struct GPUMaterial *DRW_shader_create_from_material(struct Scene *scene,
- struct Material *ma,
- struct bNodeTree *ntree,
- const void *engine_type,
- int options,
- bool is_volume_shader,
- const char *vert,
- const char *geom,
- const char *frag_lib,
- const char *defines,
- bool deferred,
- GPUMaterialEvalCallbackFn callback);
+struct GPUMaterial *DRW_shader_from_world(struct World *wo,
+ struct bNodeTree *ntree,
+ const uint64_t shader_id,
+ const bool is_volume_shader,
+ bool deferred,
+ GPUCodegenCallbackFn callback,
+ void *thunk);
+struct GPUMaterial *DRW_shader_from_material(struct Material *ma,
+ struct bNodeTree *ntree,
+ const uint64_t shader_id,
+ const bool is_volume_shader,
+ bool deferred,
+ GPUCodegenCallbackFn callback,
+ void *thunk);
void DRW_shader_free(struct GPUShader *shader);
#define DRW_SHADER_FREE_SAFE(shader) \
do { \
diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc
index 717ea00fc0c..8a7b4fc9703 100644
--- a/source/blender/draw/intern/draw_cache_extract_mesh.cc
+++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc
@@ -844,6 +844,7 @@ static void mesh_buffer_cache_create_requested_subdiv(MeshBatchCache *cache,
EXTRACT_ADD_REQUESTED(vbo, edituv_stretch_angle);
EXTRACT_ADD_REQUESTED(ibo, lines_paint_mask);
EXTRACT_ADD_REQUESTED(ibo, lines_adjacency);
+ EXTRACT_ADD_REQUESTED(vbo, orco);
EXTRACT_ADD_REQUESTED(vbo, vcol);
EXTRACT_ADD_REQUESTED(vbo, weights);
EXTRACT_ADD_REQUESTED(vbo, sculpt_data);
diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c
index e6f34d3dd0d..ec33d2b5361 100644
--- a/source/blender/draw/intern/draw_cache_impl_mesh.c
+++ b/source/blender/draw/intern/draw_cache_impl_mesh.c
@@ -2121,7 +2121,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
MDEPS_ASSERT_MAP_INDEX(TRIS_PER_MAT_INDEX);
- /* Meh loose Scene const correctness here. */
const bool use_subsurf_fdots = scene ? BKE_modifiers_uses_subsurf_facedots(scene, ob) : false;
if (do_uvcage) {
diff --git a/source/blender/draw/intern/draw_cache_impl_subdivision.cc b/source/blender/draw/intern/draw_cache_impl_subdivision.cc
index 2653035a39f..c859a72b371 100644
--- a/source/blender/draw/intern/draw_cache_impl_subdivision.cc
+++ b/source/blender/draw/intern/draw_cache_impl_subdivision.cc
@@ -1162,8 +1162,8 @@ static void draw_subdiv_ubo_update_and_bind(const DRWSubdivCache *cache,
GPU_uniformbuf_update(cache->ubo, &storage);
- const int location = GPU_shader_get_uniform_block(shader, "shader_data");
- GPU_uniformbuf_bind(cache->ubo, location);
+ const int binding = GPU_shader_get_uniform_block_binding(shader, "shader_data");
+ GPU_uniformbuf_bind(cache->ubo, binding);
}
/** \} */
@@ -1942,7 +1942,9 @@ static bool draw_subdiv_create_requested_buffers(const Scene *scene,
return false;
}
- const bool optimal_display = (smd->flags & eSubsurfModifierFlag_ControlEdges);
+ /* Edges which do not come from coarse edges should not be drawn in edit mode, only in object
+ * mode when optimal display in turned off. */
+ const bool optimal_display = (smd->flags & eSubsurfModifierFlag_ControlEdges) || is_editmode;
draw_cache->bm = bm;
draw_cache->mesh = mesh_eval;
diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h
index 34b930ae9c8..d302140d9ac 100644
--- a/source/blender/draw/intern/draw_common.h
+++ b/source/blender/draw/intern/draw_common.h
@@ -53,12 +53,17 @@ struct DRWShadingGroup *DRW_shgroup_hair_create_sub(struct Object *object,
struct ModifierData *md,
struct DRWShadingGroup *shgrp,
struct GPUMaterial *gpu_material);
+
+struct DRWShadingGroup *DRW_shgroup_curves_create_sub(struct Object *object,
+ struct DRWShadingGroup *shgrp,
+ struct GPUMaterial *gpu_material);
/**
* \note Only valid after #DRW_hair_update().
*/
struct GPUVertBuf *DRW_hair_pos_buffer_get(struct Object *object,
struct ParticleSystem *psys,
struct ModifierData *md);
+struct GPUVertBuf *DRW_curves_pos_buffer_get(struct Object *object);
void DRW_hair_duplimat_get(struct Object *object,
struct ParticleSystem *psys,
struct ModifierData *md,
diff --git a/source/blender/draw/intern/draw_hair.c b/source/blender/draw/intern/draw_hair.c
index e06df334d23..aac6f7e58c5 100644
--- a/source/blender/draw/intern/draw_hair.c
+++ b/source/blender/draw/intern/draw_hair.c
@@ -164,17 +164,28 @@ static ParticleHairCache *drw_hair_particle_cache_get(Object *object,
int subdiv,
int thickness_res)
{
- bool update;
ParticleHairCache *cache;
- if (psys) {
- /* Old particle hair. */
- update = particles_ensure_procedural_data(
- object, psys, md, &cache, gpu_material, subdiv, thickness_res);
- }
- else {
- /* New curves object. */
- update = curves_ensure_procedural_data(object, &cache, gpu_material, subdiv, thickness_res);
+ bool update = particles_ensure_procedural_data(
+ object, psys, md, &cache, gpu_material, subdiv, thickness_res);
+
+ if (update) {
+ if (drw_hair_shader_type_get() == PART_REFINE_SHADER_COMPUTE) {
+ drw_hair_particle_cache_update_compute(cache, subdiv);
+ }
+ else {
+ drw_hair_particle_cache_update_transform_feedback(cache, subdiv);
+ }
}
+ return cache;
+}
+
+static ParticleHairCache *drw_curves_cache_get(Object *object,
+ GPUMaterial *gpu_material,
+ int subdiv,
+ int thickness_res)
+{
+ ParticleHairCache *cache;
+ bool update = curves_ensure_procedural_data(object, &cache, gpu_material, subdiv, thickness_res);
if (update) {
if (drw_hair_shader_type_get() == PART_REFINE_SHADER_COMPUTE) {
@@ -201,37 +212,44 @@ GPUVertBuf *DRW_hair_pos_buffer_get(Object *object, ParticleSystem *psys, Modifi
return cache->final[subdiv].proc_buf;
}
+GPUVertBuf *DRW_curves_pos_buffer_get(Object *object)
+{
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ Scene *scene = draw_ctx->scene;
+
+ int subdiv = scene->r.hair_subdiv;
+ int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2;
+
+ ParticleHairCache *cache = drw_curves_cache_get(object, NULL, subdiv, thickness_res);
+
+ return cache->final[subdiv].proc_buf;
+}
+
void DRW_hair_duplimat_get(Object *object,
- ParticleSystem *psys,
+ ParticleSystem *UNUSED(psys),
ModifierData *UNUSED(md),
float (*dupli_mat)[4])
{
Object *dupli_parent = DRW_object_get_dupli_parent(object);
DupliObject *dupli_object = DRW_object_get_dupli(object);
- if (psys) {
- if ((dupli_parent != NULL) && (dupli_object != NULL)) {
- if (dupli_object->type & OB_DUPLICOLLECTION) {
- unit_m4(dupli_mat);
- Collection *collection = dupli_parent->instance_collection;
- if (collection != NULL) {
- sub_v3_v3(dupli_mat[3], collection->instance_offset);
- }
- mul_m4_m4m4(dupli_mat, dupli_parent->obmat, dupli_mat);
- }
- else {
- copy_m4_m4(dupli_mat, dupli_object->ob->obmat);
- invert_m4(dupli_mat);
- mul_m4_m4m4(dupli_mat, object->obmat, dupli_mat);
+ if ((dupli_parent != NULL) && (dupli_object != NULL)) {
+ if (dupli_object->type & OB_DUPLICOLLECTION) {
+ unit_m4(dupli_mat);
+ Collection *collection = dupli_parent->instance_collection;
+ if (collection != NULL) {
+ sub_v3_v3(dupli_mat[3], collection->instance_offset);
}
+ mul_m4_m4m4(dupli_mat, dupli_parent->obmat, dupli_mat);
}
else {
- unit_m4(dupli_mat);
+ copy_m4_m4(dupli_mat, dupli_object->ob->obmat);
+ invert_m4(dupli_mat);
+ mul_m4_m4m4(dupli_mat, object->obmat, dupli_mat);
}
}
else {
- /* New curves object. */
- copy_m4_m4(dupli_mat, object->obmat);
+ unit_m4(dupli_mat);
}
}
@@ -280,27 +298,15 @@ DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object,
DRW_hair_duplimat_get(object, psys, md, dupli_mat);
/* Get hair shape parameters. */
- float hair_rad_shape, hair_rad_root, hair_rad_tip;
- bool hair_close_tip;
- if (psys) {
- /* Old particle hair. */
- ParticleSettings *part = psys->part;
- hair_rad_shape = part->shape;
- hair_rad_root = part->rad_root * part->rad_scale * 0.5f;
- hair_rad_tip = part->rad_tip * part->rad_scale * 0.5f;
- hair_close_tip = (part->shape_flag & PART_SHAPE_CLOSE_TIP) != 0;
- }
- else {
- /* TODO: implement for new curves object. */
- hair_rad_shape = 1.0f;
- hair_rad_root = 0.005f;
- hair_rad_tip = 0.0f;
- hair_close_tip = true;
- }
+ ParticleSettings *part = psys->part;
+ float hair_rad_shape = part->shape;
+ float hair_rad_root = part->rad_root * part->rad_scale * 0.5f;
+ float hair_rad_tip = part->rad_tip * part->rad_scale * 0.5f;
+ bool hair_close_tip = (part->shape_flag & PART_SHAPE_CLOSE_TIP) != 0;
DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", hair_cache->final[subdiv].proc_tex);
if (hair_cache->length_tex) {
- DRW_shgroup_uniform_texture(shgrp, "hairLen", hair_cache->length_tex);
+ DRW_shgroup_uniform_texture(shgrp, "l", hair_cache->length_tex);
}
DRW_shgroup_uniform_int(shgrp, "hairStrandsRes", &hair_cache->final[subdiv].strands_res, 1);
DRW_shgroup_uniform_int_copy(shgrp, "hairThicknessRes", thickness_res);
@@ -317,6 +323,71 @@ DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object,
return shgrp;
}
+DRWShadingGroup *DRW_shgroup_curves_create_sub(Object *object,
+ DRWShadingGroup *shgrp_parent,
+ GPUMaterial *gpu_material)
+{
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ Scene *scene = draw_ctx->scene;
+
+ int subdiv = scene->r.hair_subdiv;
+ int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2;
+
+ ParticleHairCache *curves_cache = drw_curves_cache_get(
+ object, gpu_material, subdiv, thickness_res);
+
+ DRWShadingGroup *shgrp = DRW_shgroup_create_sub(shgrp_parent);
+
+ /* TODO: optimize this. Only bind the ones GPUMaterial needs. */
+ for (int i = 0; i < curves_cache->num_uv_layers; i++) {
+ for (int n = 0; n < MAX_LAYER_NAME_CT && curves_cache->uv_layer_names[i][n][0] != '\0'; n++) {
+ DRW_shgroup_uniform_texture(
+ shgrp, curves_cache->uv_layer_names[i][n], curves_cache->uv_tex[i]);
+ }
+ }
+ for (int i = 0; i < curves_cache->num_col_layers; i++) {
+ for (int n = 0; n < MAX_LAYER_NAME_CT && curves_cache->col_layer_names[i][n][0] != '\0'; n++) {
+ DRW_shgroup_uniform_texture(
+ shgrp, curves_cache->col_layer_names[i][n], curves_cache->col_tex[i]);
+ }
+ }
+
+ /* Fix issue with certain driver not drawing anything if there is no texture bound to
+ * "ac", "au", "u" or "c". */
+ if (curves_cache->num_uv_layers == 0) {
+ DRW_shgroup_uniform_texture(shgrp, "u", g_dummy_texture);
+ DRW_shgroup_uniform_texture(shgrp, "au", g_dummy_texture);
+ }
+ if (curves_cache->num_col_layers == 0) {
+ DRW_shgroup_uniform_texture(shgrp, "c", g_dummy_texture);
+ DRW_shgroup_uniform_texture(shgrp, "ac", g_dummy_texture);
+ }
+
+ /* TODO: Generalize radius implementation for curves data type. */
+ float hair_rad_shape = 1.0f;
+ float hair_rad_root = 0.005f;
+ float hair_rad_tip = 0.0f;
+ bool hair_close_tip = true;
+
+ DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", curves_cache->final[subdiv].proc_tex);
+ if (curves_cache->length_tex) {
+ DRW_shgroup_uniform_texture(shgrp, "hairLen", curves_cache->length_tex);
+ }
+ DRW_shgroup_uniform_int(shgrp, "hairStrandsRes", &curves_cache->final[subdiv].strands_res, 1);
+ DRW_shgroup_uniform_int_copy(shgrp, "hairThicknessRes", thickness_res);
+ DRW_shgroup_uniform_float_copy(shgrp, "hairRadShape", hair_rad_shape);
+ DRW_shgroup_uniform_mat4_copy(shgrp, "hairDupliMatrix", object->obmat);
+ DRW_shgroup_uniform_float_copy(shgrp, "hairRadRoot", hair_rad_root);
+ DRW_shgroup_uniform_float_copy(shgrp, "hairRadTip", hair_rad_tip);
+ DRW_shgroup_uniform_bool_copy(shgrp, "hairCloseTip", hair_close_tip);
+ /* TODO(fclem): Until we have a better way to cull the curves and render with orco, bypass
+ * culling test. */
+ GPUBatch *geom = curves_cache->final[subdiv].proc_hairs[thickness_res - 1];
+ DRW_shgroup_call_no_cull(shgrp, geom, object);
+
+ return shgrp;
+}
+
void DRW_hair_update(void)
{
#ifndef USE_TRANSFORM_FEEDBACK
diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c
index ec3a5b3a7b1..88d05fcd928 100644
--- a/source/blender/draw/intern/draw_manager.c
+++ b/source/blender/draw/intern/draw_manager.c
@@ -1286,6 +1286,10 @@ void DRW_notify_view_update(const DRWUpdateContext *update_ctx)
const bool gpencil_engine_needed = drw_gpencil_engine_needed(depsgraph, v3d);
+ if (G.is_rendering) {
+ return;
+ }
+
/* XXX Really nasty locking. But else this could
* be executed by the material previews thread
* while rendering a viewport. */
diff --git a/source/blender/draw/intern/draw_manager_shader.c b/source/blender/draw/intern/draw_manager_shader.c
index 1936aa599ff..9f8a68f81f6 100644
--- a/source/blender/draw/intern/draw_manager_shader.c
+++ b/source/blender/draw/intern/draw_manager_shader.c
@@ -417,119 +417,81 @@ GPUShader *DRW_shader_create_fullscreen_with_shaderlib_ex(const char *frag,
return sh;
}
-GPUMaterial *DRW_shader_find_from_world(World *wo,
- const void *engine_type,
- const int options,
- bool deferred)
+GPUMaterial *DRW_shader_from_world(World *wo,
+ struct bNodeTree *ntree,
+ const uint64_t shader_id,
+ const bool is_volume_shader,
+ bool deferred,
+ GPUCodegenCallbackFn callback,
+ void *thunk)
{
- GPUMaterial *mat = GPU_material_from_nodetree_find(&wo->gpumaterial, engine_type, options);
- if (DRW_state_is_image_render() || !deferred) {
- if (mat != NULL && GPU_material_status(mat) == GPU_MAT_QUEUED) {
- /* XXX Hack : we return NULL so that the engine will call DRW_shader_create_from_XXX
- * with the shader code and we will resume the compilation from there. */
- return NULL;
- }
- }
- return mat;
-}
-
-GPUMaterial *DRW_shader_find_from_material(Material *ma,
- const void *engine_type,
- const int options,
- bool deferred)
-{
- GPUMaterial *mat = GPU_material_from_nodetree_find(&ma->gpumaterial, engine_type, options);
- if (DRW_state_is_image_render() || !deferred) {
- if (mat != NULL && GPU_material_status(mat) == GPU_MAT_QUEUED) {
- /* XXX Hack : we return NULL so that the engine will call DRW_shader_create_from_XXX
- * with the shader code and we will resume the compilation from there. */
- return NULL;
- }
- }
- return mat;
-}
-
-GPUMaterial *DRW_shader_create_from_world(struct Scene *scene,
- World *wo,
- struct bNodeTree *ntree,
- const void *engine_type,
- const int options,
- const bool is_volume_shader,
- const char *vert,
- const char *geom,
- const char *frag_lib,
- const char *defines,
- bool deferred,
- GPUMaterialEvalCallbackFn callback)
-{
- GPUMaterial *mat = NULL;
- if (DRW_state_is_image_render() || !deferred) {
- mat = GPU_material_from_nodetree_find(&wo->gpumaterial, engine_type, options);
- }
-
- if (mat == NULL) {
- scene = (Scene *)DEG_get_original_id(&DST.draw_ctx.scene->id);
- mat = GPU_material_from_nodetree(scene,
- NULL,
- ntree,
- &wo->gpumaterial,
- engine_type,
- options,
- is_volume_shader,
- vert,
- geom,
- frag_lib,
- defines,
- wo->id.name,
- callback);
+ Scene *scene = (Scene *)DEG_get_original_id(&DST.draw_ctx.scene->id);
+ GPUMaterial *mat = GPU_material_from_nodetree(scene,
+ NULL,
+ ntree,
+ &wo->gpumaterial,
+ wo->id.name,
+ shader_id,
+ is_volume_shader,
+ false,
+ callback,
+ thunk);
+ if (!DRW_state_is_image_render() && deferred && GPU_material_status(mat) == GPU_MAT_QUEUED) {
+ /* Shader has been already queued. */
+ return mat;
}
- if (GPU_material_status(mat) == GPU_MAT_QUEUED) {
+ if (GPU_material_status(mat) == GPU_MAT_CREATED) {
+ GPU_material_status_set(mat, GPU_MAT_QUEUED);
drw_deferred_shader_add(mat, deferred);
}
+ if (!deferred && GPU_material_status(mat) == GPU_MAT_QUEUED) {
+ /* Force compilation for shaders already queued. */
+ drw_deferred_shader_add(mat, false);
+ }
return mat;
}
-GPUMaterial *DRW_shader_create_from_material(struct Scene *scene,
- Material *ma,
- struct bNodeTree *ntree,
- const void *engine_type,
- const int options,
- const bool is_volume_shader,
- const char *vert,
- const char *geom,
- const char *frag_lib,
- const char *defines,
- bool deferred,
- GPUMaterialEvalCallbackFn callback)
+GPUMaterial *DRW_shader_from_material(Material *ma,
+ struct bNodeTree *ntree,
+ const uint64_t shader_id,
+ const bool is_volume_shader,
+ bool deferred,
+ GPUCodegenCallbackFn callback,
+ void *thunk)
{
- GPUMaterial *mat = NULL;
- if (DRW_state_is_image_render() || !deferred) {
- mat = GPU_material_from_nodetree_find(&ma->gpumaterial, engine_type, options);
+ Scene *scene = (Scene *)DEG_get_original_id(&DST.draw_ctx.scene->id);
+ GPUMaterial *mat = GPU_material_from_nodetree(scene,
+ ma,
+ ntree,
+ &ma->gpumaterial,
+ ma->id.name,
+ shader_id,
+ is_volume_shader,
+ false,
+ callback,
+ thunk);
+
+ if (DRW_state_is_image_render()) {
+ /* Do not deferred if doing render. */
+ deferred = false;
}
- if (mat == NULL) {
- scene = (Scene *)DEG_get_original_id(&DST.draw_ctx.scene->id);
- mat = GPU_material_from_nodetree(scene,
- ma,
- ntree,
- &ma->gpumaterial,
- engine_type,
- options,
- is_volume_shader,
- vert,
- geom,
- frag_lib,
- defines,
- ma->id.name,
- callback);
+ if (deferred && GPU_material_status(mat) == GPU_MAT_QUEUED) {
+ /* Shader has been already queued. */
+ return mat;
}
- if (GPU_material_status(mat) == GPU_MAT_QUEUED) {
+ if (GPU_material_status(mat) == GPU_MAT_CREATED) {
+ GPU_material_status_set(mat, GPU_MAT_QUEUED);
drw_deferred_shader_add(mat, deferred);
}
+ if (!deferred && GPU_material_status(mat) == GPU_MAT_QUEUED) {
+ /* Force compilation for shaders already queued. */
+ drw_deferred_shader_add(mat, false);
+ }
return mat;
}
@@ -552,15 +514,15 @@ void DRW_shader_free(GPUShader *shader)
* contains the needed libraries for this shader.
* \{ */
-/* 32 because we use a 32bit bitmap. */
-#define MAX_LIB 32
+/* 64 because we use a 64bit bitmap. */
+#define MAX_LIB 64
#define MAX_LIB_NAME 64
#define MAX_LIB_DEPS 8
struct DRWShaderLibrary {
const char *libs[MAX_LIB];
char libs_name[MAX_LIB][MAX_LIB_NAME];
- uint32_t libs_deps[MAX_LIB];
+ uint64_t libs_deps[MAX_LIB];
};
DRWShaderLibrary *DRW_shader_library_create(void)
@@ -589,23 +551,27 @@ static int drw_shader_library_search(const DRWShaderLibrary *lib, const char *na
}
/* Return bitmap of dependencies. */
-static uint32_t drw_shader_dependencies_get(const DRWShaderLibrary *lib, const char *lib_code)
+static uint64_t drw_shader_dependencies_get(const DRWShaderLibrary *lib,
+ const char *pragma_str,
+ const char *lib_code,
+ const char *UNUSED(lib_name))
{
/* Search dependencies. */
- uint32_t deps = 0;
+ uint pragma_len = strlen(pragma_str);
+ uint64_t deps = 0;
const char *haystack = lib_code;
- while ((haystack = strstr(haystack, "BLENDER_REQUIRE("))) {
- haystack += 16;
+ while ((haystack = strstr(haystack, pragma_str))) {
+ haystack += pragma_len;
int dep = drw_shader_library_search(lib, haystack);
if (dep == -1) {
- char dbg_name[33];
+ char dbg_name[MAX_NAME];
int i = 0;
while ((*haystack != ')') && (i < (sizeof(dbg_name) - 2))) {
dbg_name[i] = *haystack;
haystack++;
i++;
}
- dbg_name[i + 1] = '\0';
+ dbg_name[i] = '\0';
CLOG_INFO(&LOG,
0,
@@ -614,7 +580,7 @@ static uint32_t drw_shader_dependencies_get(const DRWShaderLibrary *lib, const c
dbg_name);
}
else {
- deps |= 1u << (uint32_t)dep;
+ deps |= 1llu << ((uint64_t)dep);
}
}
return deps;
@@ -633,7 +599,8 @@ void DRW_shader_library_add_file(DRWShaderLibrary *lib, const char *lib_code, co
if (index > -1) {
lib->libs[index] = lib_code;
BLI_strncpy(lib->libs_name[index], lib_name, MAX_LIB_NAME);
- lib->libs_deps[index] = drw_shader_dependencies_get(lib, lib_code);
+ lib->libs_deps[index] = drw_shader_dependencies_get(
+ lib, "BLENDER_REQUIRE(", lib_code, lib_name);
}
else {
printf("Error: Too many libraries. Cannot add %s.\n", lib_name);
@@ -643,21 +610,20 @@ void DRW_shader_library_add_file(DRWShaderLibrary *lib, const char *lib_code, co
char *DRW_shader_library_create_shader_string(const DRWShaderLibrary *lib, const char *shader_code)
{
- uint32_t deps = drw_shader_dependencies_get(lib, shader_code);
+ uint64_t deps = drw_shader_dependencies_get(lib, "BLENDER_REQUIRE(", shader_code, "shader code");
DynStr *ds = BLI_dynstr_new();
/* Add all dependencies recursively. */
for (int i = MAX_LIB - 1; i > -1; i--) {
- if (lib->libs[i] && (deps & (1u << (uint32_t)i))) {
+ if (lib->libs[i] && (deps & (1llu << (uint64_t)i))) {
deps |= lib->libs_deps[i];
}
}
/* Concatenate all needed libs into one string. */
- for (int i = 0; i < MAX_LIB; i++) {
- if (deps & 1u) {
+ for (int i = 0; i < MAX_LIB && deps != 0llu; i++, deps >>= 1llu) {
+ if (deps & 1llu) {
BLI_dynstr_append(ds, lib->libs[i]);
}
- deps = deps >> 1;
}
BLI_dynstr_append(ds, shader_code);
diff --git a/source/blender/draw/intern/draw_shader_shared.h b/source/blender/draw/intern/draw_shader_shared.h
index 5fc76bc25e6..58875c0496a 100644
--- a/source/blender/draw/intern/draw_shader_shared.h
+++ b/source/blender/draw/intern/draw_shader_shared.h
@@ -36,16 +36,19 @@ struct ViewInfos {
};
BLI_STATIC_ASSERT_ALIGN(ViewInfos, 16)
+/* Do not override old definitions if the shader uses this header but not shader info. */
+#ifdef USE_GPU_SHADER_CREATE_INFO
/* TODO(@fclem): Mass rename. */
-#define ViewProjectionMatrix drw_view.persmat
-#define ViewProjectionMatrixInverse drw_view.persinv
-#define ViewMatrix drw_view.viewmat
-#define ViewMatrixInverse drw_view.viewinv
-#define ProjectionMatrix drw_view.winmat
-#define ProjectionMatrixInverse drw_view.wininv
-#define clipPlanes drw_view.clip_planes
-#define ViewVecs drw_view.viewvecs
-#define CameraTexCoFactors drw_view.viewcamtexcofac
+# define ViewProjectionMatrix drw_view.persmat
+# define ViewProjectionMatrixInverse drw_view.persinv
+# define ViewMatrix drw_view.viewmat
+# define ViewMatrixInverse drw_view.viewinv
+# define ProjectionMatrix drw_view.winmat
+# define ProjectionMatrixInverse drw_view.wininv
+# define clipPlanes drw_view.clip_planes
+# define ViewVecs drw_view.viewvecs
+# define CameraTexCoFactors drw_view.viewcamtexcofac
+#endif
struct ObjectMatrices {
float4x4 drw_modelMatrix;
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc
index d4c801ab066..ed1a0ccd178 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc
@@ -7,6 +7,8 @@
#include "extract_mesh.h"
+#include "draw_subdivision.h"
+
namespace blender::draw {
/* ---------------------------------------------------------------------- */
@@ -77,12 +79,77 @@ static void extract_orco_iter_poly_mesh(const MeshRenderData *mr,
}
}
+static void extract_orco_init_subdiv(const DRWSubdivCache *subdiv_cache,
+ const MeshRenderData *mr,
+ struct MeshBatchCache *UNUSED(cache),
+ void *buffer,
+ void *UNUSED(data))
+{
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ /* FIXME(fclem): We use the last component as a way to differentiate from generic vertex
+ * attributes. This is a substantial waste of video-ram and should be done another way.
+ * Unfortunately, at the time of writing, I did not found any other "non disruptive"
+ * alternative. */
+ GPU_vertformat_attr_add(&format, "orco", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ }
+
+ GPUVertBuf *dst_buffer = static_cast<GPUVertBuf *>(buffer);
+ GPU_vertbuf_init_build_on_device(dst_buffer, &format, subdiv_cache->num_subdiv_loops);
+
+ GPUVertBuf *coarse_vbo = GPU_vertbuf_calloc();
+ /* Dynamic as we upload and interpolate layers one at a time. */
+ GPU_vertbuf_init_with_format_ex(coarse_vbo, &format, GPU_USAGE_DYNAMIC);
+ GPU_vertbuf_data_alloc(coarse_vbo, mr->loop_len);
+
+ float(*coarse_vbo_data)[4] = static_cast<float(*)[4]>(GPU_vertbuf_get_data(coarse_vbo));
+
+ CustomData *cd_vdata = &mr->me->vdata;
+ float(*orco)[3] = static_cast<float(*)[3]>(CustomData_get_layer(cd_vdata, CD_ORCO));
+
+ if (mr->extract_type == MR_EXTRACT_MESH) {
+ const MLoop *mloop = mr->mloop;
+ const MPoly *mp = mr->mpoly;
+
+ int ml_index = 0;
+ for (int i = 0; i < mr->poly_len; i++, mp++) {
+ const MLoop *ml = &mloop[mp->loopstart];
+
+ for (int j = 0; j < mp->totloop; j++, ml++, ml_index++) {
+ float *loop_orco = coarse_vbo_data[ml_index];
+ copy_v3_v3(loop_orco, orco[ml->v]);
+ loop_orco[3] = 0.0; /* Tag as not a generic attribute. */
+ }
+ }
+ }
+ else {
+ BMIter iter;
+ BMFace *f;
+ BM_ITER_MESH (f, &iter, mr->bm, BM_FACES_OF_MESH) {
+ BMLoop *l_iter;
+ BMLoop *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ const int l_index = BM_elem_index_get(l_iter);
+ float *loop_orco = coarse_vbo_data[l_index];
+ copy_v3_v3(loop_orco, orco[BM_elem_index_get(l_iter->v)]);
+ loop_orco[3] = 0.0; /* Tag as not a generic attribute. */
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+ }
+
+ draw_subdiv_interp_custom_data(subdiv_cache, coarse_vbo, dst_buffer, 4, 0, false);
+
+ GPU_vertbuf_discard(coarse_vbo);
+}
+
constexpr MeshExtract create_extractor_orco()
{
MeshExtract extractor = {nullptr};
extractor.init = extract_orco_init;
extractor.iter_poly_bm = extract_orco_iter_poly_bm;
extractor.iter_poly_mesh = extract_orco_iter_poly_mesh;
+ extractor.init_subdiv = extract_orco_init_subdiv;
extractor.data_type = MR_DATA_NONE;
extractor.data_size = sizeof(MeshExtract_Orco_Data);
extractor.use_threading = true;
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc
index 225d1676151..25f78d68914 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc
@@ -317,7 +317,7 @@ static void extract_tan_init_subdiv(const DRWSubdivCache *subdiv_cache,
GPU_vertbuf_tag_dirty(coarse_vbo);
/* Include stride in offset. */
const int dst_offset = (int)subdiv_cache->num_subdiv_loops * 4 * pack_layer_index++;
- draw_subdiv_interp_custom_data(subdiv_cache, coarse_vbo, dst_buffer, 4, dst_offset, true);
+ draw_subdiv_interp_custom_data(subdiv_cache, coarse_vbo, dst_buffer, 4, dst_offset, false);
}
CustomData_free(&loop_data, mr->loop_len);
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc
index 97e5386b14d..d52226a4c90 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc
@@ -143,7 +143,7 @@ static void extract_vcol_init(const MeshRenderData *mr,
CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata;
CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata;
- Mesh me_query = {0};
+ Mesh me_query = blender::dna::shallow_zero_initialize();
BKE_id_attribute_copy_domains_temp(
ID_ME, cd_vdata, nullptr, cd_ldata, nullptr, nullptr, &me_query.id);
@@ -256,7 +256,7 @@ static void extract_vcol_init_subdiv(const DRWSubdivCache *subdiv_cache,
const CustomData *cd_ldata = extract_bmesh ? &coarse_mesh->edit_mesh->bm->ldata :
&coarse_mesh->ldata;
- Mesh me_query = *coarse_mesh;
+ Mesh me_query = blender::dna::shallow_copy(*coarse_mesh);
BKE_id_attribute_copy_domains_temp(
ID_ME, cd_vdata, nullptr, cd_ldata, nullptr, nullptr, &me_query.id);
diff --git a/source/blender/draw/intern/shaders/common_attribute_lib.glsl b/source/blender/draw/intern/shaders/common_attribute_lib.glsl
new file mode 100644
index 00000000000..99db2929a13
--- /dev/null
+++ b/source/blender/draw/intern/shaders/common_attribute_lib.glsl
@@ -0,0 +1,21 @@
+
+/* Prototype of functions to implement to load attributes data.
+ * Implementation changes based on object data type. */
+
+vec3 attr_load_orco(vec4 orco);
+vec4 attr_load_tangent(vec4 tangent);
+vec3 attr_load_uv(vec3 uv);
+vec4 attr_load_color(vec4 color);
+vec4 attr_load_vec4(vec4 attr);
+vec3 attr_load_vec3(vec3 attr);
+vec2 attr_load_vec2(vec2 attr);
+float attr_load_float(float attr);
+
+vec3 attr_load_orco(samplerBuffer orco);
+vec4 attr_load_tangent(samplerBuffer tangent);
+vec3 attr_load_uv(samplerBuffer uv);
+vec4 attr_load_color(samplerBuffer color);
+vec4 attr_load_vec4(samplerBuffer attr);
+vec3 attr_load_vec3(samplerBuffer attr);
+vec2 attr_load_vec2(samplerBuffer attr);
+float attr_load_float(samplerBuffer attr);
diff --git a/source/blender/draw/intern/shaders/common_hair_lib.glsl b/source/blender/draw/intern/shaders/common_hair_lib.glsl
index 6a8f1132e1b..ff52b483d77 100644
--- a/source/blender/draw/intern/shaders/common_hair_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_hair_lib.glsl
@@ -211,6 +211,11 @@ void hair_get_pos_tan_binor_time(bool is_persp,
wpos += wbinor * thick_time * scale;
}
+ else {
+ /* Note: Ensures 'hairThickTime' is initialised -
+ * avoids undefined behaviour on certain macOS configurations. */
+ thick_time = 0.0;
+ }
}
float hair_get_customdata_float(const samplerBuffer cd_buf)
diff --git a/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl b/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl
index 371d43827b9..2eccae5bceb 100644
--- a/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl
+++ b/source/blender/draw/intern/shaders/common_hair_refine_vert.glsl
@@ -18,7 +18,7 @@ void main(void)
vec4 weights = hair_get_weights_cardinal(interp_time);
finalColor = hair_interp_data(data0, data1, data2, data3, weights);
-#ifdef TF_WORKAROUND
+#if defined(TF_WORKAROUND)
int id = gl_VertexID - idOffset;
gl_Position.x = ((float(id % targetWidth) + 0.5) / float(targetWidth)) * 2.0 - 1.0;
gl_Position.y = ((float(id / targetWidth) + 0.5) / float(targetHeight)) * 2.0 - 1.0;
@@ -26,5 +26,10 @@ void main(void)
gl_Position.w = 1.0;
gl_PointSize = 1.0;
+#else
+# ifdef GPU_METAL
+ /* Metal still expects an output position for TF shaders. */
+ gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
+# endif
#endif
}
diff --git a/source/blender/draw/intern/shaders/common_math_lib.glsl b/source/blender/draw/intern/shaders/common_math_lib.glsl
index 4d0ffaeb40f..1ac26c91b93 100644
--- a/source/blender/draw/intern/shaders/common_math_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_math_lib.glsl
@@ -1,4 +1,8 @@
+/* WORKAROUND: to guard against double include in EEVEE. */
+#ifndef COMMON_MATH_LIB_GLSL
+#define COMMON_MATH_LIB_GLSL
+
/* ---------------------------------------------------------------------- */
/** \name Common Math Utilities
* \{ */
@@ -276,3 +280,5 @@ vec3 hue_gradient(float t)
vec3 p = abs(fract(t + vec3(1.0, 2.0 / 3.0, 1.0 / 3.0)) * 6.0 - 3.0);
return (clamp(p - 1.0, 0.0, 1.0));
}
+
+#endif /* COMMON_MATH_LIB_GLSL */
diff --git a/source/blender/draw/intern/shaders/common_smaa_lib.glsl b/source/blender/draw/intern/shaders/common_smaa_lib.glsl
index 73f65fb0799..dbc4c998b34 100644
--- a/source/blender/draw/intern/shaders/common_smaa_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_smaa_lib.glsl
@@ -569,7 +569,7 @@ SamplerState PointSampler
# define SMAAGather(tex, coord) tex.Gather(LinearSampler, coord, 0)
# endif
#endif
-#if defined(SMAA_GLSL_3) || defined(SMAA_GLSL_4)
+#if defined(SMAA_GLSL_3) || defined(SMAA_GLSL_4) || defined(GPU_METAL)
# define SMAATexture2D(tex) sampler2D tex
# define SMAATexturePass2D(tex) tex
# define SMAASampleLevelZero(tex, coord) textureLod(tex, coord, 0.0)
@@ -641,14 +641,14 @@ float2 SMAACalculatePredicatedThreshold(float2 texcoord,
*/
void SMAAMovc(bool2 cond, inout float2 variable, float2 value)
{
- SMAA_FLATTEN if (cond.x) variable.x = value.x;
- SMAA_FLATTEN if (cond.y) variable.y = value.y;
+ /* Use select function (select(genType A, genType B, genBType cond)). */
+ variable = select(variable, value, cond);
}
void SMAAMovc(bool4 cond, inout float4 variable, float4 value)
{
- SMAAMovc(cond.xy, variable.xy, value.xy);
- SMAAMovc(cond.zw, variable.zw, value.zw);
+ /* Use select function (select(genType A, genType B, genBType cond)). */
+ variable = select(variable, value, cond);
}
#if SMAA_INCLUDE_VS
@@ -1281,7 +1281,15 @@ float4 SMAABlendingWeightCalculationPS(float2 texcoord,
// Fix corners:
coords.y = texcoord.y;
- SMAADetectHorizontalCornerPattern(SMAATexturePass2D(edgesTex), weights.rg, coords.xyzy, d);
+
+# ifdef GPU_METAL
+ /* Partial vector references are unsupported in MSL. */
+ vec2 _weights = weights.rg;
+ SMAADetectHorizontalCornerPattern(SMAATexturePass2D(edgesTex), _weights, coords.xyzy, d);
+ weights.rg = _weights;
+# else
+ SMAADetectHorizontalCornerPattern(SMAATexturePass2D(edgesTex), weights.rg, coords.xyzy, d);
+# endif
# if !defined(SMAA_DISABLE_DIAG_DETECTION)
}
@@ -1324,7 +1332,15 @@ float4 SMAABlendingWeightCalculationPS(float2 texcoord,
// Fix corners:
coords.x = texcoord.x;
+
+# ifdef GPU_METAL
+ /* Partial vector references are unsupported in MSL. */
+ vec2 _weights = weights.ba;
+ SMAADetectVerticalCornerPattern(SMAATexturePass2D(edgesTex), _weights, coords.xyxz, d);
+ weights.ba = _weights;
+# else
SMAADetectVerticalCornerPattern(SMAATexturePass2D(edgesTex), weights.ba, coords.xyxz, d);
+# endif
}
return weights;
diff --git a/source/blender/draw/intern/shaders/common_view_lib.glsl b/source/blender/draw/intern/shaders/common_view_lib.glsl
index a2b8cb4bbd6..4086162a530 100644
--- a/source/blender/draw/intern/shaders/common_view_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_view_lib.glsl
@@ -1,5 +1,10 @@
+
+/* WORKAROUND: to guard against double include in EEVEE. */
+#ifndef COMMON_VIEW_LIB_GLSL
+#define COMMON_VIEW_LIB_GLSL
+
/* Temporary until we fully make the switch. */
-#ifndef USE_GPU_SHADER_CREATE_INFO
+#if !defined(USE_GPU_SHADER_CREATE_INFO)
# define DRW_RESOURCE_CHUNK_LEN 512
@@ -37,7 +42,10 @@ layout(std140) uniform viewBlock
#define cameraForward ViewMatrixInverse[2].xyz
#define cameraPos ViewMatrixInverse[3].xyz
-#define cameraVec(P) ((ProjectionMatrix[3][3] == 0.0) ? normalize(cameraPos - P) : cameraForward)
+vec3 cameraVec(vec3 P)
+{
+ return ((ProjectionMatrix[3][3] == 0.0) ? normalize(cameraPos - P) : cameraForward);
+}
#define viewCameraVec(vP) ((ProjectionMatrix[3][3] == 0.0) ? normalize(-vP) : vec3(0.0, 0.0, 1.0))
#ifdef world_clip_planes_calc_clip_distance
@@ -92,14 +100,14 @@ vec4 pack_line_data(vec2 frag_co, vec2 edge_start, vec2 edge_pos)
}
/* Temporary until we fully make the switch. */
-#ifndef DRW_SHADER_SHARED_H
+#ifndef USE_GPU_SHADER_CREATE_INFO
uniform int drw_resourceChunk;
-#endif /* DRW_SHADER_SHARED_H */
+#endif /* USE_GPU_SHADER_CREATE_INFO */
#ifdef GPU_VERTEX_SHADER
/* Temporary until we fully make the switch. */
-# ifndef DRW_SHADER_SHARED_H
+# ifndef USE_GPU_SHADER_CREATE_INFO
/* clang-format off */
# if defined(IN_PLACE_INSTANCES) || defined(INSTANCED_ATTR) || defined(DRW_LEGACY_MODEL_MATRIX) || defined(GPU_DEPRECATED_AMD_DRIVER)
@@ -121,7 +129,10 @@ uniform int drw_ResourceID;
/* Use this to declare and pass the value if
* the fragment shader uses the resource_id. */
-# ifdef USE_GEOMETRY_SHADER
+# if defined(EEVEE_GENERATED_INTERFACE)
+# define RESOURCE_ID_VARYING
+# define PASS_RESOURCE_ID resourceIDFrag = resource_id;
+# elif defined(USE_GEOMETRY_SHADER)
# define RESOURCE_ID_VARYING flat out int resourceIDGeom;
# define PASS_RESOURCE_ID resourceIDGeom = resource_id;
# else
@@ -129,12 +140,12 @@ uniform int drw_ResourceID;
# define PASS_RESOURCE_ID resourceIDFrag = resource_id;
# endif
-# endif /* DRW_SHADER_SHARED_H */
+# endif /* USE_GPU_SHADER_CREATE_INFO */
#endif /* GPU_VERTEX_SHADER */
/* Temporary until we fully make the switch. */
-#ifdef DRW_SHADER_SHARED_H
+#ifdef USE_GPU_SHADER_CREATE_INFO
/* TODO(fclem): Rename PASS_RESOURCE_ID to DRW_RESOURCE_ID_VARYING_SET */
# if defined(UNIFORM_RESOURCE_ID)
# define resource_id drw_ResourceID
@@ -159,16 +170,23 @@ uniform int drw_ResourceID;
/* If used in a fragment / geometry shader, we pass
* resource_id as varying. */
# ifdef GPU_GEOMETRY_SHADER
-# define RESOURCE_ID_VARYING \
- flat out int resourceIDFrag; \
- flat in int resourceIDGeom[];
+/* TODO(fclem): Remove. This is getting ridiculous. */
+# if !defined(EEVEE_GENERATED_INTERFACE)
+# define RESOURCE_ID_VARYING \
+ flat out int resourceIDFrag; \
+ flat in int resourceIDGeom[];
+# else
+# define RESOURCE_ID_VARYING
+# endif
# define resource_id resourceIDGeom
# define PASS_RESOURCE_ID resourceIDFrag = resource_id[0];
# endif
-# ifdef GPU_FRAGMENT_SHADER
+# if defined(GPU_FRAGMENT_SHADER)
+# if !defined(EEVEE_GENERATED_INTERFACE)
flat in int resourceIDFrag;
+# endif
# define resource_id resourceIDFrag
# endif
#endif
@@ -185,7 +203,9 @@ struct ObjectMatrices {
mat4 drw_modelMatrix;
mat4 drw_modelMatrixInverse;
};
+# endif /* DRW_SHADER_SHARED_H */
+# ifndef USE_GPU_SHADER_CREATE_INFO
layout(std140) uniform modelBlock
{
ObjectMatrices drw_matrices[DRW_RESOURCE_CHUNK_LEN];
@@ -193,24 +213,24 @@ layout(std140) uniform modelBlock
# define ModelMatrix (drw_matrices[resource_id].drw_modelMatrix)
# define ModelMatrixInverse (drw_matrices[resource_id].drw_modelMatrixInverse)
-# endif /* DRW_SHADER_SHARED_H */
+# endif /* USE_GPU_SHADER_CREATE_INFO */
#else /* GPU_INTEL */
/* Temporary until we fully make the switch. */
-# ifndef DRW_SHADER_SHARED_H
+# ifndef USE_GPU_SHADER_CREATE_INFO
/* Intel GPU seems to suffer performance impact when the model matrix is in UBO storage.
* So for now we just force using the legacy path. */
/* Note that this is also a workaround of a problem on osx (amd or nvidia)
* and older amd driver on windows. */
uniform mat4 ModelMatrix;
uniform mat4 ModelMatrixInverse;
-# endif /* DRW_SHADER_SHARED_H */
+# endif /* USE_GPU_SHADER_CREATE_INFO */
#endif
/* Temporary until we fully make the switch. */
-#ifndef DRW_SHADER_SHARED_H
+#ifndef USE_GPU_SHADER_CREATE_INFO
# define resource_handle (drw_resourceChunk * DRW_RESOURCE_CHUNK_LEN + resource_id)
#endif
@@ -337,3 +357,5 @@ vec3 get_view_vector_from_screen_uv(vec2 uv)
return vec3(0.0, 0.0, 1.0);
}
}
+
+#endif /* COMMON_VIEW_LIB_GLSL */
diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c
index ead189c0389..eb7f8e8ad83 100644
--- a/source/blender/editors/animation/anim_channels_defines.c
+++ b/source/blender/editors/animation/anim_channels_defines.c
@@ -2796,7 +2796,7 @@ static bAnimChannelType ACF_DSSPK = {
acf_dsspk_setting_ptr, /* pointer for setting */
};
-/* Hair Expander ------------------------------------------- */
+/* Curves Expander ------------------------------------------- */
/* TODO: just get this from RNA? */
static int acf_dscurves_icon(bAnimListElem *UNUSED(ale))
@@ -2859,7 +2859,7 @@ static void *acf_dscurves_setting_ptr(bAnimListElem *ale,
}
/** Curves expander type define. */
-static bAnimChannelType ACF_DSHAIR = {
+static bAnimChannelType ACF_DSCURVES = {
"Curves Expander", /* type name */
ACHANNEL_ROLE_EXPANDER, /* role */
@@ -4129,7 +4129,7 @@ static void ANIM_init_channel_typeinfo_data(void)
animchannelTypeInfo[type++] = &ACF_DSSPK; /* Speaker Channel */
animchannelTypeInfo[type++] = &ACF_DSGPENCIL; /* GreasePencil Channel */
animchannelTypeInfo[type++] = &ACF_DSMCLIP; /* MovieClip Channel */
- animchannelTypeInfo[type++] = &ACF_DSHAIR; /* Hair Channel */
+ animchannelTypeInfo[type++] = &ACF_DSCURVES; /* Curves Channel */
animchannelTypeInfo[type++] = &ACF_DSPOINTCLOUD; /* PointCloud Channel */
animchannelTypeInfo[type++] = &ACF_DSVOLUME; /* Volume Channel */
animchannelTypeInfo[type++] = &ACF_DSSIMULATION; /* Simulation Channel */
diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c
index d7bbc0eab2b..1a3ab100768 100644
--- a/source/blender/editors/animation/anim_markers.c
+++ b/source/blender/editors/animation/anim_markers.c
@@ -1465,6 +1465,83 @@ static void MARKER_OT_select_all(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Select Left/Right of Frame
+ * \{ */
+
+typedef enum eMarkers_LeftRightSelect_Mode {
+ MARKERS_LRSEL_LEFT = 0,
+ MARKERS_LRSEL_RIGHT,
+} eMarkers_LeftRightSelect_Mode;
+
+static const EnumPropertyItem prop_markers_select_leftright_modes[] = {
+ {MARKERS_LRSEL_LEFT, "LEFT", 0, "Before Current Frame", ""},
+ {MARKERS_LRSEL_RIGHT, "RIGHT", 0, "After Current Frame", ""},
+ {0, NULL, 0, NULL, NULL},
+};
+
+static void ED_markers_select_leftright(bAnimContext *ac,
+ const eMarkers_LeftRightSelect_Mode mode,
+ const bool extend)
+{
+ ListBase *markers = ac->markers;
+ Scene *scene = ac->scene;
+
+ if (markers == NULL) {
+ return;
+ }
+
+ if (!extend) {
+ deselect_markers(markers);
+ }
+
+ LISTBASE_FOREACH (TimeMarker *, marker, markers) {
+ if ((mode == MARKERS_LRSEL_LEFT && marker->frame <= CFRA) ||
+ (mode == MARKERS_LRSEL_RIGHT && marker->frame >= CFRA)) {
+ marker->flag |= SELECT;
+ }
+ }
+}
+
+static int ed_marker_select_leftright_exec(bContext *C, wmOperator *op)
+{
+ const eMarkers_LeftRightSelect_Mode mode = RNA_enum_get(op->ptr, "mode");
+ const bool extend = RNA_boolean_get(op->ptr, "extend");
+
+ bAnimContext ac;
+ if (ANIM_animdata_get_context(C, &ac) == 0) {
+ return OPERATOR_CANCELLED;
+ }
+
+ ED_markers_select_leftright(&ac, mode, extend);
+
+ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+static void MARKER_OT_select_leftright(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Select Markers Before/After Current Frame";
+ ot->description = "Select markers on and left/right of the current frame";
+ ot->idname = "MARKER_OT_select_leftright";
+
+ /* api callbacks */
+ ot->exec = ed_marker_select_leftright_exec;
+ ot->poll = ed_markers_poll_markers_exist;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* rna storage */
+ RNA_def_enum(
+ ot->srna, "mode", prop_markers_select_leftright_modes, MARKERS_LRSEL_LEFT, "mode", "Mode");
+ RNA_def_boolean(ot->srna, "extend", false, "extend", "Extend");
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Remove Marker
*
* Remove selected time-markers.
@@ -1735,6 +1812,7 @@ void ED_operatortypes_marker(void)
WM_operatortype_append(MARKER_OT_select);
WM_operatortype_append(MARKER_OT_select_box);
WM_operatortype_append(MARKER_OT_select_all);
+ WM_operatortype_append(MARKER_OT_select_leftright);
WM_operatortype_append(MARKER_OT_delete);
WM_operatortype_append(MARKER_OT_rename);
WM_operatortype_append(MARKER_OT_make_links_scene);
diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c
index 8d079641e9f..5b742ddf272 100644
--- a/source/blender/editors/animation/keyframing.c
+++ b/source/blender/editors/animation/keyframing.c
@@ -14,6 +14,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
+#include "BLI_dynstr.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
@@ -1109,9 +1110,62 @@ static float *visualkey_get_values(
/* ------------------------- Insert Key API ------------------------- */
+/* Check indices that were intended to be remapped and report any failed remaps. */
+static void get_keyframe_values_create_reports(ReportList *reports,
+ PointerRNA ptr,
+ PropertyRNA *prop,
+ const int index,
+ const int count,
+ const bool force_all,
+ const BLI_bitmap *successful_remaps)
+{
+
+ DynStr *ds_failed_indices = BLI_dynstr_new();
+
+ int total_failed = 0;
+ for (int i = 0; i < count; i++) {
+ const bool cur_index_evaluated = ELEM(index, i, -1) || force_all;
+ if (!cur_index_evaluated) {
+ /* values[i] was never intended to be remapped. */
+ continue;
+ }
+
+ if (BLI_BITMAP_TEST_BOOL(successful_remaps, i)) {
+ /* values[i] succesfully remapped. */
+ continue;
+ }
+
+ total_failed++;
+ /* Report that values[i] were intended to be remapped but failed remapping process. */
+ BLI_dynstr_appendf(ds_failed_indices, "%d, ", i);
+ }
+
+ if (total_failed == 0) {
+ BLI_dynstr_free(ds_failed_indices);
+ return;
+ }
+
+ char *str_failed_indices = BLI_dynstr_get_cstring(ds_failed_indices);
+ BLI_dynstr_free(ds_failed_indices);
+
+ BKE_reportf(reports,
+ RPT_WARNING,
+ "Could not insert %i keyframe(s) due to zero NLA influence, base value, or value "
+ "remapping failed: %s.%s for indices [%s]",
+ total_failed,
+ ptr.owner_id->name,
+ RNA_property_ui_name(prop),
+ str_failed_indices);
+
+ MEM_freeN(str_failed_indices);
+}
+
/**
* Retrieve current property values to keyframe,
* possibly applying NLA correction when necessary.
+ *
+ * \param r_successful_remaps: Enables bits for indices which are both intended to be remapped and
+ * were successfully remapped. Bitmap allocated so it must be freed afterward.
*/
static float *get_keyframe_values(ReportList *reports,
PointerRNA ptr,
@@ -1121,8 +1175,10 @@ static float *get_keyframe_values(ReportList *reports,
eInsertKeyFlags flag,
float *buffer,
int buffer_size,
+ const struct AnimationEvalContext *anim_eval_context,
int *r_count,
- bool *r_force_all)
+ bool *r_force_all,
+ BLI_bitmap **r_successful_remaps)
{
float *values;
@@ -1138,17 +1194,25 @@ static float *get_keyframe_values(ReportList *reports,
values = setting_get_rna_values(&ptr, prop, buffer, buffer_size, r_count);
}
- /* adjust the value for NLA factors */
- if (!BKE_animsys_nla_remap_keyframe_values(
- nla_context, &ptr, prop, values, *r_count, index, r_force_all)) {
- BKE_report(
- reports, RPT_ERROR, "Could not insert keyframe due to zero NLA influence or base value");
+ *r_successful_remaps = BLI_BITMAP_NEW(*r_count, __func__);
- if (values != buffer) {
- MEM_freeN(values);
- }
- return NULL;
- }
+ /* adjust the value for NLA factors */
+ BKE_animsys_nla_remap_keyframe_values(nla_context,
+ &ptr,
+ prop,
+ values,
+ *r_count,
+ index,
+ anim_eval_context,
+ r_force_all,
+ *r_successful_remaps);
+ get_keyframe_values_create_reports(reports,
+ ptr,
+ prop,
+ index,
+ *r_count,
+ r_force_all ? *r_force_all : false,
+ *r_successful_remaps);
return values;
}
@@ -1284,6 +1348,7 @@ bool insert_keyframe_direct(ReportList *reports,
int value_count;
int index = fcu->array_index;
+ BLI_bitmap *successful_remaps = NULL;
float *values = get_keyframe_values(reports,
ptr,
prop,
@@ -1292,13 +1357,10 @@ bool insert_keyframe_direct(ReportList *reports,
flag,
value_buffer,
RNA_MAX_ARRAY_LENGTH,
+ anim_eval_context,
&value_count,
- NULL);
-
- if (values == NULL) {
- /* This happens if NLA rejects this insertion. */
- return false;
- }
+ NULL,
+ &successful_remaps);
if (index >= 0 && index < value_count) {
curval = values[index];
@@ -1308,6 +1370,14 @@ bool insert_keyframe_direct(ReportList *reports,
MEM_freeN(values);
}
+ const bool curval_valid = BLI_BITMAP_TEST_BOOL(successful_remaps, index);
+ MEM_freeN(successful_remaps);
+
+ /* This happens if NLA rejects this insertion. */
+ if (!curval_valid) {
+ return false;
+ }
+
return insert_keyframe_value(reports, &ptr, prop, fcu, anim_eval_context, curval, keytype, flag);
}
@@ -1461,6 +1531,7 @@ int insert_keyframe(Main *bmain,
int value_count;
bool force_all;
+ BLI_bitmap *successful_remaps = NULL;
float *values = get_keyframe_values(reports,
ptr,
prop,
@@ -1469,77 +1540,72 @@ int insert_keyframe(Main *bmain,
flag,
value_buffer,
RNA_MAX_ARRAY_LENGTH,
+ anim_eval_context,
&value_count,
- &force_all);
+ &force_all,
+ &successful_remaps);
- if (values != NULL) {
- /* Key the entire array. */
- if (array_index == -1 || force_all) {
- /* In force mode, if any of the curves succeeds, drop the replace mode and restart. */
- if (force_all && (flag & (INSERTKEY_REPLACE | INSERTKEY_AVAILABLE)) != 0) {
- int exclude = -1;
+ /* Key the entire array. */
+ if (array_index == -1 || force_all) {
+ /* In force mode, if any of the curves succeeds, drop the replace mode and restart. */
+ if (force_all && (flag & (INSERTKEY_REPLACE | INSERTKEY_AVAILABLE)) != 0) {
+ int exclude = -1;
- for (array_index = 0; array_index < value_count; array_index++) {
- if (insert_keyframe_fcurve_value(bmain,
- reports,
- &ptr,
- prop,
- act,
- group,
- rna_path,
- array_index,
- &remapped_context,
- values[array_index],
- keytype,
- flag)) {
- ret++;
- exclude = array_index;
- break;
- }
+ for (array_index = 0; array_index < value_count; array_index++) {
+ if (!BLI_BITMAP_TEST_BOOL(successful_remaps, array_index)) {
+ continue;
}
- if (exclude != -1) {
- flag &= ~(INSERTKEY_REPLACE | INSERTKEY_AVAILABLE);
-
- for (array_index = 0; array_index < value_count; array_index++) {
- if (array_index != exclude) {
- ret += insert_keyframe_fcurve_value(bmain,
- reports,
- &ptr,
- prop,
- act,
- group,
- rna_path,
- array_index,
- &remapped_context,
- values[array_index],
- keytype,
- flag);
- }
- }
+ if (insert_keyframe_fcurve_value(bmain,
+ reports,
+ &ptr,
+ prop,
+ act,
+ group,
+ rna_path,
+ array_index,
+ &remapped_context,
+ values[array_index],
+ keytype,
+ flag)) {
+ ret++;
+ exclude = array_index;
+ break;
}
}
- /* Simply insert all channels. */
- else {
+
+ if (exclude != -1) {
+ flag &= ~(INSERTKEY_REPLACE | INSERTKEY_AVAILABLE);
+
for (array_index = 0; array_index < value_count; array_index++) {
- ret += insert_keyframe_fcurve_value(bmain,
- reports,
- &ptr,
- prop,
- act,
- group,
- rna_path,
- array_index,
- &remapped_context,
- values[array_index],
- keytype,
- flag);
+ if (!BLI_BITMAP_TEST_BOOL(successful_remaps, array_index)) {
+ continue;
+ }
+
+ if (array_index != exclude) {
+ ret += insert_keyframe_fcurve_value(bmain,
+ reports,
+ &ptr,
+ prop,
+ act,
+ group,
+ rna_path,
+ array_index,
+ &remapped_context,
+ values[array_index],
+ keytype,
+ flag);
+ }
}
}
}
- /* Key a single index. */
+ /* Simply insert all channels. */
else {
- if (array_index >= 0 && array_index < value_count) {
+ for (array_index = 0; array_index < value_count; array_index++) {
+ if (!BLI_BITMAP_TEST_BOOL(successful_remaps, array_index)) {
+ continue;
+ }
+
ret += insert_keyframe_fcurve_value(bmain,
reports,
&ptr,
@@ -1554,12 +1620,31 @@ int insert_keyframe(Main *bmain,
flag);
}
}
-
- if (values != value_buffer) {
- MEM_freeN(values);
+ }
+ /* Key a single index. */
+ else {
+ if (array_index >= 0 && array_index < value_count &&
+ BLI_BITMAP_TEST_BOOL(successful_remaps, array_index)) {
+ ret += insert_keyframe_fcurve_value(bmain,
+ reports,
+ &ptr,
+ prop,
+ act,
+ group,
+ rna_path,
+ array_index,
+ &remapped_context,
+ values[array_index],
+ keytype,
+ flag);
}
}
+ if (values != value_buffer) {
+ MEM_freeN(values);
+ }
+
+ MEM_freeN(successful_remaps);
BKE_animsys_free_nla_keyframing_context_cache(&tmp_nla_cache);
if (ret) {
@@ -1979,23 +2064,57 @@ static int insert_key_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UN
{
Scene *scene = CTX_data_scene(C);
- /* if prompting or no active Keying Set, show the menu */
- if ((scene->active_keyingset == 0) || RNA_boolean_get(op->ptr, "always_prompt")) {
- uiPopupMenu *pup;
- uiLayout *layout;
-
- /* call the menu, which will call this operator again, hence the canceled */
- pup = UI_popup_menu_begin(C, WM_operatortype_name(op->type, op->ptr), ICON_NONE);
- layout = UI_popup_menu_layout(pup);
- uiItemsEnumO(layout, "ANIM_OT_keyframe_insert_menu", "type");
- UI_popup_menu_end(C, pup);
+ /* When there is an active keying set and no request to prompt, keyframe immediately. */
+ if ((scene->active_keyingset != 0) && !RNA_boolean_get(op->ptr, "always_prompt")) {
+ /* Just call the exec() on the active keying-set. */
+ RNA_enum_set(op->ptr, "type", 0);
+ return op->type->exec(C, op);
+ }
+
+ /* Show a menu listing all keying-sets, the enum is expanded here to make use of the
+ * operator that accesses the keying-set by name. This is important for the ability
+ * to assign shortcuts to arbitrarily named keying sets. See T89560.
+ * These menu items perform the key-frame insertion (not this operator)
+ * hence the #OPERATOR_INTERFACE return. */
+ uiPopupMenu *pup = UI_popup_menu_begin(C, WM_operatortype_name(op->type, op->ptr), ICON_NONE);
+ uiLayout *layout = UI_popup_menu_layout(pup);
+
+ /* Even though `ANIM_OT_keyframe_insert_menu` can show a menu in one line,
+ * prefer `ANIM_OT_keyframe_insert_by_name` so users can bind keys to specific
+ * keying sets by name in the key-map instead of the index which isn't stable. */
+ PropertyRNA *prop = RNA_struct_find_property(op->ptr, "type");
+ const EnumPropertyItem *item_array = NULL;
+ int totitem;
+ bool free;
+
+ RNA_property_enum_items_gettexted(C, op->ptr, prop, &item_array, &totitem, &free);
+
+ for (int i = 0; i < totitem; i++) {
+ const EnumPropertyItem *item = &item_array[i];
+ if (item->identifier[0] != '\0') {
+ uiItemStringO(layout,
+ item->name,
+ item->icon,
+ "ANIM_OT_keyframe_insert_by_name",
+ "type",
+ item->identifier);
+ }
+ else {
+ /* This enum shouldn't contain headings, assert there are none.
+ * NOTE: If in the future the enum includes them, additional layout code can be
+ * added to show them - although that doesn't seem likely. */
+ BLI_assert(item->name == NULL);
+ uiItemS(layout);
+ }
+ }
- return OPERATOR_INTERFACE;
+ if (free) {
+ MEM_freeN((void *)item_array);
}
- /* just call the exec() on the active keyingset */
- RNA_enum_set(op->ptr, "type", 0);
- return op->type->exec(C, op);
+ UI_popup_menu_end(C, pup);
+
+ return OPERATOR_INTERFACE;
}
void ANIM_OT_keyframe_insert_menu(wmOperatorType *ot)
diff --git a/source/blender/editors/curves/intern/curves_ops.cc b/source/blender/editors/curves/intern/curves_ops.cc
index 3a4dcd4f5f6..7d07c211542 100644
--- a/source/blender/editors/curves/intern/curves_ops.cc
+++ b/source/blender/editors/curves/intern/curves_ops.cc
@@ -121,147 +121,171 @@ static float4 compute_mface_weights_for_position(const Mesh &mesh,
return mface_weights;
}
-static int curves_convert_to_particle_system_exec(bContext *C, wmOperator *UNUSED(op))
+static void try_convert_single_object(Object &curves_ob,
+ Main &bmain,
+ Scene &scene,
+ bool *r_could_not_convert_some_curves)
{
- Main *bmain = CTX_data_main(C);
- Scene *scene = CTX_data_scene(C);
-
- CTX_DATA_BEGIN (C, Object *, curves_ob, selected_objects) {
- if (curves_ob->type != OB_CURVES) {
- continue;
- }
- Curves &curves_id = *static_cast<Curves *>(curves_ob->data);
- CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
- if (curves_id.surface == nullptr) {
- continue;
- }
- Object &surface_ob = *curves_id.surface;
- if (surface_ob.type != OB_MESH) {
- continue;
+ if (curves_ob.type != OB_CURVES) {
+ return;
+ }
+ Curves &curves_id = *static_cast<Curves *>(curves_ob.data);
+ CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
+ if (curves_id.surface == nullptr) {
+ return;
+ }
+ Object &surface_ob = *curves_id.surface;
+ if (surface_ob.type != OB_MESH) {
+ return;
+ }
+ Mesh &surface_me = *static_cast<Mesh *>(surface_ob.data);
+
+ const Span<float3> positions_cu = curves.positions();
+ const VArray<int> looptri_indices = curves.surface_triangle_indices();
+ const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&surface_me),
+ BKE_mesh_runtime_looptri_len(&surface_me)};
+
+ /* Find indices of curves that can be transferred to the old hair system. */
+ Vector<int> curves_indices_to_transfer;
+ for (const int curve_i : curves.curves_range()) {
+ const int looptri_i = looptri_indices[curve_i];
+ if (looptri_i >= 0 && looptri_i < looptris.size()) {
+ curves_indices_to_transfer.append(curve_i);
}
- Mesh &surface_me = *static_cast<Mesh *>(surface_ob.data);
-
- const Span<float3> positions_cu = curves.positions();
- const VArray<int> looptri_indices = curves.surface_triangle_indices();
- const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&surface_me),
- BKE_mesh_runtime_looptri_len(&surface_me)};
-
- /* Find indices of curves that can be transferred to the old hair system. */
- Vector<int> curves_indices_to_transfer;
- for (const int curve_i : curves.curves_range()) {
- const int looptri_i = looptri_indices[curve_i];
- if (looptri_i >= 0 && looptri_i < looptris.size()) {
- curves_indices_to_transfer.append(curve_i);
- }
+ else {
+ *r_could_not_convert_some_curves = true;
}
+ }
- const int hairs_num = curves_indices_to_transfer.size();
- if (hairs_num == 0) {
- continue;
- }
+ const int hairs_num = curves_indices_to_transfer.size();
+ if (hairs_num == 0) {
+ return;
+ }
- ParticleSystem *particle_system = nullptr;
- LISTBASE_FOREACH (ParticleSystem *, psys, &surface_ob.particlesystem) {
- if (STREQ(psys->name, curves_ob->id.name + 2)) {
- particle_system = psys;
- break;
- }
- }
- if (particle_system == nullptr) {
- ParticleSystemModifierData &psmd = *reinterpret_cast<ParticleSystemModifierData *>(
- object_add_particle_system(bmain, scene, &surface_ob, curves_ob->id.name + 2));
- particle_system = psmd.psys;
+ ParticleSystem *particle_system = nullptr;
+ LISTBASE_FOREACH (ParticleSystem *, psys, &surface_ob.particlesystem) {
+ if (STREQ(psys->name, curves_ob.id.name + 2)) {
+ particle_system = psys;
+ break;
}
+ }
+ if (particle_system == nullptr) {
+ ParticleSystemModifierData &psmd = *reinterpret_cast<ParticleSystemModifierData *>(
+ object_add_particle_system(&bmain, &scene, &surface_ob, curves_ob.id.name + 2));
+ particle_system = psmd.psys;
+ particle_system->part->draw_step = 3;
+ }
+
+ ParticleSettings &settings = *particle_system->part;
- ParticleSettings &settings = *particle_system->part;
+ psys_free_particles(particle_system);
+ settings.type = PART_HAIR;
+ settings.totpart = 0;
+ psys_changed_type(&surface_ob, particle_system);
- psys_free_particles(particle_system);
- settings.type = PART_HAIR;
- settings.totpart = 0;
- psys_changed_type(&surface_ob, particle_system);
+ MutableSpan<ParticleData> particles{
+ static_cast<ParticleData *>(MEM_calloc_arrayN(hairs_num, sizeof(ParticleData), __func__)),
+ hairs_num};
- MutableSpan<ParticleData> particles{
- static_cast<ParticleData *>(MEM_calloc_arrayN(hairs_num, sizeof(ParticleData), __func__)),
- hairs_num};
+ /* The old hair system still uses #MFace, so make sure those are available on the mesh. */
+ BKE_mesh_tessface_calc(&surface_me);
- /* The old hair system still uses #MFace, so make sure those are available on the mesh. */
- BKE_mesh_tessface_calc(&surface_me);
+ /* Prepare utility data structure to map hair roots to mfaces. */
+ const Span<int> mface_to_poly_map{
+ static_cast<int *>(CustomData_get_layer(&surface_me.fdata, CD_ORIGINDEX)),
+ surface_me.totface};
+ Array<Vector<int>> poly_to_mface_map(surface_me.totpoly);
+ for (const int mface_i : mface_to_poly_map.index_range()) {
+ const int poly_i = mface_to_poly_map[mface_i];
+ poly_to_mface_map[poly_i].append(mface_i);
+ }
- /* Prepare utility data structure to map hair roots to mfaces. */
- const Span<int> mface_to_poly_map{
- static_cast<int *>(CustomData_get_layer(&surface_me.fdata, CD_ORIGINDEX)),
- surface_me.totface};
- Array<Vector<int>> poly_to_mface_map(surface_me.totpoly);
- for (const int mface_i : mface_to_poly_map.index_range()) {
- const int poly_i = mface_to_poly_map[mface_i];
- poly_to_mface_map[poly_i].append(mface_i);
+ /* Prepare transformation matrices. */
+ const float4x4 curves_to_world_mat = curves_ob.obmat;
+ const float4x4 surface_to_world_mat = surface_ob.obmat;
+ const float4x4 world_to_surface_mat = surface_to_world_mat.inverted();
+ const float4x4 curves_to_surface_mat = world_to_surface_mat * curves_to_world_mat;
+
+ for (const int new_hair_i : curves_indices_to_transfer.index_range()) {
+ const int curve_i = curves_indices_to_transfer[new_hair_i];
+ const IndexRange points = curves.points_for_curve(curve_i);
+
+ const int looptri_i = looptri_indices[curve_i];
+ const MLoopTri &looptri = looptris[looptri_i];
+ const int poly_i = looptri.poly;
+
+ const float3 &root_pos_cu = positions_cu[points.first()];
+ const float3 root_pos_su = curves_to_surface_mat * root_pos_cu;
+
+ const int mface_i = find_mface_for_root_position(
+ surface_me, poly_to_mface_map[poly_i], root_pos_su);
+ const MFace &mface = surface_me.mface[mface_i];
+
+ const float4 mface_weights = compute_mface_weights_for_position(
+ surface_me, mface, root_pos_su);
+
+ ParticleData &particle = particles[new_hair_i];
+ const int num_keys = points.size();
+ MutableSpan<HairKey> hair_keys{
+ static_cast<HairKey *>(MEM_calloc_arrayN(num_keys, sizeof(HairKey), __func__)), num_keys};
+
+ particle.hair = hair_keys.data();
+ particle.totkey = hair_keys.size();
+ copy_v4_v4(particle.fuv, mface_weights);
+ particle.num = mface_i;
+ /* Not sure if there is a better way to initialize this. */
+ particle.num_dmcache = DMCACHE_NOTFOUND;
+
+ float4x4 hair_to_surface_mat;
+ psys_mat_hair_to_object(
+ &surface_ob, &surface_me, PART_FROM_FACE, &particle, hair_to_surface_mat.values);
+ /* In theory, #psys_mat_hair_to_object should handle this, but it doesn't right now. */
+ copy_v3_v3(hair_to_surface_mat.values[3], root_pos_su);
+ const float4x4 surface_to_hair_mat = hair_to_surface_mat.inverted();
+
+ for (const int key_i : hair_keys.index_range()) {
+ const float3 &key_pos_cu = positions_cu[points[key_i]];
+ const float3 key_pos_su = curves_to_surface_mat * key_pos_cu;
+ const float3 key_pos_ha = surface_to_hair_mat * key_pos_su;
+
+ HairKey &key = hair_keys[key_i];
+ copy_v3_v3(key.co, key_pos_ha);
+ key.time = 100.0f * key_i / (float)(hair_keys.size() - 1);
}
+ }
- /* Prepare transformation matrices. */
- const float4x4 curves_to_world_mat = curves_ob->obmat;
- const float4x4 surface_to_world_mat = surface_ob.obmat;
- const float4x4 world_to_surface_mat = surface_to_world_mat.inverted();
- const float4x4 curves_to_surface_mat = world_to_surface_mat * curves_to_world_mat;
+ particle_system->particles = particles.data();
+ particle_system->totpart = particles.size();
+ particle_system->flag |= PSYS_EDITED;
+ particle_system->recalc |= ID_RECALC_PSYS_RESET;
- for (const int new_hair_i : curves_indices_to_transfer.index_range()) {
- const int curve_i = curves_indices_to_transfer[new_hair_i];
- const IndexRange points = curves.points_for_curve(curve_i);
-
- const int looptri_i = looptri_indices[curve_i];
- const MLoopTri &looptri = looptris[looptri_i];
- const int poly_i = looptri.poly;
-
- const float3 &root_pos_cu = positions_cu[points.first()];
- const float3 root_pos_su = curves_to_surface_mat * root_pos_cu;
-
- const int mface_i = find_mface_for_root_position(
- surface_me, poly_to_mface_map[poly_i], root_pos_su);
- const MFace &mface = surface_me.mface[mface_i];
-
- const float4 mface_weights = compute_mface_weights_for_position(
- surface_me, mface, root_pos_su);
-
- ParticleData &particle = particles[new_hair_i];
- const int num_keys = points.size();
- MutableSpan<HairKey> hair_keys{
- static_cast<HairKey *>(MEM_calloc_arrayN(num_keys, sizeof(HairKey), __func__)),
- num_keys};
-
- particle.hair = hair_keys.data();
- particle.totkey = hair_keys.size();
- copy_v4_v4(particle.fuv, mface_weights);
- particle.num = mface_i;
- /* Not sure if there is a better way to initialize this. */
- particle.num_dmcache = DMCACHE_NOTFOUND;
-
- float4x4 hair_to_surface_mat;
- psys_mat_hair_to_object(
- &surface_ob, &surface_me, PART_FROM_FACE, &particle, hair_to_surface_mat.values);
- /* In theory, #psys_mat_hair_to_object should handle this, but it doesn't right now. */
- copy_v3_v3(hair_to_surface_mat.values[3], root_pos_su);
- const float4x4 surface_to_hair_mat = hair_to_surface_mat.inverted();
-
- for (const int key_i : hair_keys.index_range()) {
- const float3 &key_pos_cu = positions_cu[points[key_i]];
- const float3 key_pos_su = curves_to_surface_mat * key_pos_cu;
- const float3 key_pos_ha = surface_to_hair_mat * key_pos_su;
-
- HairKey &key = hair_keys[key_i];
- copy_v3_v3(key.co, key_pos_ha);
- key.time = 100.0f * key_i / (float)(hair_keys.size() - 1);
- }
- }
+ DEG_id_tag_update(&surface_ob.id, ID_RECALC_GEOMETRY);
+ DEG_id_tag_update(&settings.id, ID_RECALC_COPY_ON_WRITE);
+}
+
+static int curves_convert_to_particle_system_exec(bContext *C, wmOperator *op)
+{
+ Main &bmain = *CTX_data_main(C);
+ Scene &scene = *CTX_data_scene(C);
- particle_system->particles = particles.data();
- particle_system->totpart = particles.size();
- particle_system->flag |= PSYS_EDITED;
- particle_system->recalc |= ID_RECALC_PSYS_RESET;
+ bool could_not_convert_some_curves = false;
- DEG_id_tag_update(&surface_ob.id, ID_RECALC_GEOMETRY);
- DEG_id_tag_update(&settings.id, ID_RECALC_COPY_ON_WRITE);
+ Object &active_object = *CTX_data_active_object(C);
+ try_convert_single_object(active_object, bmain, scene, &could_not_convert_some_curves);
+
+ CTX_DATA_BEGIN (C, Object *, curves_ob, selected_objects) {
+ if (curves_ob != &active_object) {
+ try_convert_single_object(*curves_ob, bmain, scene, &could_not_convert_some_curves);
+ }
}
CTX_DATA_END;
+ if (could_not_convert_some_curves) {
+ BKE_report(op->reports,
+ RPT_INFO,
+ "Some curves could not be converted because they were not attached to the surface");
+ }
+
WM_main_add_notifier(NC_OBJECT | ND_PARTICLE | NA_EDITED, nullptr);
return OPERATOR_FINISHED;
@@ -473,7 +497,7 @@ static void CURVES_OT_snap_curves_to_surface(wmOperatorType *ot)
"Deform",
"Re-attach curves to a deformed surface using the existing attachment information. This "
"only works when the topology of the surface mesh has not changed"},
- {0, NULL, 0, NULL, NULL},
+ {0, nullptr, 0, nullptr, nullptr},
};
RNA_def_enum(ot->srna,
diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt
index 8fca0c46c82..9e7e00d5656 100644
--- a/source/blender/editors/datafiles/CMakeLists.txt
+++ b/source/blender/editors/datafiles/CMakeLists.txt
@@ -767,9 +767,9 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES
ops.armature.extrude_cursor
ops.armature.extrude_move
ops.curve.draw
- ops.curve.pen
ops.curve.extrude_cursor
ops.curve.extrude_move
+ ops.curve.pen
ops.curve.radius
ops.curve.vertex_random
ops.curves.sculpt_add
@@ -777,6 +777,7 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES
ops.curves.sculpt_cut
ops.curves.sculpt_delete
ops.curves.sculpt_grow_shrink
+ ops.curves.sculpt_snake_hook
ops.generic.cursor
ops.generic.select
ops.generic.select_box
@@ -847,8 +848,8 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES
ops.sculpt.border_hide
ops.sculpt.border_mask
ops.sculpt.box_trim
- ops.sculpt.color_filter
ops.sculpt.cloth_filter
+ ops.sculpt.color_filter
ops.sculpt.face_set_edit
ops.sculpt.lasso_face_set
ops.sculpt.lasso_mask
diff --git a/source/blender/editors/geometry/geometry_attributes.cc b/source/blender/editors/geometry/geometry_attributes.cc
index 75094b46c8b..467b8efa622 100644
--- a/source/blender/editors/geometry/geometry_attributes.cc
+++ b/source/blender/editors/geometry/geometry_attributes.cc
@@ -380,7 +380,7 @@ void GEOMETRY_OT_color_attribute_add(wmOperatorType *ot)
prop = RNA_def_string(ot->srna, "name", "Color", MAX_NAME, "Name", "Name of color attribute");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- static EnumPropertyItem domains[3] = {{ATTR_DOMAIN_POINT, "POINT", 0, "Point", ""},
+ static EnumPropertyItem domains[3] = {{ATTR_DOMAIN_POINT, "POINT", 0, "Vertex", ""},
{ATTR_DOMAIN_CORNER, "CORNER", 0, "Face Corner", ""},
{0, nullptr, 0, nullptr, nullptr}};
diff --git a/source/blender/editors/gizmo_library/gizmo_draw_utils.c b/source/blender/editors/gizmo_library/gizmo_draw_utils.c
index 8d2aec18451..c6303c197e7 100644
--- a/source/blender/editors/gizmo_library/gizmo_draw_utils.c
+++ b/source/blender/editors/gizmo_library/gizmo_draw_utils.c
@@ -82,9 +82,35 @@ void wm_gizmo_vec_draw(
const float color[4], const float (*verts)[3], uint vert_count, uint pos, uint primitive_type)
{
immUniformColor4fv(color);
- immBegin(primitive_type, vert_count);
- for (int i = 0; i < vert_count; i++) {
- immVertex3fv(pos, verts[i]);
+
+ if (primitive_type == GPU_PRIM_LINE_LOOP) {
+ /* Line loop alternative for Metal/Vulkan. */
+ immBegin(GPU_PRIM_LINES, vert_count * 2);
+ immVertex3fv(pos, verts[0]);
+ for (int i = 1; i < vert_count; i++) {
+ immVertex3fv(pos, verts[i]);
+ immVertex3fv(pos, verts[i]);
+ }
+ immVertex3fv(pos, verts[0]);
+ immEnd();
+ }
+ else if (primitive_type == GPU_PRIM_TRI_FAN) {
+ /* Note(Metal): Tri-fan alternative for Metal. Triangle List is more efficient for small
+ * primitive counts. */
+ int tri_count = vert_count - 2;
+ immBegin(GPU_PRIM_TRIS, tri_count * 3);
+ for (int i = 0; i < tri_count; i++) {
+ immVertex3fv(pos, verts[0]);
+ immVertex3fv(pos, verts[i + 1]);
+ immVertex3fv(pos, verts[i + 2]);
+ }
+ immEnd();
+ }
+ else {
+ immBegin(primitive_type, vert_count);
+ for (int i = 0; i < vert_count; i++) {
+ immVertex3fv(pos, verts[i]);
+ }
+ immEnd();
}
- immEnd();
}
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 5e20cc73f1a..b326d6d1859 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c
@@ -74,20 +74,21 @@ static void button2d_geom_draw_backdrop(const wmGizmo *gz,
GPU_viewport_size_get_f(viewport);
GPUVertFormat *format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ /* Note(Metal): Prefer 3D coordinate for 2D rendering when using 3D shader. */
+ uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
/* TODO: other draw styles. */
if (color[3] == 1.0 && fill_alpha == 1.0 && select == false) {
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4fv(color);
- imm_draw_circle_fill_2d(pos, 0, 0, 1.0f, CIRCLE_RESOLUTION);
+ imm_draw_circle_fill_3d(pos, 0.0f, 0.0f, 1.0f, CIRCLE_RESOLUTION);
immUnbindProgram();
immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR);
immUniform2fv("viewportSize", &viewport[2]);
immUniform1f("lineWidth", gz->line_width * U.pixelsize);
immUniformColor4fv(color);
- imm_draw_circle_wire_2d(pos, 0, 0, 1.0f, CIRCLE_RESOLUTION);
+ imm_draw_circle_wire_3d(pos, 0.0f, 0.0f, 1.0f, CIRCLE_RESOLUTION);
immUnbindProgram();
}
else {
@@ -96,7 +97,7 @@ static void button2d_geom_draw_backdrop(const wmGizmo *gz,
const float fill_color[4] = {UNPACK3(color), fill_alpha * color[3]};
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4fv(fill_color);
- imm_draw_circle_fill_2d(pos, 0, 0, 1.0f, CIRCLE_RESOLUTION);
+ imm_draw_circle_fill_3d(pos, 0.0f, 0.0f, 1.0f, CIRCLE_RESOLUTION);
immUnbindProgram();
}
@@ -106,7 +107,7 @@ static void button2d_geom_draw_backdrop(const wmGizmo *gz,
immUniform2fv("viewportSize", &viewport[2]);
immUniform1f("lineWidth", gz->line_width * U.pixelsize);
immUniformColor4fv(color);
- imm_draw_circle_wire_2d(pos, 0, 0, 1.0f, CIRCLE_RESOLUTION);
+ imm_draw_circle_wire_3d(pos, 0.0f, 0.0f, 1.0f, CIRCLE_RESOLUTION);
immUnbindProgram();
}
}
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 4c54aa10c33..e4cb6d149f5 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c
@@ -99,7 +99,8 @@ static void cage2d_draw_box_corners(const rctf *r,
const float color[3],
const float line_width)
{
- uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ /* Note(Metal): Prefer using 3D coordinates with 3D shader, even if rendering 2D gizmo's. */
+ uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR);
immUniformColor3fv(color);
@@ -112,25 +113,25 @@ static void cage2d_draw_box_corners(const rctf *r,
immBegin(GPU_PRIM_LINES, 16);
- immVertex2f(pos, r->xmin, r->ymin + margin[1]);
- immVertex2f(pos, r->xmin, r->ymin);
- immVertex2f(pos, r->xmin, r->ymin);
- immVertex2f(pos, r->xmin + margin[0], r->ymin);
+ immVertex3f(pos, r->xmin, r->ymin + margin[1], 0.0f);
+ immVertex3f(pos, r->xmin, r->ymin, 0.0f);
+ immVertex3f(pos, r->xmin, r->ymin, 0.0f);
+ immVertex3f(pos, r->xmin + margin[0], r->ymin, 0.0f);
- immVertex2f(pos, r->xmax, r->ymin + margin[1]);
- immVertex2f(pos, r->xmax, r->ymin);
- immVertex2f(pos, r->xmax, r->ymin);
- immVertex2f(pos, r->xmax - margin[0], r->ymin);
+ immVertex3f(pos, r->xmax, r->ymin + margin[1], 0.0f);
+ immVertex3f(pos, r->xmax, r->ymin, 0.0f);
+ immVertex3f(pos, r->xmax, r->ymin, 0.0f);
+ immVertex3f(pos, r->xmax - margin[0], r->ymin, 0.0f);
- immVertex2f(pos, r->xmax, r->ymax - margin[1]);
- immVertex2f(pos, r->xmax, r->ymax);
- immVertex2f(pos, r->xmax, r->ymax);
- immVertex2f(pos, r->xmax - margin[0], r->ymax);
+ immVertex3f(pos, r->xmax, r->ymax - margin[1], 0.0f);
+ immVertex3f(pos, r->xmax, r->ymax, 0.0f);
+ immVertex3f(pos, r->xmax, r->ymax, 0.0f);
+ immVertex3f(pos, r->xmax - margin[0], r->ymax, 0.0f);
- immVertex2f(pos, r->xmin, r->ymax - margin[1]);
- immVertex2f(pos, r->xmin, r->ymax);
- immVertex2f(pos, r->xmin, r->ymax);
- immVertex2f(pos, r->xmin + margin[0], r->ymax);
+ immVertex3f(pos, r->xmin, r->ymax - margin[1], 0.0f);
+ immVertex3f(pos, r->xmin, r->ymax, 0.0f);
+ immVertex3f(pos, r->xmin, r->ymax, 0.0f);
+ immVertex3f(pos, r->xmin + margin[0], r->ymax, 0.0f);
immEnd();
@@ -440,12 +441,35 @@ static void cage2d_draw_box_interaction(const float color[4],
static void imm_draw_point_aspect_2d(
uint pos, float x, float y, float rad_x, float rad_y, bool solid)
{
- immBegin(solid ? GPU_PRIM_TRI_FAN : GPU_PRIM_LINE_LOOP, 4);
- immVertex2f(pos, x - rad_x, y - rad_y);
- immVertex2f(pos, x - rad_x, y + rad_y);
- immVertex2f(pos, x + rad_x, y + rad_y);
- immVertex2f(pos, x + rad_x, y - rad_y);
- immEnd();
+ if (solid) {
+ /* Note(Metal/AMD): Small Triangle-list primitives more optimal for GPU HW than Trianglestrip.
+ */
+ immBegin(GPU_PRIM_TRIS, 6);
+ immVertex2f(pos, x - rad_x, y - rad_y);
+ immVertex2f(pos, x - rad_x, y + rad_y);
+ immVertex2f(pos, x + rad_x, y + rad_y);
+
+ immVertex2f(pos, x - rad_x, y - rad_y);
+ immVertex2f(pos, x + rad_x, y + rad_y);
+ immVertex2f(pos, x + rad_x, y - rad_y);
+ immEnd();
+ }
+ else {
+ /* Note(Metal/AMD): Small Line-list primitives more optimal for GPU HW than Linestrip. */
+ immBegin(GPU_PRIM_LINES, 8);
+ immVertex2f(pos, x - rad_x, y - rad_y);
+ immVertex2f(pos, x - rad_x, y + rad_y);
+
+ immVertex2f(pos, x - rad_x, y + rad_y);
+ immVertex2f(pos, x + rad_x, y + rad_y);
+
+ immVertex2f(pos, x + rad_x, y + rad_y);
+ immVertex2f(pos, x + rad_x, y - rad_y);
+
+ immVertex2f(pos, x + rad_x, y - rad_y);
+ immVertex2f(pos, x - rad_x, y - rad_y);
+ immEnd();
+ }
}
static void cage2d_draw_circle_wire(const rctf *r,
@@ -455,7 +479,9 @@ static void cage2d_draw_circle_wire(const rctf *r,
const int draw_options,
const float line_width)
{
- uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ /* Note(Metal): Prefer using 3D coordinates with 3D shader input, even if rendering 2D gizmo's.
+ */
+ uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR);
immUniformColor3fv(color);
@@ -465,17 +491,28 @@ static void cage2d_draw_circle_wire(const rctf *r,
immUniform2fv("viewportSize", &viewport[2]);
immUniform1f("lineWidth", line_width * U.pixelsize);
- immBegin(GPU_PRIM_LINE_LOOP, 4);
- immVertex2f(pos, r->xmin, r->ymin);
- immVertex2f(pos, r->xmax, r->ymin);
- immVertex2f(pos, r->xmax, r->ymax);
- immVertex2f(pos, r->xmin, r->ymax);
+ /* Small 'lines' primitives more efficient for hardware processing than linestrip. */
+ immBegin(GPU_PRIM_LINES, 8);
+ immVertex3f(pos, r->xmin, r->ymin, 0.0f);
+ immVertex3f(pos, r->xmax, r->ymin, 0.0f);
+
+ immVertex3f(pos, r->xmax, r->ymin, 0.0f);
+ immVertex3f(pos, r->xmax, r->ymax, 0.0f);
+
+ immVertex3f(pos, r->xmax, r->ymax, 0.0f);
+ immVertex3f(pos, r->xmin, r->ymax, 0.0f);
+
+ immVertex3f(pos, r->xmin, r->ymax, 0.0f);
+ immVertex3f(pos, r->xmin, r->ymin, 0.0f);
immEnd();
if (transform_flag & ED_GIZMO_CAGE2D_XFORM_FLAG_ROTATE) {
- immBegin(GPU_PRIM_LINE_LOOP, 2);
- immVertex2f(pos, BLI_rctf_cent_x(r), r->ymax);
- immVertex2f(pos, BLI_rctf_cent_x(r), r->ymax + margin[1]);
+ immBegin(GPU_PRIM_LINES, 4);
+ immVertex3f(pos, BLI_rctf_cent_x(r), r->ymax, 0.0f);
+ immVertex3f(pos, BLI_rctf_cent_x(r), r->ymax + margin[1], 0.0f);
+
+ immVertex3f(pos, BLI_rctf_cent_x(r), r->ymax + margin[1], 0.0f);
+ immVertex3f(pos, BLI_rctf_cent_x(r), r->ymax, 0.0f);
immEnd();
}
@@ -485,10 +522,10 @@ static void cage2d_draw_circle_wire(const rctf *r,
const float center[2] = {BLI_rctf_cent_x(r), BLI_rctf_cent_y(r)};
immBegin(GPU_PRIM_LINES, 4);
- immVertex2f(pos, center[0] - rad[0], center[1] - rad[1]);
- immVertex2f(pos, center[0] + rad[0], center[1] + rad[1]);
- immVertex2f(pos, center[0] + rad[0], center[1] - rad[1]);
- immVertex2f(pos, center[0] - rad[0], center[1] + rad[1]);
+ immVertex3f(pos, center[0] - rad[0], center[1] - rad[1], 0.0f);
+ immVertex3f(pos, center[0] + rad[0], center[1] + rad[1], 0.0f);
+ immVertex3f(pos, center[0] + rad[0], center[1] - rad[1], 0.0f);
+ immVertex3f(pos, center[0] - rad[0], center[1] + rad[1], 0.0f);
immEnd();
}
}
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 e1f584bf9e4..a76242404ba 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c
@@ -96,7 +96,8 @@ static void dial_geom_draw(const float color[4],
ED_GIZMO_DIAL_DRAW_FLAG_FILL)));
GPUVertFormat *format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ /* Note(Metal): Prefer using 3D coordinates with 3D shader, even if rendering 2D gizmo's. */
+ uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
if (clip_plane) {
immBindBuiltinProgram(filled ? GPU_SHADER_3D_CLIPPED_UNIFORM_COLOR :
@@ -114,18 +115,19 @@ static void dial_geom_draw(const float color[4],
if (filled) {
if (arc_partial_angle == 0.0f) {
if (arc_inner_factor == 0.0f) {
- imm_draw_circle_fill_2d(pos, 0, 0, 1.0, DIAL_RESOLUTION);
+ imm_draw_circle_fill_3d(pos, 0.0f, 0.0f, 1.0f, DIAL_RESOLUTION);
}
else {
- imm_draw_disk_partial_fill_2d(
- pos, 0, 0, arc_inner_factor, 1.0f, DIAL_RESOLUTION, 0, RAD2DEGF(M_PI * 2));
+ imm_draw_disk_partial_fill_3d(
+ pos, 0.0f, 0.0f, 0.0f, arc_inner_factor, 1.0f, DIAL_RESOLUTION, 0, RAD2DEGF(M_PI * 2));
}
}
else {
float arc_partial_deg = RAD2DEGF((M_PI * 2) - arc_partial_angle);
- imm_draw_disk_partial_fill_2d(pos,
- 0,
- 0,
+ imm_draw_disk_partial_fill_3d(pos,
+ 0.0f,
+ 0.0f,
+ 0.0f,
arc_inner_factor,
1.0f,
DIAL_RESOLUTION,
@@ -140,15 +142,15 @@ static void dial_geom_draw(const float color[4],
immUniform1f("lineWidth", line_width * U.pixelsize);
if (arc_partial_angle == 0.0f) {
- imm_draw_circle_wire_2d(pos, 0, 0, 1.0, DIAL_RESOLUTION);
+ imm_draw_circle_wire_3d(pos, 0.0f, 0.0f, 1.0f, DIAL_RESOLUTION);
if (arc_inner_factor != 0.0f) {
- imm_draw_circle_wire_2d(pos, 0, 0, arc_inner_factor, DIAL_RESOLUTION);
+ imm_draw_circle_wire_3d(pos, 0.0f, 0.0f, arc_inner_factor, DIAL_RESOLUTION);
}
}
else {
float arc_partial_deg = RAD2DEGF((M_PI * 2) - arc_partial_angle);
- imm_draw_circle_partial_wire_2d(
- pos, 0, 0, 1.0, DIAL_RESOLUTION, -arc_partial_deg / 2, arc_partial_deg);
+ imm_draw_circle_partial_wire_3d(
+ pos, 0.0f, 0.0f, 0.0f, 1.0f, DIAL_RESOLUTION, -arc_partial_deg / 2, arc_partial_deg);
# if 0
if (arc_inner_factor != 0.0f) {
BLI_assert(0);
@@ -186,7 +188,7 @@ static void dial_ghostarc_draw_helpline(const float angle,
immUniformColor4fv(color);
immBegin(GPU_PRIM_LINE_STRIP, 2);
- immVertex3f(pos, 0.0f, 0, 0.0f);
+ immVertex3f(pos, 0.0f, 0.0f, 0.0f);
immVertex3fv(pos, co_outer);
immEnd();
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 447fe1005a1..5fb1173521a 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c
@@ -98,7 +98,8 @@ static void move_geom_draw(const wmGizmo *gz,
ED_GIZMO_MOVE_DRAW_FLAG_FILL)));
GPUVertFormat *format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ /* Note(Metal): Prefer using 3D coordinates with 3D shader, even if rendering 2D gizmo's. */
+ uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
immBindBuiltinProgram(filled ? GPU_SHADER_3D_UNIFORM_COLOR :
GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR);
@@ -115,20 +116,20 @@ static void move_geom_draw(const wmGizmo *gz,
if (draw_style == ED_GIZMO_MOVE_STYLE_RING_2D) {
if (filled) {
- imm_draw_circle_fill_2d(pos, 0, 0, radius, DIAL_RESOLUTION);
+ imm_draw_circle_fill_3d(pos, 0.0f, 0.0f, radius, DIAL_RESOLUTION);
}
else {
- imm_draw_circle_wire_2d(pos, 0, 0, radius, DIAL_RESOLUTION);
+ imm_draw_circle_wire_3d(pos, 0.0f, 0.0f, radius, DIAL_RESOLUTION);
}
}
else if (draw_style == ED_GIZMO_MOVE_STYLE_CROSS_2D) {
const float radius_diag = M_SQRT1_2 * radius;
immBegin(GPU_PRIM_LINES, 4);
- immVertex2f(pos, radius_diag, radius_diag);
- immVertex2f(pos, -radius_diag, -radius_diag);
+ immVertex3f(pos, radius_diag, radius_diag, 0.0f);
+ immVertex3f(pos, -radius_diag, -radius_diag, 0.0f);
- immVertex2f(pos, -radius_diag, radius_diag);
- immVertex2f(pos, radius_diag, -radius_diag);
+ immVertex3f(pos, -radius_diag, radius_diag, 0.0f);
+ immVertex3f(pos, radius_diag, -radius_diag, 0.0f);
immEnd();
}
else {
diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c
index d962dcdfa10..c4fd34212c3 100644
--- a/source/blender/editors/gpencil/gpencil_select.c
+++ b/source/blender/editors/gpencil/gpencil_select.c
@@ -826,7 +826,7 @@ static bool gpencil_select_same_material(bContext *C)
bGPdata *gpd = ED_gpencil_data_get_active(C);
const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
/* First, build set containing all the colors of selected strokes */
- GSet *selected_colors = BLI_gset_str_new("GP Selected Colors");
+ GSet *selected_colors = BLI_gset_int_new("GP Selected Colors");
bool changed = false;
@@ -835,7 +835,7 @@ static bool gpencil_select_same_material(bContext *C)
/* add instead of insert here, otherwise the uniqueness check gets skipped,
* and we get many duplicate entries...
*/
- BLI_gset_add(selected_colors, &gps->mat_nr);
+ BLI_gset_add(selected_colors, POINTER_FROM_INT(gps->mat_nr));
}
}
CTX_DATA_END;
@@ -843,7 +843,8 @@ static bool gpencil_select_same_material(bContext *C)
/* Second, select any visible stroke that uses these colors */
if (is_curve_edit) {
CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
- if (gps->editcurve != NULL && BLI_gset_haskey(selected_colors, &gps->mat_nr)) {
+ if (gps->editcurve != NULL &&
+ BLI_gset_haskey(selected_colors, POINTER_FROM_INT(gps->mat_nr))) {
bGPDcurve *gpc = gps->editcurve;
for (int i = 0; i < gpc->tot_curve_points; i++) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
@@ -861,7 +862,7 @@ static bool gpencil_select_same_material(bContext *C)
}
else {
CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
- if (BLI_gset_haskey(selected_colors, &gps->mat_nr)) {
+ if (BLI_gset_haskey(selected_colors, POINTER_FROM_INT(gps->mat_nr))) {
/* select this stroke */
bGPDspoint *pt;
int i;
diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h
index 01885911ac4..d969277fef5 100644
--- a/source/blender/editors/include/ED_armature.h
+++ b/source/blender/editors/include/ED_armature.h
@@ -363,7 +363,7 @@ void ED_mesh_deform_bind_callback(struct Object *object,
struct MeshDeformModifierData *mmd,
struct Mesh *cagemesh,
float *vertexcos,
- int totvert,
+ int verts_num,
float cagemat[4][4]);
/* Pose backups, pose_backup.c */
diff --git a/source/blender/editors/include/ED_paint.h b/source/blender/editors/include/ED_paint.h
index cd28fbe9687..ba5834fd508 100644
--- a/source/blender/editors/include/ED_paint.h
+++ b/source/blender/editors/include/ED_paint.h
@@ -114,11 +114,6 @@ void ED_paintcurve_undo_push_end(struct bContext *C);
void ED_paintcurve_undosys_type(struct UndoType *ut);
/* paint_canvas.cc */
-struct Image *ED_paint_canvas_image_get(const struct PaintModeSettings *settings,
- struct Object *ob);
-int ED_paint_canvas_uvmap_layer_index_get(const struct PaintModeSettings *settings,
- struct Object *ob);
-
/** Color type of an object can be overridden in sculpt/paint mode. */
eV3DShadingColorType ED_paint_shading_color_override(struct bContext *C,
const struct PaintModeSettings *settings,
diff --git a/source/blender/editors/include/ED_sculpt.h b/source/blender/editors/include/ED_sculpt.h
index 3100c135db4..54d67c50d5c 100644
--- a/source/blender/editors/include/ED_sculpt.h
+++ b/source/blender/editors/include/ED_sculpt.h
@@ -17,6 +17,9 @@ struct UndoType;
struct ViewContext;
struct bContext;
struct rcti;
+struct wmMsgSubscribeKey;
+struct wmMsgSubscribeValue;
+struct wmRegionMessageSubscribeParams;
/* sculpt.c */
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index c14cce28052..3f58aab3e53 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -595,6 +595,7 @@ typedef void (*uiMenuHandleFunc)(struct bContext *C, void *arg, int event);
*/
typedef bool (*uiMenuStepFunc)(struct bContext *C, int direction, void *arg1);
+typedef void *(*uiCopyArgFunc)(const void *arg);
typedef void (*uiFreeArgFunc)(void *arg);
/* interface_query.c */
@@ -2065,6 +2066,24 @@ void uiLayoutSetFunc(uiLayout *layout, uiMenuHandleFunc handlefunc, void *argv);
void uiLayoutSetContextPointer(uiLayout *layout, const char *name, struct PointerRNA *ptr);
struct bContextStore *uiLayoutGetContextStore(uiLayout *layout);
void uiLayoutContextCopy(uiLayout *layout, struct bContextStore *context);
+
+/**
+ * Set tooltip function for all buttons in the layout.
+ * func, arg and free_arg are passed on to UI_but_func_tooltip_set, so their meaning is the same.
+ *
+ * \param func: The callback function that gets called to get tooltip content
+ * \param arg: An optional opaque pointer that gets passed to func
+ * \param free_arg: An optional callback for freeing arg (can be set to e.g. MEM_freeN)
+ * \param copy_arg: An optional callback for duplicating arg in case UI_but_func_tooltip_set
+ * is being called on multiple buttons (can be set to e.g. MEM_dupallocN). If set to NULL, arg will
+ * be passed as-is to all buttons.
+ */
+void uiLayoutSetTooltipFunc(uiLayout *layout,
+ uiButToolTipFunc func,
+ void *arg,
+ uiCopyArgFunc copy_arg,
+ uiFreeArgFunc free_arg);
+
/**
* This is a bit of a hack but best keep it in one place at least.
*/
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 8935df7b581..2c408619fe7 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -3018,12 +3018,12 @@ static bool ui_textedit_set_cursor_pos_foreach_glyph(const char *UNUSED(str),
const size_t str_step_ofs,
const rcti *glyph_step_bounds,
const int UNUSED(glyph_advance_x),
- const rctf *glyph_bounds,
+ const rcti *glyph_bounds,
const int UNUSED(glyph_bearing[2]),
void *user_data)
{
int *cursor_data = user_data;
- const float center = glyph_step_bounds->xmin + (BLI_rctf_size_x(glyph_bounds) / 2.0f);
+ const int center = glyph_step_bounds->xmin + (BLI_rcti_size_x(glyph_bounds) / 2.0f);
if (cursor_data[0] < center) {
cursor_data[1] = str_step_ofs;
return false;
diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c
index e3bf1a48907..332b9b44b0a 100644
--- a/source/blender/editors/interface/interface_icons.c
+++ b/source/blender/editors/interface/interface_icons.c
@@ -1584,10 +1584,10 @@ 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);
- const int data_loc = GPU_shader_get_uniform_block(shader, "multi_rect_data");
+ const int data_binding = GPU_shader_get_uniform_block_binding(shader, "multi_rect_data");
GPUUniformBuf *ubo = GPU_uniformbuf_create_ex(
sizeof(struct MultiRectCallData), texture_draw_calls->drawcall_cache, __func__);
- GPU_uniformbuf_bind(ubo, data_loc);
+ GPU_uniformbuf_bind(ubo, data_binding);
const int img_binding = GPU_shader_get_texture_binding(shader, "image");
GPU_texture_bind_ex(texture, GPU_SAMPLER_ICON, img_binding, false);
diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c
index ac807f06891..c1bb2ed6d18 100644
--- a/source/blender/editors/interface/interface_layout.c
+++ b/source/blender/editors/interface/interface_layout.c
@@ -5681,6 +5681,40 @@ void uiLayoutContextCopy(uiLayout *layout, bContextStore *context)
layout->context = CTX_store_add_all(&block->contexts, context);
}
+void uiLayoutSetTooltipFunc(uiLayout *layout,
+ uiButToolTipFunc func,
+ void *arg,
+ uiCopyArgFunc copy_arg,
+ uiFreeArgFunc free_arg)
+{
+ bool arg_used = false;
+
+ LISTBASE_FOREACH (uiItem *, item, &layout->items) {
+ /* Each button will call free_arg for "its" argument, so we need to
+ * duplicate the allocation for each button after the first. */
+ if (copy_arg != NULL && arg_used) {
+ arg = copy_arg(arg);
+ }
+ arg_used = true;
+
+ if (item->type == ITEM_BUTTON) {
+ uiButtonItem *bitem = (uiButtonItem *)item;
+ if (bitem->but->type == UI_BTYPE_DECORATOR) {
+ continue;
+ }
+ UI_but_func_tooltip_set(bitem->but, func, arg, free_arg);
+ }
+ else {
+ uiLayoutSetTooltipFunc((uiLayout *)item, func, arg, copy_arg, free_arg);
+ }
+ }
+
+ if (!arg_used) {
+ /* Free the original copy of arg in case the layout is empty. */
+ free_arg(arg);
+ }
+}
+
void uiLayoutSetContextFromBut(uiLayout *layout, uiBut *but)
{
if (but->opptr) {
diff --git a/source/blender/editors/interface/interface_query.cc b/source/blender/editors/interface/interface_query.cc
index 2767a184619..337b2852d57 100644
--- a/source/blender/editors/interface/interface_query.cc
+++ b/source/blender/editors/interface/interface_query.cc
@@ -61,7 +61,7 @@ bool ui_but_is_toggle(const uiBut *but)
bool ui_but_is_interactive(const uiBut *but, const bool labeledit)
{
/* NOTE: #UI_BTYPE_LABEL is included for highlights, this allows drags. */
- if ((but->type == UI_BTYPE_LABEL) && but->dragpoin == nullptr) {
+ if ((but->type == UI_BTYPE_LABEL) && but->dragpoin == nullptr && but->tip_func == nullptr) {
return false;
}
if (ELEM(but->type, UI_BTYPE_ROUNDBOX, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE, UI_BTYPE_LISTBOX)) {
diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c
index 29553ff65d1..ca18ec14a54 100644
--- a/source/blender/editors/interface/interface_region_tooltip.c
+++ b/source/blender/editors/interface/interface_region_tooltip.c
@@ -788,8 +788,10 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C,
}
/* Tip Label (only for buttons not already showing the label).
- * Check prefix instead of comparing because the button may include the shortcut. */
- if (but_label.strinfo && !STRPREFIX(but->drawstr, but_label.strinfo)) {
+ * Check prefix instead of comparing because the button may include the shortcut.
+ * Buttons with dynamic tooltips also don't get their default label here since they
+ * can already provide more accurate and specific tooltip content. */
+ if (but_label.strinfo && !STRPREFIX(but->drawstr, but_label.strinfo) && !but->tip_func) {
uiTooltipField *field = text_field_add(data,
&(uiTooltipFormat){
.style = UI_TIP_STYLE_HEADER,
diff --git a/source/blender/editors/interface/interface_style.cc b/source/blender/editors/interface/interface_style.cc
index b4e97f8a396..0156a943015 100644
--- a/source/blender/editors/interface/interface_style.cc
+++ b/source/blender/editors/interface/interface_style.cc
@@ -161,7 +161,7 @@ void UI_fontstyle_draw_ex(const uiFontStyle *fs,
}
else {
/* Draw from bound-box center. */
- const float height = BLF_ascender(fs->uifont_id) + BLF_descender(fs->uifont_id);
+ const int height = BLF_ascender(fs->uifont_id) + BLF_descender(fs->uifont_id);
yofs = ceil(0.5f * (BLI_rcti_size_y(rect) - height));
}
@@ -279,9 +279,9 @@ void UI_fontstyle_draw_simple_backdrop(const uiFontStyle *fs,
UI_fontstyle_set(fs);
{
- const float width = BLF_width(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX);
- const float height = BLF_height_max(fs->uifont_id);
- const float decent = BLF_descender(fs->uifont_id);
+ const int width = BLF_width(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX);
+ const int height = BLF_height_max(fs->uifont_id);
+ const int decent = BLF_descender(fs->uifont_id);
const float margin = height / 4.0f;
rctf rect;
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 38499a7f089..a5e2e9353bf 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -787,7 +787,7 @@ static const char *template_id_browse_tip(const StructRNA *type)
case ID_LP:
return N_("Browse LightProbe to be linked");
case ID_CV:
- return N_("Browse Hair Curves Data to be linked");
+ return N_("Browse Curves Data to be linked");
case ID_PT:
return N_("Browse Point Cloud Data to be linked");
case ID_VO:
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c
index a16c24f63cd..98ecf91adbc 100644
--- a/source/blender/editors/interface/interface_widgets.c
+++ b/source/blender/editors/interface/interface_widgets.c
@@ -1859,15 +1859,15 @@ static bool widget_draw_text_underline_calc_position(const char *UNUSED(str),
const size_t str_step_ofs,
const rcti *glyph_step_bounds,
const int UNUSED(glyph_advance_x),
- const rctf *glyph_bounds,
+ const rcti *glyph_bounds,
const int UNUSED(glyph_bearing[2]),
void *user_data)
{
struct UnderlineData *ul_data = user_data;
if (ul_data->str_offset == str_step_ofs) {
/* Full width of this glyph including both bearings. */
- const float width = glyph_bounds->xmin + BLI_rctf_size_x(glyph_bounds) + glyph_bounds->xmin;
- ul_data->r_offset_px[0] = glyph_step_bounds->xmin + ((width - ul_data->width_px) * 0.5f);
+ const int width = glyph_bounds->xmin + BLI_rcti_size_x(glyph_bounds) + glyph_bounds->xmin;
+ ul_data->r_offset_px[0] = glyph_step_bounds->xmin + ((width - ul_data->width_px) / 2);
/* One line-width below the lower glyph bounds. */
ul_data->r_offset_px[1] = glyph_bounds->ymin - U.pixelsize;
/* Early exit. */
diff --git a/source/blender/editors/io/io_obj.c b/source/blender/editors/io/io_obj.c
index 97f1e08fdff..beed4abd52b 100644
--- a/source/blender/editors/io/io_obj.c
+++ b/source/blender/editors/io/io_obj.c
@@ -378,6 +378,7 @@ static int wm_obj_import_exec(bContext *C, wmOperator *op)
import_params.clamp_size = RNA_float_get(op->ptr, "clamp_size");
import_params.forward_axis = RNA_enum_get(op->ptr, "forward_axis");
import_params.up_axis = RNA_enum_get(op->ptr, "up_axis");
+ import_params.validate_meshes = RNA_boolean_get(op->ptr, "validate_meshes");
OBJ_import(C, &import_params);
@@ -388,8 +389,8 @@ static void ui_obj_import_settings(uiLayout *layout, PointerRNA *imfptr)
{
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
- uiLayout *box = uiLayoutBox(layout);
+ uiLayout *box = uiLayoutBox(layout);
uiItemL(box, IFACE_("Transform"), ICON_OBJECT_DATA);
uiLayout *col = uiLayoutColumn(box, false);
uiLayout *sub = uiLayoutColumn(col, false);
@@ -397,6 +398,11 @@ static void ui_obj_import_settings(uiLayout *layout, PointerRNA *imfptr)
sub = uiLayoutColumn(col, false);
uiItemR(sub, imfptr, "forward_axis", 0, IFACE_("Axis Forward"), ICON_NONE);
uiItemR(sub, imfptr, "up_axis", 0, IFACE_("Up"), ICON_NONE);
+
+ box = uiLayoutBox(layout);
+ uiItemL(box, IFACE_("Options"), ICON_EXPORT);
+ col = uiLayoutColumn(box, false);
+ uiItemR(col, imfptr, "validate_meshes", 0, NULL, ICON_NONE);
}
static void wm_obj_import_draw(bContext *C, wmOperator *op)
@@ -442,4 +448,9 @@ void WM_OT_obj_import(struct wmOperatorType *ot)
"Forward Axis",
"");
RNA_def_enum(ot->srna, "up_axis", io_obj_transform_axis_up, OBJ_AXIS_Y_UP, "Up Axis", "");
+ RNA_def_boolean(ot->srna,
+ "validate_meshes",
+ false,
+ "Validate Meshes",
+ "Check imported mesh objects for invalid data (slow)");
}
diff --git a/source/blender/editors/io/io_usd.c b/source/blender/editors/io/io_usd.c
index bff1bdd0ed8..e0616a0cec3 100644
--- a/source/blender/editors/io/io_usd.c
+++ b/source/blender/editors/io/io_usd.c
@@ -215,47 +215,43 @@ void WM_OT_usd_export(struct wmOperatorType *ot)
"selected_objects_only",
false,
"Selection Only",
- "Only selected objects are exported. Unselected parents of selected objects are "
+ "Only export selected objects. Unselected parents of selected objects are "
"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");
+ "Only export visible objects. Invisible parents of exported objects are "
+ "exported as empty transforms");
- RNA_def_boolean(ot->srna,
- "export_animation",
- false,
- "Animation",
- "When checked, the render frame range is exported. When false, only the current "
- "frame is exported");
RNA_def_boolean(
- ot->srna, "export_hair", false, "Hair", "When checked, hair is exported as USD curves");
- RNA_def_boolean(ot->srna,
- "export_uvmaps",
- true,
- "UV Maps",
- "When checked, all UV maps of exported meshes are included in the export");
+ ot->srna,
+ "export_animation",
+ false,
+ "Animation",
+ "Export all frames in the render frame range, rather than only the current frame");
+ RNA_def_boolean(
+ ot->srna, "export_hair", false, "Hair", "Export hair particle systems as USD curves");
+ RNA_def_boolean(
+ ot->srna, "export_uvmaps", true, "UV Maps", "Include all mesh UV maps in the export");
RNA_def_boolean(ot->srna,
"export_normals",
true,
"Normals",
- "When checked, normals of exported meshes are included in the export");
+ "Include normals of exported meshes in the export");
RNA_def_boolean(ot->srna,
"export_materials",
true,
"Materials",
- "When checked, the viewport settings of materials are exported as USD preview "
- "materials, and material assignments are exported as geometry subsets");
+ "Export viewport settings of materials as USD preview materials, and export "
+ "material assignments as geometry subsets");
RNA_def_boolean(ot->srna,
"use_instancing",
false,
"Instancing",
- "When checked, instanced objects are exported as references in USD. "
- "When unchecked, instanced objects are exported as real objects");
+ "Export instanced objects as references in USD rather than real objects");
RNA_def_enum(ot->srna,
"evaluation_mode",
diff --git a/source/blender/editors/mask/mask_ops.c b/source/blender/editors/mask/mask_ops.c
index 595e6d791ef..3c0e7ee399c 100644
--- a/source/blender/editors/mask/mask_ops.c
+++ b/source/blender/editors/mask/mask_ops.c
@@ -15,6 +15,7 @@
#include "BKE_mask.h"
#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
#include "DNA_mask_types.h"
#include "DNA_object_types.h" /* SELECT */
@@ -243,7 +244,7 @@ static void mask_point_undistort_pos(SpaceClip *sc, float r_co[2], const float c
}
static bool spline_under_mouse_get(const bContext *C,
- Mask *mask,
+ Mask *mask_orig,
const float co[2],
MaskLayer **r_mask_layer,
MaskSpline **r_mask_spline)
@@ -258,6 +259,9 @@ static bool spline_under_mouse_get(const bContext *C,
*r_mask_layer = NULL;
*r_mask_spline = NULL;
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Mask *mask_eval = (Mask *)DEG_get_evaluated_id(depsgraph, &mask_orig->id);
+
int width, height;
ED_mask_get_size(area, &width, &height);
float pixel_co[2];
@@ -266,22 +270,25 @@ static bool spline_under_mouse_get(const bContext *C,
if (sc != NULL) {
undistort = (sc->clip != NULL) && (sc->user.render_flag & MCLIP_PROXY_RENDER_UNDISTORT) != 0;
}
- for (MaskLayer *mask_layer = mask->masklayers.first; mask_layer != NULL;
- mask_layer = mask_layer->next) {
- if (mask_layer->visibility_flag & MASK_HIDE_SELECT) {
+
+ for (MaskLayer *mask_layer_orig = mask_orig->masklayers.first,
+ *mask_layer_eval = mask_eval->masklayers.first;
+ mask_layer_orig != NULL;
+ mask_layer_orig = mask_layer_orig->next, mask_layer_eval = mask_layer_eval->next) {
+ if (mask_layer_orig->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
-
- for (MaskSpline *spline = mask_layer->splines.first; spline != NULL; spline = spline->next) {
- MaskSplinePoint *points_array;
- float min[2], max[2], center[2];
- if ((spline->flag & SELECT) == 0) {
+ for (MaskSpline *spline_orig = mask_layer_orig->splines.first,
+ *spline_eval = mask_layer_eval->splines.first;
+ spline_orig != NULL;
+ spline_orig = spline_orig->next, spline_eval = spline_eval->next) {
+ if ((spline_orig->flag & SELECT) == 0) {
continue;
}
-
- points_array = BKE_mask_spline_point_array(spline);
+ MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline_eval);
+ float min[2], max[2], center[2];
INIT_MINMAX2(min, max);
- for (int i = 0; i < spline->tot_point; i++) {
+ for (int i = 0; i < spline_orig->tot_point; i++) {
MaskSplinePoint *point_deform = &points_array[i];
BezTriple *bezt = &point_deform->bezt;
@@ -302,8 +309,8 @@ static bool spline_under_mouse_get(const bContext *C,
float max_bb_side = min_ff((max[0] - min[0]) * width, (max[1] - min[1]) * height);
if (dist_squared <= max_bb_side * max_bb_side * 0.5f &&
(closest_spline == NULL || dist_squared < closest_dist_squared)) {
- closest_layer = mask_layer;
- closest_spline = spline;
+ closest_layer = mask_layer_orig;
+ closest_spline = spline_orig;
closest_dist_squared = dist_squared;
}
}
@@ -311,7 +318,7 @@ static bool spline_under_mouse_get(const bContext *C,
if (closest_dist_squared < square_f(threshold) && closest_spline != NULL) {
float diff_score;
if (ED_mask_find_nearest_diff_point(C,
- mask,
+ mask_orig,
co,
threshold,
false,
diff --git a/source/blender/editors/mask/mask_select.c b/source/blender/editors/mask/mask_select.c
index 29cd593ce7f..e3c3f1c38a0 100644
--- a/source/blender/editors/mask/mask_select.c
+++ b/source/blender/editors/mask/mask_select.c
@@ -16,6 +16,9 @@
#include "BKE_context.h"
#include "BKE_mask.h"
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
+
#include "DNA_mask_types.h"
#include "WM_api.h"
@@ -427,7 +430,9 @@ static int box_select_exec(bContext *C, wmOperator *op)
ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
- Mask *mask = CTX_data_edit_mask(C);
+ Mask *mask_orig = CTX_data_edit_mask(C);
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Mask *mask_eval = (Mask *)DEG_get_evaluated_id(depsgraph, &mask_orig->id);
rcti rect;
rctf rectf;
@@ -436,7 +441,7 @@ static int box_select_exec(bContext *C, wmOperator *op)
const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
const bool select = (sel_op != SEL_OP_SUB);
if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
- ED_mask_select_toggle_all(mask, SEL_DESELECT);
+ ED_mask_select_toggle_all(mask_orig, SEL_DESELECT);
changed = true;
}
@@ -447,16 +452,22 @@ static int box_select_exec(bContext *C, wmOperator *op)
ED_mask_point_pos(area, region, rect.xmax, rect.ymax, &rectf.xmax, &rectf.ymax);
/* do actual selection */
- LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) {
- if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
+ for (MaskLayer *mask_layer_orig = mask_orig->masklayers.first,
+ *mask_layer_eval = mask_eval->masklayers.first;
+ mask_layer_orig != NULL;
+ mask_layer_orig = mask_layer_orig->next, mask_layer_eval = mask_layer_eval->next) {
+ if (mask_layer_orig->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
+ for (MaskSpline *spline_orig = mask_layer_orig->splines.first,
+ *spline_eval = mask_layer_eval->splines.first;
+ spline_orig != NULL;
+ spline_orig = spline_orig->next, spline_eval = spline_eval->next) {
- LISTBASE_FOREACH (MaskSpline *, spline, &mask_layer->splines) {
- MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
+ MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline_eval);
- for (int i = 0; i < spline->tot_point; i++) {
- MaskSplinePoint *point = &spline->points[i];
+ for (int i = 0; i < spline_orig->tot_point; i++) {
+ MaskSplinePoint *point = &spline_orig->points[i];
MaskSplinePoint *point_deform = &points_array[i];
/* TODO: handles? */
@@ -471,10 +482,10 @@ static int box_select_exec(bContext *C, wmOperator *op)
}
if (changed) {
- ED_mask_select_flush_all(mask);
+ ED_mask_select_flush_all(mask_orig);
- DEG_id_tag_update(&mask->id, ID_RECALC_SELECT);
- WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
+ DEG_id_tag_update(&mask_orig->id, ID_RECALC_SELECT);
+ WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask_orig);
return OPERATOR_FINISHED;
}
@@ -517,14 +528,16 @@ static bool do_lasso_select_mask(bContext *C,
ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
- Mask *mask = CTX_data_edit_mask(C);
+ Mask *mask_orig = CTX_data_edit_mask(C);
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Mask *mask_eval = (Mask *)DEG_get_evaluated_id(depsgraph, &mask_orig->id);
rcti rect;
bool changed = false;
const bool select = (sel_op != SEL_OP_SUB);
if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
- ED_mask_select_toggle_all(mask, SEL_DESELECT);
+ ED_mask_select_toggle_all(mask_orig, SEL_DESELECT);
changed = true;
}
@@ -532,16 +545,22 @@ static bool do_lasso_select_mask(bContext *C,
BLI_lasso_boundbox(&rect, mcoords, mcoords_len);
/* do actual selection */
- LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) {
- if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
+ for (MaskLayer *mask_layer_orig = mask_orig->masklayers.first,
+ *mask_layer_eval = mask_eval->masklayers.first;
+ mask_layer_orig != NULL;
+ mask_layer_orig = mask_layer_orig->next, mask_layer_eval = mask_layer_eval->next) {
+ if (mask_layer_orig->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
+ for (MaskSpline *spline_orig = mask_layer_orig->splines.first,
+ *spline_eval = mask_layer_eval->splines.first;
+ spline_orig != NULL;
+ spline_orig = spline_orig->next, spline_eval = spline_eval->next) {
- LISTBASE_FOREACH (MaskSpline *, spline, &mask_layer->splines) {
- MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
+ MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline_eval);
- for (int i = 0; i < spline->tot_point; i++) {
- MaskSplinePoint *point = &spline->points[i];
+ for (int i = 0; i < spline_orig->tot_point; i++) {
+ MaskSplinePoint *point = &spline_orig->points[i];
MaskSplinePoint *point_deform = &points_array[i];
/* TODO: handles? */
@@ -572,10 +591,10 @@ static bool do_lasso_select_mask(bContext *C,
}
if (changed) {
- ED_mask_select_flush_all(mask);
+ ED_mask_select_flush_all(mask_orig);
- DEG_id_tag_update(&mask->id, ID_RECALC_SELECT);
- WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
+ DEG_id_tag_update(&mask_orig->id, ID_RECALC_SELECT);
+ WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask_orig);
}
return changed;
@@ -643,7 +662,9 @@ static int circle_select_exec(bContext *C, wmOperator *op)
ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
- Mask *mask = CTX_data_edit_mask(C);
+ Mask *mask_orig = CTX_data_edit_mask(C);
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Mask *mask_eval = (Mask *)DEG_get_evaluated_id(depsgraph, &mask_orig->id);
float zoomx, zoomy, offset[2], ellipse[2];
int width, height;
@@ -668,21 +689,27 @@ static int circle_select_exec(bContext *C, wmOperator *op)
WM_gesture_is_modal_first(op->customdata));
const bool select = (sel_op != SEL_OP_SUB);
if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
- ED_mask_select_toggle_all(mask, SEL_DESELECT);
+ ED_mask_select_toggle_all(mask_orig, SEL_DESELECT);
changed = true;
}
/* do actual selection */
- LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) {
- if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
+ for (MaskLayer *mask_layer_orig = mask_orig->masklayers.first,
+ *mask_layer_eval = mask_eval->masklayers.first;
+ mask_layer_orig != NULL;
+ mask_layer_orig = mask_layer_orig->next, mask_layer_eval = mask_layer_eval->next) {
+ if (mask_layer_orig->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
+ for (MaskSpline *spline_orig = mask_layer_orig->splines.first,
+ *spline_eval = mask_layer_eval->splines.first;
+ spline_orig != NULL;
+ spline_orig = spline_orig->next, spline_eval = spline_eval->next) {
- LISTBASE_FOREACH (MaskSpline *, spline, &mask_layer->splines) {
- MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
+ MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline_eval);
- for (int i = 0; i < spline->tot_point; i++) {
- MaskSplinePoint *point = &spline->points[i];
+ for (int i = 0; i < spline_orig->tot_point; i++) {
+ MaskSplinePoint *point = &spline_orig->points[i];
MaskSplinePoint *point_deform = &points_array[i];
if (mask_spline_point_inside_ellipse(&point_deform->bezt, offset, ellipse)) {
@@ -696,10 +723,10 @@ static int circle_select_exec(bContext *C, wmOperator *op)
}
if (changed) {
- ED_mask_select_flush_all(mask);
+ ED_mask_select_flush_all(mask_orig);
- DEG_id_tag_update(&mask->id, ID_RECALC_SELECT);
- WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask);
+ DEG_id_tag_update(&mask_orig->id, ID_RECALC_SELECT);
+ WM_event_add_notifier(C, NC_MASK | ND_SELECT, mask_orig);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt
index 02feccc211a..97376a495c1 100644
--- a/source/blender/editors/object/CMakeLists.txt
+++ b/source/blender/editors/object/CMakeLists.txt
@@ -43,7 +43,7 @@ set(SRC
object_gpencil_modifier.c
object_hook.c
object_modes.c
- object_modifier.c
+ object_modifier.cc
object_ops.c
object_random.c
object_relations.c
diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c
index cc8644285c0..2518b2d201d 100644
--- a/source/blender/editors/object/object_edit.c
+++ b/source/blender/editors/object/object_edit.c
@@ -1828,13 +1828,6 @@ static bool move_to_collection_poll(bContext *C)
if (CTX_wm_space_outliner(C) != NULL) {
return ED_outliner_collections_editor_poll(C);
}
-
- View3D *v3d = CTX_wm_view3d(C);
-
- if (v3d && v3d->localvd) {
- return false;
- }
-
return ED_operator_objectmode(C);
}
diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h
index dd6e11abbf9..fb61200be9d 100644
--- a/source/blender/editors/object/object_intern.h
+++ b/source/blender/editors/object/object_intern.h
@@ -36,6 +36,7 @@ void OBJECT_OT_scale_clear(struct wmOperatorType *ot);
void OBJECT_OT_origin_clear(struct wmOperatorType *ot);
void OBJECT_OT_visual_transform_apply(struct wmOperatorType *ot);
void OBJECT_OT_transform_apply(struct wmOperatorType *ot);
+void OBJECT_OT_parent_inverse_apply(wmOperatorType *ot);
void OBJECT_OT_transform_axis_target(struct wmOperatorType *ot);
void OBJECT_OT_origin_set(struct wmOperatorType *ot);
@@ -200,6 +201,7 @@ void OBJECT_OT_skin_armature_create(struct wmOperatorType *ot);
void OBJECT_OT_laplaciandeform_bind(struct wmOperatorType *ot);
void OBJECT_OT_surfacedeform_bind(struct wmOperatorType *ot);
void OBJECT_OT_geometry_nodes_input_attribute_toggle(struct wmOperatorType *ot);
+void OBJECT_OT_geometry_node_tree_copy_assign(struct wmOperatorType *ot);
/* object_gpencil_modifiers.c */
diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.cc
index 545265b18b1..f7543c97903 100644
--- a/source/blender/editors/object/object_modifier.c
+++ b/source/blender/editors/object/object_modifier.cc
@@ -5,9 +5,9 @@
* \ingroup edobj
*/
-#include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
+#include <cmath>
+#include <cstdio>
+#include <cstdlib>
#include "MEM_guardedalloc.h"
@@ -142,19 +142,19 @@ static void object_force_modifier_bind_simple_options(Depsgraph *depsgraph,
ModifierData *ED_object_modifier_add(
ReportList *reports, Main *bmain, Scene *scene, Object *ob, const char *name, int type)
{
- ModifierData *md = NULL, *new_md = NULL;
- const ModifierTypeInfo *mti = BKE_modifier_get_info(type);
+ ModifierData *md = nullptr, *new_md = nullptr;
+ const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)type);
/* Check compatibility of modifier [T25291, T50373]. */
if (!BKE_object_support_modifier_type_check(ob, type)) {
BKE_reportf(reports, RPT_WARNING, "Modifiers cannot be added to object '%s'", ob->id.name + 2);
- return NULL;
+ return nullptr;
}
if (mti->flags & eModifierTypeFlag_Single) {
- if (BKE_modifiers_findby_type(ob, type)) {
+ if (BKE_modifiers_findby_type(ob, (ModifierType)type)) {
BKE_report(reports, RPT_WARNING, "Only one modifier of this type is allowed");
- return NULL;
+ return nullptr;
}
}
@@ -169,9 +169,10 @@ ModifierData *ED_object_modifier_add(
new_md = BKE_modifier_new(type);
if (mti->flags & eModifierTypeFlag_RequiresOriginalData) {
- md = ob->modifiers.first;
+ md = static_cast<ModifierData *>(ob->modifiers.first);
- while (md && BKE_modifier_get_info(md->type)->type == eModifierTypeType_OnlyDeform) {
+ while (md &&
+ BKE_modifier_get_info((ModifierType)md->type)->type == eModifierTypeType_OnlyDeform) {
md = md->next;
}
@@ -217,7 +218,7 @@ ModifierData *ED_object_modifier_add(
}
else if (type == eModifierType_Skin) {
/* ensure skin-node customdata exists */
- BKE_mesh_ensure_skin_customdata(ob->data);
+ BKE_mesh_ensure_skin_customdata(static_cast<Mesh *>(ob->data));
}
}
@@ -248,7 +249,7 @@ bool ED_object_iter_other(Main *bmain,
bool (*callback)(Object *ob, void *callback_data),
void *callback_data)
{
- ID *ob_data_id = orig_ob->data;
+ ID *ob_data_id = static_cast<ID *>(orig_ob->data);
int users = ob_data_id->us;
if (ob_data_id->flag & LIB_FAKEUSER) {
@@ -260,7 +261,8 @@ bool ED_object_iter_other(Main *bmain,
Object *ob;
int totfound = include_orig ? 0 : 1;
- for (ob = bmain->objects.first; ob && totfound < users; ob = ob->id.next) {
+ for (ob = static_cast<Object *>(bmain->objects.first); ob && totfound < users;
+ ob = reinterpret_cast<Object *>(ob->id.next)) {
if (((ob != orig_ob) || include_orig) && (ob->data == orig_ob->data)) {
if (callback(ob, callback_data)) {
return true;
@@ -281,7 +283,7 @@ static bool object_has_modifier_cb(Object *ob, void *data)
{
ModifierType type = *((ModifierType *)data);
- return object_has_modifier(ob, NULL, type);
+ return object_has_modifier(ob, nullptr, type);
}
bool ED_object_multires_update_totlevels_cb(Object *ob, void *totlevel_v)
@@ -342,7 +344,7 @@ static bool object_modifier_remove(
else if (md->type == eModifierType_Multires) {
/* Delete MDisps layer if not used by another multires modifier */
if (object_modifier_safe_to_delete(bmain, ob, md, eModifierType_Multires)) {
- multires_customdata_delete(ob->data);
+ multires_customdata_delete(static_cast<Mesh *>(ob->data));
}
}
else if (md->type == eModifierType_Skin) {
@@ -384,7 +386,7 @@ bool ED_object_modifier_remove(
void ED_object_modifier_clear(Main *bmain, Scene *scene, Object *ob)
{
- ModifierData *md = ob->modifiers.first;
+ ModifierData *md = static_cast<ModifierData *>(ob->modifiers.first);
bool sort_depsgraph = false;
if (!md) {
@@ -406,10 +408,10 @@ void ED_object_modifier_clear(Main *bmain, Scene *scene, Object *ob)
bool ED_object_modifier_move_up(ReportList *reports, Object *ob, ModifierData *md)
{
if (md->prev) {
- const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
+ const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
if (mti->type != eModifierTypeType_OnlyDeform) {
- const ModifierTypeInfo *nmti = BKE_modifier_get_info(md->prev->type);
+ const ModifierTypeInfo *nmti = BKE_modifier_get_info((ModifierType)md->prev->type);
if (nmti->flags & eModifierTypeFlag_RequiresOriginalData) {
BKE_report(reports, RPT_WARNING, "Cannot move above a modifier requiring original data");
@@ -430,10 +432,10 @@ bool ED_object_modifier_move_up(ReportList *reports, Object *ob, ModifierData *m
bool ED_object_modifier_move_down(ReportList *reports, Object *ob, ModifierData *md)
{
if (md->next) {
- const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
+ const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
if (mti->flags & eModifierTypeFlag_RequiresOriginalData) {
- const ModifierTypeInfo *nmti = BKE_modifier_get_info(md->next->type);
+ const ModifierTypeInfo *nmti = BKE_modifier_get_info((ModifierType)md->next->type);
if (nmti->type != eModifierTypeType_OnlyDeform) {
BKE_report(reports, RPT_WARNING, "Cannot move beyond a non-deforming modifier");
@@ -456,7 +458,7 @@ bool ED_object_modifier_move_to_index(ReportList *reports,
ModifierData *md,
const int index)
{
- BLI_assert(md != NULL);
+ BLI_assert(md != nullptr);
BLI_assert(index >= 0);
if (index >= BLI_listbase_count(&ob->modifiers)) {
BKE_report(reports, RPT_WARNING, "Cannot move modifier beyond the end of the stack");
@@ -534,7 +536,7 @@ bool ED_object_modifier_convert(ReportList *UNUSED(reports),
return false;
}
ParticleSystem *psys_eval = psys_eval_get(depsgraph, ob, psys_orig);
- if (psys_eval->pathcache == NULL) {
+ if (psys_eval->pathcache == nullptr) {
return false;
}
@@ -572,15 +574,15 @@ bool ED_object_modifier_convert(ReportList *UNUSED(reports),
}
/* add new mesh */
- Object *obn = BKE_object_add(bmain, view_layer, OB_MESH, NULL);
- Mesh *me = obn->data;
+ Object *obn = BKE_object_add(bmain, view_layer, OB_MESH, nullptr);
+ Mesh *me = static_cast<Mesh *>(obn->data);
me->totvert = verts_num;
me->totedge = edges_num;
- me->mvert = CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, NULL, verts_num);
- me->medge = CustomData_add_layer(&me->edata, CD_MEDGE, CD_CALLOC, NULL, edges_num);
- me->mface = CustomData_add_layer(&me->fdata, CD_MFACE, CD_CALLOC, NULL, 0);
+ me->mvert = (MVert *)CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, nullptr, verts_num);
+ me->medge = (MEdge *)CustomData_add_layer(&me->edata, CD_MEDGE, CD_CALLOC, nullptr, edges_num);
+ me->mface = (MFace *)CustomData_add_layer(&me->fdata, CD_MFACE, CD_CALLOC, nullptr, 0);
MVert *mvert = me->mvert;
MEdge *medge = me->medge;
@@ -650,9 +652,9 @@ static bool modifier_apply_shape(Main *bmain,
Object *ob,
ModifierData *md_eval)
{
- const ModifierTypeInfo *mti = BKE_modifier_get_info(md_eval->type);
+ const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md_eval->type);
- if (mti->isDisabled && mti->isDisabled(scene, md_eval, 0)) {
+ if (mti->isDisabled && mti->isDisabled(scene, md_eval, false)) {
BKE_report(reports, RPT_ERROR, "Modifier is disabled, skipping apply");
return false;
}
@@ -668,7 +670,7 @@ static bool modifier_apply_shape(Main *bmain,
* we can look into supporting them. */
if (ob->type == OB_MESH) {
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
Key *key = me->key;
if (!BKE_modifier_is_same_topology(md_eval) || mti->type == eModifierTypeType_NonGeometrical) {
@@ -683,22 +685,22 @@ static bool modifier_apply_shape(Main *bmain,
return false;
}
- if (key == NULL) {
+ if (key == nullptr) {
key = me->key = BKE_key_add(bmain, (ID *)me);
key->type = KEY_RELATIVE;
/* if that was the first key block added, then it was the basis.
* Initialize it with the mesh, and add another for the modifier */
- KeyBlock *kb = BKE_keyblock_add(key, NULL);
+ KeyBlock *kb = BKE_keyblock_add(key, nullptr);
BKE_keyblock_convert_from_mesh(me, key, kb);
}
KeyBlock *kb = BKE_keyblock_add(key, md_eval->name);
BKE_mesh_nomain_to_meshkey(mesh_applied, me, kb);
- BKE_id_free(NULL, mesh_applied);
+ BKE_id_free(nullptr, mesh_applied);
}
else {
- /* TODO: implement for hair, point clouds and volumes. */
+ /* TODO: implement for curves, point clouds and volumes. */
BKE_report(reports, RPT_ERROR, "Cannot apply modifier for this object type");
return false;
}
@@ -708,15 +710,15 @@ static bool modifier_apply_shape(Main *bmain,
static bool modifier_apply_obdata(
ReportList *reports, Depsgraph *depsgraph, Scene *scene, Object *ob, ModifierData *md_eval)
{
- const ModifierTypeInfo *mti = BKE_modifier_get_info(md_eval->type);
+ const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md_eval->type);
- if (mti->isDisabled && mti->isDisabled(scene, md_eval, 0)) {
+ if (mti->isDisabled && mti->isDisabled(scene, md_eval, false)) {
BKE_report(reports, RPT_ERROR, "Modifier is disabled, skipping apply");
return false;
}
if (ob->type == OB_MESH) {
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
MultiresModifierData *mmd = find_multires_modifier_before(scene, md_eval);
if (me->key && mti->type != eModifierTypeType_NonGeometrical) {
@@ -757,9 +759,9 @@ static bool modifier_apply_obdata(
}
else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) {
Object *object_eval = DEG_get_evaluated_object(depsgraph, ob);
- Curve *curve = ob->data;
- Curve *curve_eval = (Curve *)object_eval->data;
- ModifierEvalContext mectx = {depsgraph, object_eval, 0};
+ Curve *curve = static_cast<Curve *>(ob->data);
+ Curve *curve_eval = static_cast<Curve *>(object_eval->data);
+ ModifierEvalContext mectx = {depsgraph, object_eval, (ModifierApplyFlag)0};
if (ELEM(mti->type, eModifierTypeType_Constructive, eModifierTypeType_Nonconstructive)) {
BKE_report(
@@ -773,7 +775,7 @@ static bool modifier_apply_obdata(
int verts_num;
float(*vertexCos)[3] = BKE_curve_nurbs_vert_coords_alloc(&curve_eval->nurb, &verts_num);
- mti->deformVerts(md_eval, &mectx, NULL, vertexCos, verts_num);
+ mti->deformVerts(md_eval, &mectx, nullptr, vertexCos, verts_num);
BKE_curve_nurbs_vert_coords_apply(&curve->nurb, vertexCos, false);
MEM_freeN(vertexCos);
@@ -782,8 +784,8 @@ static bool modifier_apply_obdata(
}
else if (ob->type == OB_LATTICE) {
Object *object_eval = DEG_get_evaluated_object(depsgraph, ob);
- Lattice *lattice = ob->data;
- ModifierEvalContext mectx = {depsgraph, object_eval, 0};
+ Lattice *lattice = static_cast<Lattice *>(ob->data);
+ ModifierEvalContext mectx = {depsgraph, object_eval, (ModifierApplyFlag)0};
if (ELEM(mti->type, eModifierTypeType_Constructive, eModifierTypeType_Nonconstructive)) {
BKE_report(reports, RPT_ERROR, "Constructive modifiers cannot be applied");
@@ -792,7 +794,7 @@ static bool modifier_apply_obdata(
int verts_num;
float(*vertexCos)[3] = BKE_lattice_vert_coords_alloc(lattice, &verts_num);
- mti->deformVerts(md_eval, &mectx, NULL, vertexCos, verts_num);
+ mti->deformVerts(md_eval, &mectx, nullptr, vertexCos, verts_num);
BKE_lattice_vert_coords_apply(lattice, vertexCos);
MEM_freeN(vertexCos);
@@ -800,7 +802,7 @@ static bool modifier_apply_obdata(
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
}
else {
- /* TODO: implement for hair, point clouds and volumes. */
+ /* TODO: implement for curves, point clouds and volumes. */
BKE_report(reports, RPT_ERROR, "Cannot apply modifier for this object type");
return false;
}
@@ -918,7 +920,7 @@ static int modifier_add_exec(bContext *C, wmOperator *op)
Object *ob = ED_object_active_context(C);
int type = RNA_enum_get(op->ptr, "type");
- if (!ED_object_modifier_add(op->reports, bmain, scene, ob, NULL, type)) {
+ if (!ED_object_modifier_add(op->reports, bmain, scene, ob, nullptr, type)) {
return OPERATOR_CANCELLED;
}
@@ -938,15 +940,15 @@ static const EnumPropertyItem *modifier_add_itemf(bContext *C,
return rna_enum_object_modifier_type_items;
}
- EnumPropertyItem *items = NULL;
+ EnumPropertyItem *items = nullptr;
int totitem = 0;
- const EnumPropertyItem *group_item = NULL;
+ const EnumPropertyItem *group_item = nullptr;
for (int a = 0; rna_enum_object_modifier_type_items[a].identifier; a++) {
const EnumPropertyItem *md_item = &rna_enum_object_modifier_type_items[a];
if (md_item->identifier[0]) {
- const ModifierTypeInfo *mti = BKE_modifier_get_info(md_item->value);
+ const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md_item->value);
if (mti->flags & eModifierTypeFlag_NoUserAdd) {
continue;
@@ -963,7 +965,7 @@ static const EnumPropertyItem *modifier_add_itemf(bContext *C,
if (group_item) {
RNA_enum_item_add(&items, &totitem, group_item);
- group_item = NULL;
+ group_item = nullptr;
}
RNA_enum_item_add(&items, &totitem, md_item);
@@ -1016,9 +1018,9 @@ bool edit_modifier_poll_generic(bContext *C,
Main *bmain = CTX_data_main(C);
PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", rna_type);
Object *ob = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C);
- ModifierData *mod = ptr.data; /* May be NULL. */
+ ModifierData *mod = static_cast<ModifierData *>(ptr.data); /* May be nullptr. */
- if (mod == NULL && ob != NULL) {
+ if (mod == nullptr && ob != nullptr) {
mod = BKE_object_active_modifier(ob);
}
@@ -1038,7 +1040,7 @@ bool edit_modifier_poll_generic(bContext *C,
return false;
}
- if (!is_editmode_allowed && CTX_data_edit_object(C) != NULL) {
+ if (!is_editmode_allowed && CTX_data_edit_object(C) != nullptr) {
CTX_wm_operator_poll_msg_set(C, "This modifier operation is not allowed from Edit mode");
return false;
}
@@ -1061,7 +1063,7 @@ static bool edit_modifier_liboverride_allowed_poll(bContext *C)
void edit_modifier_properties(wmOperatorType *ot)
{
PropertyRNA *prop = RNA_def_string(
- ot->srna, "modifier", NULL, MAX_NAME, "Modifier", "Name of the modifier to edit");
+ ot->srna, "modifier", nullptr, MAX_NAME, "Modifier", "Name of the modifier to edit");
RNA_def_property_flag(prop, PROP_HIDDEN);
}
@@ -1087,8 +1089,8 @@ bool edit_modifier_invoke_properties(bContext *C, wmOperator *op)
}
PointerRNA ctx_ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_Modifier);
- if (ctx_ptr.data != NULL) {
- ModifierData *md = ctx_ptr.data;
+ if (ctx_ptr.data != nullptr) {
+ ModifierData *md = static_cast<ModifierData *>(ctx_ptr.data);
RNA_string_set(op->ptr, "modifier", md->name);
return true;
}
@@ -1112,14 +1114,14 @@ static bool edit_modifier_invoke_properties_with_hover(bContext *C,
/* Note that the context pointer is *not* the active modifier, it is set in UI layouts. */
PointerRNA ctx_ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_Modifier);
- if (ctx_ptr.data != NULL) {
- ModifierData *md = ctx_ptr.data;
+ if (ctx_ptr.data != nullptr) {
+ ModifierData *md = static_cast<ModifierData *>(ctx_ptr.data);
RNA_string_set(op->ptr, "modifier", md->name);
return true;
}
PointerRNA *panel_ptr = UI_region_panel_custom_data_under_cursor(C, event);
- if (panel_ptr == NULL || RNA_pointer_is_null(panel_ptr)) {
+ if (panel_ptr == nullptr || RNA_pointer_is_null(panel_ptr)) {
*r_retval = OPERATOR_CANCELLED;
return false;
}
@@ -1132,7 +1134,7 @@ static bool edit_modifier_invoke_properties_with_hover(bContext *C,
return false;
}
- const ModifierData *md = panel_ptr->data;
+ const ModifierData *md = static_cast<const ModifierData *>(panel_ptr->data);
RNA_string_set(op->ptr, "modifier", md->name);
return true;
}
@@ -1145,7 +1147,7 @@ ModifierData *edit_modifier_property_get(wmOperator *op, Object *ob, int type)
ModifierData *md = BKE_modifiers_findby_name(ob, modifier_name);
if (md && type != 0 && md->type != type) {
- md = NULL;
+ md = nullptr;
}
return md;
@@ -1166,7 +1168,7 @@ static int modifier_remove_exec(bContext *C, wmOperator *op)
ModifierData *md = edit_modifier_property_get(op, ob, 0);
int mode_orig = ob->mode;
- if (md == NULL) {
+ if (md == nullptr) {
return OPERATOR_CANCELLED;
}
@@ -1184,7 +1186,7 @@ static int modifier_remove_exec(bContext *C, wmOperator *op)
if (mode_orig & OB_MODE_PARTICLE_EDIT) {
if ((ob->mode & OB_MODE_PARTICLE_EDIT) == 0) {
if (ob == OBACT(view_layer)) {
- WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_OBJECT, NULL);
+ WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_OBJECT, nullptr);
}
}
}
@@ -1371,14 +1373,14 @@ static bool modifier_apply_poll(bContext *C)
Scene *scene = CTX_data_scene(C);
PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_Modifier);
- Object *ob = (ptr.owner_id != NULL) ? (Object *)ptr.owner_id : ED_object_active_context(C);
- ModifierData *md = ptr.data; /* May be NULL. */
+ Object *ob = (ptr.owner_id != nullptr) ? (Object *)ptr.owner_id : ED_object_active_context(C);
+ ModifierData *md = static_cast<ModifierData *>(ptr.data); /* May be nullptr. */
- if (ID_IS_OVERRIDE_LIBRARY(ob) || ((ob->data != NULL) && ID_IS_OVERRIDE_LIBRARY(ob->data))) {
+ if (ID_IS_OVERRIDE_LIBRARY(ob) || ((ob->data != nullptr) && ID_IS_OVERRIDE_LIBRARY(ob->data))) {
CTX_wm_operator_poll_msg_set(C, "Modifiers cannot be applied on override data");
return false;
}
- if (md != NULL) {
+ if (md != nullptr) {
if ((ob->mode & OB_MODE_SCULPT) && (find_multires_modifier_before(scene, md)) &&
(BKE_modifier_is_same_topology(md) == false)) {
CTX_wm_operator_poll_msg_set(
@@ -1399,14 +1401,14 @@ static int modifier_apply_exec_ex(bContext *C, wmOperator *op, int apply_as, boo
const bool do_report = RNA_boolean_get(op->ptr, "report");
const bool do_single_user = RNA_boolean_get(op->ptr, "single_user");
- if (md == NULL) {
+ if (md == nullptr) {
return OPERATOR_CANCELLED;
}
if (do_single_user && ID_REAL_USERS(ob->data) > 1) {
ED_object_single_obdata_user(bmain, scene, ob);
BKE_main_id_newptr_and_tag_clear(bmain);
- WM_event_add_notifier(C, NC_WINDOW, NULL);
+ WM_event_add_notifier(C, NC_WINDOW, nullptr);
DEG_relations_tag_update(bmain);
}
@@ -1447,9 +1449,9 @@ static int modifier_apply_invoke(bContext *C, wmOperator *op, const wmEvent *eve
int retval;
if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) {
PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_Modifier);
- Object *ob = (ptr.owner_id != NULL) ? (Object *)ptr.owner_id : ED_object_active_context(C);
+ Object *ob = (ptr.owner_id != nullptr) ? (Object *)ptr.owner_id : ED_object_active_context(C);
- if ((ob->data != NULL) && ID_REAL_USERS(ob->data) > 1) {
+ if ((ob->data != nullptr) && ID_REAL_USERS(ob->data) > 1) {
PropertyRNA *prop = RNA_struct_find_property(op->ptr, "single_user");
if (!RNA_property_is_set(op->ptr, prop)) {
RNA_property_boolean_set(op->ptr, prop, true);
@@ -1485,7 +1487,7 @@ void OBJECT_OT_modifier_apply(wmOperatorType *ot)
false,
"Make Data Single User",
"Make the object's data single user if needed");
- RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE));
}
/** \} */
@@ -1525,7 +1527,7 @@ static char *modifier_apply_as_shapekey_get_description(struct bContext *UNUSED(
return BLI_strdup(TIP_("Apply modifier as a new shapekey and keep it in the stack"));
}
- return NULL;
+ return nullptr;
}
void OBJECT_OT_modifier_apply_as_shapekey(wmOperatorType *ot)
@@ -1703,7 +1705,7 @@ static int modifier_copy_to_selected_exec(bContext *C, wmOperator *op)
}
int num_copied = 0;
- const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
+ const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
if (ob == obact) {
@@ -1721,7 +1723,7 @@ static int modifier_copy_to_selected_exec(bContext *C, wmOperator *op)
}
if (mti->flags & eModifierTypeFlag_Single) {
- if (BKE_modifiers_findby_type(ob, md->type)) {
+ if (BKE_modifiers_findby_type(ob, (ModifierType)md->type)) {
BKE_reportf(op->reports,
RPT_WARNING,
"Modifier can only be added once to object '%s'",
@@ -1768,12 +1770,12 @@ static bool modifier_copy_to_selected_poll(bContext *C)
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_Modifier);
Object *obact = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C);
- ModifierData *md = ptr.data;
+ ModifierData *md = static_cast<ModifierData *>(ptr.data);
/* This just mirrors the check in #BKE_object_copy_modifier,
* but there is no reasoning for it there. */
if (md && ELEM(md->type, eModifierType_Hook, eModifierType_Collision)) {
- CTX_wm_operator_poll_msg_set(C, "Not supported for \"Collision\" or \"Hook\" modifiers");
+ CTX_wm_operator_poll_msg_set(C, R"(Not supported for "Collision" or "Hook" modifiers)");
return false;
}
@@ -1909,7 +1911,7 @@ static EnumPropertyItem prop_multires_subdivide_mode_type[] = {
0,
"Linear",
"Create a new level using linear interpolation of the sculpted displacement"},
- {0, NULL, 0, NULL, NULL},
+ {0, nullptr, 0, nullptr, nullptr},
};
static int multires_subdivide_exec(bContext *C, wmOperator *op)
@@ -1978,7 +1980,7 @@ void OBJECT_OT_multires_subdivide(wmOperatorType *ot)
static int multires_reshape_exec(bContext *C, wmOperator *op)
{
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- Object *ob = ED_object_active_context(C), *secondob = NULL;
+ Object *ob = ED_object_active_context(C), *secondob = nullptr;
MultiresModifierData *mmd = (MultiresModifierData *)edit_modifier_property_get(
op, ob, eModifierType_Multires);
@@ -2048,7 +2050,7 @@ static int multires_external_save_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Object *ob = ED_object_active_context(C);
- Mesh *me = (ob) ? ob->data : op->customdata;
+ Mesh *me = (ob) ? static_cast<Mesh *>(ob->data) : static_cast<Mesh *>(op->customdata);
char path[FILE_MAX];
const bool relative = RNA_boolean_get(op->ptr, "relative_path");
@@ -2075,7 +2077,7 @@ static int multires_external_save_exec(bContext *C, wmOperator *op)
static int multires_external_save_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
Object *ob = ED_object_active_context(C);
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
char path[FILE_MAX];
if (!edit_modifier_invoke_properties(C, op)) {
@@ -2140,7 +2142,7 @@ void OBJECT_OT_multires_external_save(wmOperatorType *ot)
static int multires_external_pack_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *ob = ED_object_active_context(C);
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
if (!CustomData_external_test(&me->ldata, CD_MDISPS)) {
return OPERATOR_CANCELLED;
@@ -2334,7 +2336,7 @@ void OBJECT_OT_multires_rebuild_subdiv(wmOperatorType *ot)
static void modifier_skin_customdata_delete(Object *ob)
{
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
BMEditMesh *em = me->edit_mesh;
if (em) {
@@ -2353,7 +2355,7 @@ static bool skin_poll(bContext *C)
static bool skin_edit_poll(bContext *C)
{
Object *ob = CTX_data_edit_object(C);
- return (ob != NULL &&
+ return (ob != nullptr &&
edit_modifier_poll_generic(C, &RNA_SkinModifier, (1 << OB_MESH), true, false) &&
!ID_IS_OVERRIDE_LIBRARY(ob) && !ID_IS_OVERRIDE_LIBRARY(ob->data));
}
@@ -2367,7 +2369,7 @@ static void skin_root_clear(BMVert *bm_vert, GSet *visited, const int cd_vert_sk
BMVert *v2 = BM_edge_other_vert(bm_edge, bm_vert);
if (BLI_gset_add(visited, v2)) {
- MVertSkin *vs = BM_ELEM_CD_GET_VOID_P(v2, cd_vert_skin_offset);
+ MVertSkin *vs = static_cast<MVertSkin *>(BM_ELEM_CD_GET_VOID_P(v2, cd_vert_skin_offset));
/* clear vertex root flag and add to visited set */
vs->flag &= ~MVERT_SKIN_ROOT;
@@ -2385,7 +2387,7 @@ static int skin_root_mark_exec(bContext *C, wmOperator *UNUSED(op))
GSet *visited = BLI_gset_ptr_new(__func__);
- BKE_mesh_ensure_skin_customdata(ob->data);
+ BKE_mesh_ensure_skin_customdata(static_cast<Mesh *>(ob->data));
const int cd_vert_skin_offset = CustomData_get_offset(&bm->vdata, CD_MVERT_SKIN);
@@ -2393,7 +2395,8 @@ static int skin_root_mark_exec(bContext *C, wmOperator *UNUSED(op))
BMIter bm_iter;
BM_ITER_MESH (bm_vert, &bm_iter, bm, BM_VERTS_OF_MESH) {
if (BM_elem_flag_test(bm_vert, BM_ELEM_SELECT) && BLI_gset_add(visited, bm_vert)) {
- MVertSkin *vs = BM_ELEM_CD_GET_VOID_P(bm_vert, cd_vert_skin_offset);
+ MVertSkin *vs = static_cast<MVertSkin *>(
+ BM_ELEM_CD_GET_VOID_P(bm_vert, cd_vert_skin_offset));
/* mark vertex as root and add to visited set */
vs->flag |= MVERT_SKIN_ROOT;
@@ -2403,7 +2406,7 @@ static int skin_root_mark_exec(bContext *C, wmOperator *UNUSED(op))
}
}
- BLI_gset_free(visited, NULL);
+ BLI_gset_free(visited, nullptr);
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
@@ -2424,17 +2427,17 @@ void OBJECT_OT_skin_root_mark(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-typedef enum {
+enum SkinLooseAction {
SKIN_LOOSE_MARK,
SKIN_LOOSE_CLEAR,
-} SkinLooseAction;
+};
static int skin_loose_mark_clear_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_edit_object(C);
BMEditMesh *em = BKE_editmesh_from_object(ob);
BMesh *bm = em->bm;
- SkinLooseAction action = RNA_enum_get(op->ptr, "action");
+ SkinLooseAction action = static_cast<SkinLooseAction>(RNA_enum_get(op->ptr, "action"));
if (!CustomData_has_layer(&bm->vdata, CD_MVERT_SKIN)) {
return OPERATOR_CANCELLED;
@@ -2444,7 +2447,8 @@ static int skin_loose_mark_clear_exec(bContext *C, wmOperator *op)
BMIter bm_iter;
BM_ITER_MESH (bm_vert, &bm_iter, bm, BM_VERTS_OF_MESH) {
if (BM_elem_flag_test(bm_vert, BM_ELEM_SELECT)) {
- MVertSkin *vs = CustomData_bmesh_get(&bm->vdata, bm_vert->head.data, CD_MVERT_SKIN);
+ MVertSkin *vs = static_cast<MVertSkin *>(
+ CustomData_bmesh_get(&bm->vdata, bm_vert->head.data, CD_MVERT_SKIN));
switch (action) {
case SKIN_LOOSE_MARK:
@@ -2468,7 +2472,7 @@ void OBJECT_OT_skin_loose_mark_clear(wmOperatorType *ot)
static const EnumPropertyItem action_items[] = {
{SKIN_LOOSE_MARK, "MARK", 0, "Mark", "Mark selected vertices as loose"},
{SKIN_LOOSE_CLEAR, "CLEAR", 0, "Clear", "Set selected vertices as not loose"},
- {0, NULL, 0, NULL, NULL},
+ {0, nullptr, 0, nullptr, nullptr},
};
ot->name = "Skin Mark/Clear Loose";
@@ -2481,7 +2485,7 @@ void OBJECT_OT_skin_loose_mark_clear(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
- RNA_def_enum(ot->srna, "action", action_items, SKIN_LOOSE_MARK, "Action", NULL);
+ RNA_def_enum(ot->srna, "action", action_items, SKIN_LOOSE_MARK, "Action", nullptr);
}
static int skin_radii_equalize_exec(bContext *C, wmOperator *UNUSED(op))
@@ -2498,7 +2502,8 @@ static int skin_radii_equalize_exec(bContext *C, wmOperator *UNUSED(op))
BMIter bm_iter;
BM_ITER_MESH (bm_vert, &bm_iter, bm, BM_VERTS_OF_MESH) {
if (BM_elem_flag_test(bm_vert, BM_ELEM_SELECT)) {
- MVertSkin *vs = CustomData_bmesh_get(&bm->vdata, bm_vert->head.data, CD_MVERT_SKIN);
+ MVertSkin *vs = static_cast<MVertSkin *>(
+ CustomData_bmesh_get(&bm->vdata, bm_vert->head.data, CD_MVERT_SKIN));
float avg = (vs->radius[0] + vs->radius[1]) * 0.5f;
vs->radius[0] = vs->radius[1] = avg;
@@ -2548,7 +2553,7 @@ static void skin_armature_bone_create(Object *skin_ob,
EditBone *bone = ED_armature_ebone_add(arm, "Bone");
bone->parent = parent_bone;
- if (parent_bone != NULL) {
+ if (parent_bone != nullptr) {
bone->flag |= BONE_CONNECTED;
}
@@ -2559,7 +2564,7 @@ static void skin_armature_bone_create(Object *skin_ob,
/* add bDeformGroup */
bDeformGroup *dg = BKE_object_defgroup_add_name(skin_ob, bone->name);
- if (dg != NULL) {
+ if (dg != nullptr) {
ED_vgroup_vert_add(skin_ob, dg, parent_v, 1, WEIGHT_REPLACE);
ED_vgroup_vert_add(skin_ob, dg, v, 1, WEIGHT_REPLACE);
}
@@ -2570,7 +2575,7 @@ static void skin_armature_bone_create(Object *skin_ob,
static Object *modifier_skin_armature_create(Depsgraph *depsgraph, Main *bmain, Object *skin_ob)
{
- Mesh *me = skin_ob->data;
+ Mesh *me = static_cast<Mesh *>(skin_ob->data);
Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
Object *ob_eval = DEG_get_evaluated_object(depsgraph, skin_ob);
@@ -2579,18 +2584,19 @@ static Object *modifier_skin_armature_create(Depsgraph *depsgraph, Main *bmain,
MVert *mvert = me_eval_deform->mvert;
/* add vertex weights to original mesh */
- CustomData_add_layer(&me->vdata, CD_MDEFORMVERT, CD_CALLOC, NULL, me->totvert);
+ CustomData_add_layer(&me->vdata, CD_MDEFORMVERT, CD_CALLOC, nullptr, me->totvert);
ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph);
- Object *arm_ob = BKE_object_add(bmain, view_layer, OB_ARMATURE, NULL);
+ Object *arm_ob = BKE_object_add(bmain, view_layer, OB_ARMATURE, nullptr);
BKE_object_transform_copy(arm_ob, skin_ob);
- bArmature *arm = arm_ob->data;
+ bArmature *arm = static_cast<bArmature *>(arm_ob->data);
arm->layer = 1;
arm_ob->dtx |= OB_DRAW_IN_FRONT;
arm->drawtype = ARM_LINE;
- arm->edbo = MEM_callocN(sizeof(ListBase), "edbo armature");
+ arm->edbo = MEM_cnew<ListBase>("edbo armature");
- MVertSkin *mvert_skin = CustomData_get_layer(&me->vdata, CD_MVERT_SKIN);
+ MVertSkin *mvert_skin = static_cast<MVertSkin *>(
+ CustomData_get_layer(&me->vdata, CD_MVERT_SKIN));
int *emap_mem;
MeshElemMap *emap;
BKE_mesh_vert_edge_map_create(&emap, &emap_mem, me->medge, me->totvert, me->totedge);
@@ -2601,7 +2607,7 @@ static Object *modifier_skin_armature_create(Depsgraph *depsgraph, Main *bmain,
* edit-armature functions to convert back to regular bones */
for (int v = 0; v < me->totvert; v++) {
if (mvert_skin[v].flag & MVERT_SKIN_ROOT) {
- EditBone *bone = NULL;
+ EditBone *bone = nullptr;
/* Unless the skin root has just one adjacent edge, create
* a fake root bone (have it going off in the Y direction
@@ -2637,7 +2643,7 @@ static int skin_armature_create_exec(bContext *C, wmOperator *op)
Main *bmain = CTX_data_main(C);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Object *ob = CTX_data_active_object(C);
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
ModifierData *skin_md;
if (!CustomData_has_layer(&me->vdata, CD_MVERT_SKIN)) {
@@ -2716,7 +2722,7 @@ static int correctivesmooth_bind_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- const bool is_bind = (csmd->bind_coords != NULL);
+ const bool is_bind = (csmd->bind_coords != nullptr);
MEM_SAFE_FREE(csmd->bind_coords);
MEM_SAFE_FREE(csmd->delta_cache.deltas);
@@ -2785,11 +2791,11 @@ static int meshdeform_bind_exec(bContext *C, wmOperator *op)
MeshDeformModifierData *mmd = (MeshDeformModifierData *)edit_modifier_property_get(
op, ob, eModifierType_MeshDeform);
- if (mmd == NULL) {
+ if (mmd == nullptr) {
return OPERATOR_CANCELLED;
}
- if (mmd->bindcagecos != NULL) {
+ if (mmd->bindcagecos != nullptr) {
MEM_SAFE_FREE(mmd->bindcagecos);
MEM_SAFE_FREE(mmd->dyngrid);
MEM_SAFE_FREE(mmd->dyninfluences);
@@ -2809,7 +2815,7 @@ static int meshdeform_bind_exec(bContext *C, wmOperator *op)
depsgraph, ob, &mmd->modifier);
mmd_eval->bindfunc = ED_mesh_deform_bind_callback;
object_force_modifier_bind_simple_options(depsgraph, ob, &mmd->modifier);
- mmd_eval->bindfunc = NULL;
+ mmd_eval->bindfunc = nullptr;
}
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
@@ -2905,7 +2911,7 @@ static bool ocean_bake_poll(bContext *C)
return edit_modifier_poll_generic(C, &RNA_OceanModifier, 0, true, false);
}
-typedef struct OceanBakeJob {
+struct OceanBakeJob {
/* from wmJob */
struct Object *owner;
short *stop, *do_update;
@@ -2914,12 +2920,12 @@ typedef struct OceanBakeJob {
struct OceanCache *och;
struct Ocean *ocean;
struct OceanModifierData *omd;
-} OceanBakeJob;
+};
static void oceanbake_free(void *customdata)
{
- OceanBakeJob *oj = customdata;
- MEM_freeN(oj);
+ OceanBakeJob *oj = static_cast<OceanBakeJob *>(customdata);
+ MEM_delete(oj);
}
/* called by oceanbake, only to check job 'stop' value */
@@ -2937,7 +2943,7 @@ static int oceanbake_breakjob(void *UNUSED(customdata))
/* called by oceanbake, wmJob sends notifier */
static void oceanbake_update(void *customdata, float progress, int *cancel)
{
- OceanBakeJob *oj = customdata;
+ OceanBakeJob *oj = static_cast<OceanBakeJob *>(customdata);
if (oceanbake_breakjob(oj)) {
*cancel = 1;
@@ -2949,7 +2955,7 @@ static void oceanbake_update(void *customdata, float progress, int *cancel)
static void oceanbake_startjob(void *customdata, short *stop, short *do_update, float *progress)
{
- OceanBakeJob *oj = customdata;
+ OceanBakeJob *oj = static_cast<OceanBakeJob *>(customdata);
oj->stop = stop;
oj->do_update = do_update;
@@ -2965,11 +2971,11 @@ static void oceanbake_startjob(void *customdata, short *stop, short *do_update,
static void oceanbake_endjob(void *customdata)
{
- OceanBakeJob *oj = customdata;
+ OceanBakeJob *oj = static_cast<OceanBakeJob *>(customdata);
if (oj->ocean) {
BKE_ocean_free(oj->ocean);
- oj->ocean = NULL;
+ oj->ocean = nullptr;
}
oj->omd->oceancache = oj->och;
@@ -3009,7 +3015,7 @@ static int ocean_bake_exec(bContext *C, wmOperator *op)
omd->foam_fade,
omd->resolution);
- och->time = MEM_mallocN(och->duration * sizeof(float), "foam bake time");
+ och->time = static_cast<float *>(MEM_mallocN(och->duration * sizeof(float), "foam bake time"));
int cfra = scene->r.cfra;
@@ -3057,7 +3063,7 @@ static int ocean_bake_exec(bContext *C, wmOperator *op)
"Ocean Simulation",
WM_JOB_PROGRESS,
WM_JOB_TYPE_OBJECT_SIM_OCEAN);
- OceanBakeJob *oj = MEM_callocN(sizeof(OceanBakeJob), "ocean bake job");
+ OceanBakeJob *oj = MEM_cnew<OceanBakeJob>("ocean bake job");
oj->owner = ob;
oj->ocean = ocean;
oj->och = och;
@@ -3065,7 +3071,7 @@ static int ocean_bake_exec(bContext *C, wmOperator *op)
WM_jobs_customdata_set(wm_job, oj, oceanbake_free);
WM_jobs_timer(wm_job, 0.1, NC_OBJECT | ND_MODIFIER, NC_OBJECT | ND_MODIFIER);
- WM_jobs_callbacks(wm_job, oceanbake_startjob, NULL, NULL, oceanbake_endjob);
+ WM_jobs_callbacks(wm_job, oceanbake_startjob, nullptr, nullptr, oceanbake_endjob);
WM_jobs_start(CTX_wm_manager(C), wm_job);
@@ -3115,7 +3121,7 @@ static int laplaciandeform_bind_exec(bContext *C, wmOperator *op)
LaplacianDeformModifierData *lmd = (LaplacianDeformModifierData *)edit_modifier_property_get(
op, ob, eModifierType_LaplacianDeform);
- if (lmd == NULL) {
+ if (lmd == nullptr) {
return OPERATOR_CANCELLED;
}
@@ -3137,11 +3143,11 @@ static int laplaciandeform_bind_exec(bContext *C, wmOperator *op)
/* This is hard to know from the modifier itself whether the evaluation is
* happening for binding or not. So we copy all the required data here. */
lmd->verts_num = lmd_eval->verts_num;
- if (lmd_eval->vertexco == NULL) {
+ if (lmd_eval->vertexco == nullptr) {
MEM_SAFE_FREE(lmd->vertexco);
}
else {
- lmd->vertexco = MEM_dupallocN(lmd_eval->vertexco);
+ lmd->vertexco = static_cast<float *>(MEM_dupallocN(lmd_eval->vertexco));
}
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
@@ -3192,7 +3198,7 @@ static int surfacedeform_bind_exec(bContext *C, wmOperator *op)
SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)edit_modifier_property_get(
op, ob, eModifierType_SurfaceDeform);
- if (smd == NULL) {
+ if (smd == nullptr) {
return OPERATOR_CANCELLED;
}
@@ -3258,7 +3264,7 @@ static int geometry_nodes_input_attribute_toggle_exec(bContext *C, wmOperator *o
char modifier_name[MAX_NAME];
RNA_string_get(op->ptr, "modifier_name", modifier_name);
NodesModifierData *nmd = (NodesModifierData *)BKE_modifiers_findby_name(ob, modifier_name);
- if (nmd == NULL) {
+ if (nmd == nullptr) {
return OPERATOR_CANCELLED;
}
@@ -3288,8 +3294,55 @@ void OBJECT_OT_geometry_nodes_input_attribute_toggle(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
- RNA_def_string(ot->srna, "prop_path", NULL, 0, "Prop Path", "");
- RNA_def_string(ot->srna, "modifier_name", NULL, MAX_NAME, "Modifier Name", "");
+ RNA_def_string(ot->srna, "prop_path", nullptr, 0, "Prop Path", "");
+ RNA_def_string(ot->srna, "modifier_name", nullptr, MAX_NAME, "Modifier Name", "");
+}
+
+/** \} */
+
+/* ------------------------------------------------------------------- */
+/** \name Copy and Assign Geometry Node Group operator
+ * \{ */
+
+static int geometry_node_tree_copy_assign_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Main *bmain = CTX_data_main(C);
+ Object *ob = ED_object_active_context(C);
+
+ ModifierData *md = BKE_object_active_modifier(ob);
+ if (md->type != eModifierType_Nodes) {
+ return OPERATOR_CANCELLED;
+ }
+
+ NodesModifierData *nmd = (NodesModifierData *)md;
+ bNodeTree *tree = nmd->node_group;
+ if (tree == nullptr) {
+ return OPERATOR_CANCELLED;
+ }
+
+ bNodeTree *new_tree = (bNodeTree *)BKE_id_copy_ex(
+ bmain, &tree->id, nullptr, LIB_ID_COPY_ACTIONS | LIB_ID_COPY_DEFAULT);
+
+ if (new_tree == nullptr) {
+ return OPERATOR_CANCELLED;
+ }
+
+ nmd->node_group = new_tree;
+ id_us_min(&tree->id);
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
+ return OPERATOR_FINISHED;
+}
+
+void OBJECT_OT_geometry_node_tree_copy_assign(wmOperatorType *ot)
+{
+ ot->name = "Copy Geometry Node Group";
+ ot->description = "Copy the active geometry node group and assign it to the active modifier";
+ ot->idname = "OBJECT_OT_geometry_node_tree_copy_assign";
+
+ ot->exec = geometry_node_tree_copy_assign_exec;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c
index cf5c349228f..9b21dabb4bb 100644
--- a/source/blender/editors/object/object_ops.c
+++ b/source/blender/editors/object/object_ops.c
@@ -33,6 +33,7 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_origin_clear);
WM_operatortype_append(OBJECT_OT_visual_transform_apply);
WM_operatortype_append(OBJECT_OT_transform_apply);
+ WM_operatortype_append(OBJECT_OT_parent_inverse_apply);
WM_operatortype_append(OBJECT_OT_transform_axis_target);
WM_operatortype_append(OBJECT_OT_origin_set);
@@ -132,6 +133,7 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_skin_radii_equalize);
WM_operatortype_append(OBJECT_OT_skin_armature_create);
WM_operatortype_append(OBJECT_OT_geometry_nodes_input_attribute_toggle);
+ WM_operatortype_append(OBJECT_OT_geometry_node_tree_copy_assign);
/* grease pencil modifiers */
WM_operatortype_append(OBJECT_OT_gpencil_modifier_add);
diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c
index 7be46bdb24b..5ef8e573e27 100644
--- a/source/blender/editors/object/object_relations.c
+++ b/source/blender/editors/object/object_relations.c
@@ -936,8 +936,19 @@ static int parent_set_invoke_menu(bContext *C, wmOperatorType *ot)
RNA_boolean_set(&opptr, "keep_transform", true);
#endif
- uiItemO(
- layout, IFACE_("Object (Without Inverse)"), ICON_NONE, "OBJECT_OT_parent_no_inverse_set");
+ uiItemBooleanO(layout,
+ IFACE_("Object (Without Inverse)"),
+ ICON_NONE,
+ "OBJECT_OT_parent_no_inverse_set",
+ "keep_transform",
+ 0);
+
+ uiItemBooleanO(layout,
+ IFACE_("Object (Keep Transform Without Inverse)"),
+ ICON_NONE,
+ "OBJECT_OT_parent_no_inverse_set",
+ "keep_transform",
+ 1);
struct {
bool mesh, gpencil;
@@ -1055,6 +1066,8 @@ static int parent_noinv_set_exec(bContext *C, wmOperator *op)
Main *bmain = CTX_data_main(C);
Object *par = ED_object_active_context(C);
+ const bool keep_transform = RNA_boolean_get(op->ptr, "keep_transform");
+
DEG_id_tag_update(&par->id, ID_RECALC_TRANSFORM);
/* context iterator */
@@ -1064,16 +1077,21 @@ static int parent_noinv_set_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_ERROR, "Loop in parents");
}
else {
- /* clear inverse matrix and also the object location */
- unit_m4(ob->parentinv);
- memset(ob->loc, 0, sizeof(float[3]));
-
/* set recalc flags */
DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
/* set parenting type for object - object only... */
ob->parent = par;
ob->partype = PAROBJECT; /* NOTE: DNA define, not operator property. */
+
+ if (keep_transform) {
+ BKE_object_apply_parent_inverse(ob);
+ continue;
+ }
+
+ /* clear inverse matrix and also the object location */
+ unit_m4(ob->parentinv);
+ memset(ob->loc, 0, sizeof(float[3]));
}
}
}
@@ -1100,6 +1118,12 @@ void OBJECT_OT_parent_no_inverse_set(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_boolean(ot->srna,
+ "keep_transform",
+ false,
+ "Keep Transform",
+ "Preserve the world transform throughout parenting");
}
/** \} */
diff --git a/source/blender/editors/object/object_select.c b/source/blender/editors/object/object_select.c
index 3ebf578ca6b..c3d8fb9cfe5 100644
--- a/source/blender/editors/object/object_select.c
+++ b/source/blender/editors/object/object_select.c
@@ -1449,7 +1449,7 @@ void OBJECT_OT_select_random(wmOperatorType *ot)
ot->idname = "OBJECT_OT_select_random";
/* api callbacks */
- /*ot->invoke = object_select_random_invoke XXX: need a number popup ;*/
+ // ot->invoke = object_select_random_invoke; /* TODO: need a number popup. */
ot->exec = object_select_random_exec;
ot->poll = objects_selectable_poll;
diff --git a/source/blender/editors/object/object_transform.cc b/source/blender/editors/object/object_transform.cc
index 3d4d543012d..976fe683f95 100644
--- a/source/blender/editors/object/object_transform.cc
+++ b/source/blender/editors/object/object_transform.cc
@@ -597,7 +597,7 @@ static bool apply_objects_internal_can_multiuser(bContext *C)
{
Object *obact = CTX_data_active_object(C);
- if (ELEM(NULL, obact, obact->data)) {
+ if (ELEM(nullptr, obact, obact->data)) {
return false;
}
@@ -1173,6 +1173,38 @@ void OBJECT_OT_transform_apply(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
+static int object_parent_inverse_apply_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) {
+ if (ob->parent == nullptr) {
+ continue;
+ }
+
+ DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
+ BKE_object_apply_parent_inverse(ob);
+ }
+ CTX_DATA_END;
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, nullptr);
+
+ return OPERATOR_FINISHED;
+}
+
+void OBJECT_OT_parent_inverse_apply(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Apply Parent Inverse";
+ ot->description = "Apply the object's parent inverse to the its data";
+ ot->idname = "OBJECT_OT_parent_inverse_apply";
+
+ /* api callbacks */
+ ot->exec = object_parent_inverse_apply_exec;
+ ot->poll = ED_operator_objectmode;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c
index eba647a1b17..0e03feba340 100644
--- a/source/blender/editors/physics/particle_edit.c
+++ b/source/blender/editors/physics/particle_edit.c
@@ -4693,10 +4693,6 @@ static int brush_edit_init(bContext *C, wmOperator *op)
BrushEdit *bedit;
float min[3], max[3];
- if (!WM_toolsystem_active_tool_is_brush(C)) {
- return 0;
- }
-
/* set the 'distance factor' for grabbing (used in comb etc) */
INIT_MINMAX(min, max);
PE_minmax(depsgraph, scene, view_layer, min, max);
@@ -5033,6 +5029,11 @@ static void brush_edit_cancel(bContext *UNUSED(C), wmOperator *op)
brush_edit_exit(op);
}
+static bool brush_edit_poll(bContext *C)
+{
+ return PE_poll_view3d(C) && WM_toolsystem_active_tool_is_brush(C);
+}
+
void PARTICLE_OT_brush_edit(wmOperatorType *ot)
{
/* identifiers */
@@ -5045,7 +5046,7 @@ void PARTICLE_OT_brush_edit(wmOperatorType *ot)
ot->invoke = brush_edit_invoke;
ot->modal = brush_edit_modal;
ot->cancel = brush_edit_cancel;
- ot->poll = PE_poll_view3d;
+ ot->poll = brush_edit_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
diff --git a/source/blender/editors/render/render_shading.cc b/source/blender/editors/render/render_shading.cc
index f5bd60df2b4..da2290f7372 100644
--- a/source/blender/editors/render/render_shading.cc
+++ b/source/blender/editors/render/render_shading.cc
@@ -1167,7 +1167,7 @@ void SCENE_OT_view_layer_add_lightgroup(wmOperatorType *ot)
ot->prop = RNA_def_string(ot->srna,
"name",
nullptr,
- sizeof(((ViewLayerLightgroup *)NULL)->name),
+ sizeof(((ViewLayerLightgroup *)nullptr)->name),
"Name",
"Name of newly created lightgroup");
}
@@ -2613,7 +2613,7 @@ static void copy_mtex_copybuf(ID *id)
}
if (mtex && *mtex) {
- memcpy(&mtexcopybuf, *mtex, sizeof(MTex));
+ mtexcopybuf = blender::dna::shallow_copy(**mtex);
mtexcopied = 1;
}
else {
@@ -2649,7 +2649,7 @@ static void paste_mtex_copybuf(ID *id)
id_us_min(&(*mtex)->tex->id);
}
- memcpy(*mtex, &mtexcopybuf, sizeof(MTex));
+ **mtex = blender::dna::shallow_copy(mtexcopybuf);
id_us_plus((ID *)mtexcopybuf.tex);
}
diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c
index 30bf23e0987..ad815f0d998 100644
--- a/source/blender/editors/screen/area.c
+++ b/source/blender/editors/screen/area.c
@@ -2176,12 +2176,12 @@ struct RegionTypeAlignInfo {
* Needed for detecting which header displays the space-type switcher.
*/
bool hidden;
- } by_type[RGN_TYPE_LEN];
+ } by_type[RGN_TYPE_NUM];
};
static void region_align_info_from_area(ScrArea *area, struct RegionTypeAlignInfo *r_align_info)
{
- for (int index = 0; index < RGN_TYPE_LEN; index++) {
+ for (int index = 0; index < RGN_TYPE_NUM; 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;
@@ -2189,7 +2189,7 @@ static void region_align_info_from_area(ScrArea *area, struct RegionTypeAlignInf
LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
const int index = region->regiontype;
- if ((uint)index < RGN_TYPE_LEN) {
+ if ((uint)index < RGN_TYPE_NUM) {
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;
}
@@ -2252,7 +2252,7 @@ static short region_alignment_from_header_and_tool_header_state(
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])
+ ARegion *region_by_type[RGN_TYPE_NUM])
{
/* Abbreviate access. */
const short header_alignment_src = region_align_info_src->by_type[RGN_TYPE_HEADER].alignment;
@@ -2365,12 +2365,12 @@ static void region_align_info_to_area_for_headers(
}
static void region_align_info_to_area(
- ScrArea *area, const struct RegionTypeAlignInfo region_align_info_src[RGN_TYPE_LEN])
+ ScrArea *area, const struct RegionTypeAlignInfo region_align_info_src[RGN_TYPE_NUM])
{
- ARegion *region_by_type[RGN_TYPE_LEN] = {NULL};
+ ARegion *region_by_type[RGN_TYPE_NUM] = {NULL};
LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
const int index = region->regiontype;
- if ((uint)index < RGN_TYPE_LEN) {
+ if ((uint)index < RGN_TYPE_NUM) {
region_by_type[index] = region;
}
}
@@ -2437,7 +2437,7 @@ void ED_area_newspace(bContext *C, ScrArea *area, int type, const bool skip_regi
*/
bool sync_header_alignment = false;
- struct RegionTypeAlignInfo region_align_info[RGN_TYPE_LEN];
+ struct RegionTypeAlignInfo region_align_info[RGN_TYPE_NUM];
if ((slold != NULL) && (slold->link_flag & SPACE_FLAG_TYPE_TEMPORARY) == 0) {
region_align_info_from_area(area, region_align_info);
sync_header_alignment = true;
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt
index b89cbcf87fa..abdae5c44f9 100644
--- a/source/blender/editors/sculpt_paint/CMakeLists.txt
+++ b/source/blender/editors/sculpt_paint/CMakeLists.txt
@@ -72,6 +72,7 @@ set(SRC
sculpt_multiplane_scrape.c
sculpt_ops.c
sculpt_paint_color.c
+ sculpt_paint_image.cc
sculpt_pose.c
sculpt_smooth.c
sculpt_transform.c
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc
index dcfda658bbd..992ac77803a 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc
@@ -74,431 +74,6 @@ using blender::bke::CurvesGeometry;
/** \name * SCULPT_CURVES_OT_brush_stroke
* \{ */
-class DensityAddOperation : public CurvesSculptStrokeOperation {
- private:
- /** Contains the root points of the curves that existed before this operation started. */
- KDTree_3d *old_kdtree_ = nullptr;
- /** Number of points in the kdtree above. */
- int old_kdtree_size_ = 0;
-
- /**
- * Indicates that the corresponding curve has already been created and can't be changed by this
- * operation anymore.
- */
- static constexpr int ExistsAlreadyIndex = INT32_MAX;
-
- struct NewPointsData {
- Vector<float3> bary_coords;
- Vector<int> looptri_indices;
- Vector<float3> positions;
- Vector<float3> normals;
- };
-
- public:
- ~DensityAddOperation() override
- {
- if (old_kdtree_ != nullptr) {
- BLI_kdtree_3d_free(old_kdtree_);
- }
- }
-
- void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override
- {
- Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(C);
- Scene &scene = *CTX_data_scene(C);
- Object &object = *CTX_data_active_object(C);
- ARegion *region = CTX_wm_region(C);
- View3D *v3d = CTX_wm_view3d(C);
-
- Curves &curves_id = *static_cast<Curves *>(object.data);
- CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
-
- if (curves_id.surface == nullptr || curves_id.surface->type != OB_MESH) {
- return;
- }
-
- const Object &surface_ob = *curves_id.surface;
- const Mesh &surface = *static_cast<const Mesh *>(surface_ob.data);
- const float4x4 surface_ob_mat = surface_ob.obmat;
- const float4x4 surface_ob_imat = surface_ob_mat.inverted();
-
- ToolSettings &tool_settings = *scene.toolsettings;
- CurvesSculpt &curves_sculpt = *tool_settings.curves_sculpt;
- Brush &brush = *BKE_paint_brush(&curves_sculpt.paint);
- const float brush_radius_screen = BKE_brush_size_get(&scene, &brush);
- const float strength = BKE_brush_alpha_get(&scene, &brush);
- const float minimum_distance = curves_sculpt.distance;
-
- /* This is the main ray that is used to determine the brush position in 3D space. */
- float3 ray_start, ray_end;
- ED_view3d_win_to_segment_clipped(
- &depsgraph, region, v3d, stroke_extension.mouse_position, ray_start, ray_end, true);
- ray_start = surface_ob_imat * ray_start;
- ray_end = surface_ob_imat * ray_end;
- const float3 ray_direction = math::normalize(ray_end - ray_start);
-
- /* This ray is used to determine the brush radius in 3d space. */
- float3 offset_ray_start, offset_ray_end;
- ED_view3d_win_to_segment_clipped(&depsgraph,
- region,
- v3d,
- stroke_extension.mouse_position +
- float2(0, brush_radius_screen),
- offset_ray_start,
- offset_ray_end,
- true);
- offset_ray_start = surface_ob_imat * offset_ray_start;
- offset_ray_end = surface_ob_imat * offset_ray_end;
-
- float4x4 ob_imat;
- invert_m4_m4(ob_imat.values, object.obmat);
-
- const float4x4 transform = ob_imat * surface_ob_mat;
-
- BVHTreeFromMesh bvhtree;
- BKE_bvhtree_from_mesh_get(&bvhtree, &surface, BVHTREE_FROM_LOOPTRI, 2);
-
- /* Do a raycast against the surface object to find the brush position. */
- BVHTreeRayHit ray_hit;
- ray_hit.dist = FLT_MAX;
- ray_hit.index = -1;
- BLI_bvhtree_ray_cast(bvhtree.tree,
- ray_start,
- ray_direction,
- 0.0f,
- &ray_hit,
- bvhtree.raycast_callback,
- &bvhtree);
-
- if (ray_hit.index == -1) {
- /* The ray did not hit the surface. */
- free_bvhtree_from_mesh(&bvhtree);
- return;
- }
- /* Brush position in the space of the surface object. */
- const float3 brush_pos_3d_surface = ray_hit.co;
- const float brush_radius_3d_surface = dist_to_line_v3(
- brush_pos_3d_surface, offset_ray_start, offset_ray_end);
-
- /* Brush position in the space of the curves object. */
- const float3 brush_pos_3d_curves = transform * brush_pos_3d_surface;
- const float brush_radius_3d_curves = dist_to_line_v3(
- brush_pos_3d_curves, transform * offset_ray_start, transform * offset_ray_end);
-
- Vector<int> looptri_indices = this->find_looptri_indices_to_consider(
- bvhtree, brush_pos_3d_surface, brush_radius_3d_surface);
-
- free_bvhtree_from_mesh(&bvhtree);
-
- if (old_kdtree_ == nullptr && minimum_distance > 0.0f) {
- old_kdtree_ = this->kdtree_from_curve_roots_and_positions(curves, curves.curves_range(), {});
- old_kdtree_size_ = curves.curves_num();
- }
-
- float density;
- if (minimum_distance > 0.0f) {
- /* Estimate the sampling density based on the target minimum distance. */
- density = strength * pow2f(1.0f / minimum_distance);
- }
- else {
- /* Sample a somewhat constant amount of points based on the strength. */
- const float brush_circle_area_3d = M_PI * pow2f(brush_radius_3d_curves);
- density = strength * 100.0f / brush_circle_area_3d;
- }
-
- NewPointsData new_points = this->sample_new_points(density,
- minimum_distance,
- brush_radius_3d_curves,
- brush_pos_3d_curves,
- looptri_indices,
- transform,
- surface);
- if (minimum_distance > 0.0f) {
- this->eliminate_too_close_points(new_points, curves, minimum_distance);
- }
- this->insert_new_curves(new_points, curves);
-
- DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY);
- ED_region_tag_redraw(region);
- }
-
- private:
- Vector<int> find_looptri_indices_to_consider(BVHTreeFromMesh &bvhtree,
- const float3 &brush_pos,
- const float brush_radius_3d)
- {
- Vector<int> looptri_indices;
-
- struct RangeQueryUserData {
- Vector<int> &indices;
- } range_query_user_data = {looptri_indices};
-
- BLI_bvhtree_range_query(
- bvhtree.tree,
- brush_pos,
- brush_radius_3d,
- [](void *userdata, int index, const float co[3], float dist_sq) {
- UNUSED_VARS(co, dist_sq);
- RangeQueryUserData &data = *static_cast<RangeQueryUserData *>(userdata);
- data.indices.append(index);
- },
- &range_query_user_data);
-
- return looptri_indices;
- }
-
- KDTree_3d *kdtree_from_curve_roots_and_positions(const CurvesGeometry &curves,
- const IndexRange curves_range,
- Span<float3> extra_positions)
- {
- const int tot_points = curves_range.size() + extra_positions.size();
- KDTree_3d *kdtree = BLI_kdtree_3d_new(tot_points);
- for (const int curve_i : curves_range) {
- const int first_point_i = curves.offsets()[curve_i];
- const float3 root_position = curves.positions()[first_point_i];
- BLI_kdtree_3d_insert(kdtree, ExistsAlreadyIndex, root_position);
- }
- for (const int i : extra_positions.index_range()) {
- BLI_kdtree_3d_insert(kdtree, i, extra_positions[i]);
- }
- BLI_kdtree_3d_balance(kdtree);
- return kdtree;
- }
-
- bool is_too_close_to_existing_point(const float3 position, const float minimum_distance) const
- {
- if (old_kdtree_ == nullptr) {
- return false;
- }
- KDTreeNearest_3d nearest;
- nearest.index = -1;
- BLI_kdtree_3d_find_nearest(old_kdtree_, position, &nearest);
- if (nearest.index >= 0 && nearest.dist < minimum_distance) {
- return true;
- }
- return false;
- }
-
- NewPointsData sample_new_points(const float density,
- const float minimum_distance,
- const float brush_radius_3d,
- const float3 &brush_pos,
- const Span<int> looptri_indices,
- const float4x4 &transform,
- const Mesh &surface)
- {
- const float brush_radius_3d_sq = brush_radius_3d * brush_radius_3d;
- const float area_threshold = M_PI * brush_radius_3d_sq;
-
- const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&surface),
- BKE_mesh_runtime_looptri_len(&surface)};
-
- threading::EnumerableThreadSpecific<NewPointsData> new_points_per_thread;
-
- const double time = PIL_check_seconds_timer();
- const uint64_t time_as_int = *reinterpret_cast<const uint64_t *>(&time);
- const uint32_t rng_base_seed = time_as_int ^ (time_as_int >> 32);
-
- RandomNumberGenerator rng{rng_base_seed};
-
- threading::parallel_for(looptri_indices.index_range(), 512, [&](const IndexRange range) {
- RandomNumberGenerator looptri_rng{rng_base_seed + (uint32_t)range.start()};
-
- for (const int looptri_index : looptri_indices.slice(range)) {
- const MLoopTri &looptri = looptris[looptri_index];
- const float3 &v0 = transform * float3(surface.mvert[surface.mloop[looptri.tri[0]].v].co);
- const float3 &v1 = transform * float3(surface.mvert[surface.mloop[looptri.tri[1]].v].co);
- const float3 &v2 = transform * float3(surface.mvert[surface.mloop[looptri.tri[2]].v].co);
- const float looptri_area = area_tri_v3(v0, v1, v2);
-
- float3 normal;
- normal_tri_v3(normal, v0, v1, v2);
-
- /* Use a different sampling strategy depending on whether the triangle is large or small
- * compared to the brush size. When the triangle is small, points are distributed within
- * the triangle directly. If the triangle is larger than the brush, distribute new points
- * in a circle on the triangle plane. */
- if (looptri_area < area_threshold) {
- const int amount = looptri_rng.round_probabilistic(looptri_area * density);
-
- threading::parallel_for(IndexRange(amount), 512, [&](const IndexRange amount_range) {
- RandomNumberGenerator point_rng{rng_base_seed + looptri_index * 1000 +
- (uint32_t)amount_range.start()};
- NewPointsData &new_points = new_points_per_thread.local();
-
- for ([[maybe_unused]] const int i : amount_range) {
- const float3 bary_coord = point_rng.get_barycentric_coordinates();
- const float3 point_pos = attribute_math::mix3(bary_coord, v0, v1, v2);
-
- if (math::distance(point_pos, brush_pos) > brush_radius_3d) {
- continue;
- }
- if (minimum_distance > 0.0f &&
- this->is_too_close_to_existing_point(point_pos, minimum_distance)) {
- continue;
- }
-
- new_points.bary_coords.append(bary_coord);
- new_points.looptri_indices.append(looptri_index);
- new_points.positions.append(point_pos);
- new_points.normals.append(normal);
- }
- });
- }
- else {
- float3 hit_pos_proj = brush_pos;
- project_v3_plane(hit_pos_proj, normal, v0);
- const float proj_distance_sq = math::distance_squared(hit_pos_proj, brush_pos);
- const float brush_radius_factor_sq = 1.0f -
- std::min(1.0f,
- proj_distance_sq / brush_radius_3d_sq);
- const float radius_proj_sq = brush_radius_3d_sq * brush_radius_factor_sq;
- const float radius_proj = std::sqrt(radius_proj_sq);
- const float circle_area = M_PI * radius_proj_sq;
-
- const int amount = rng.round_probabilistic(circle_area * density);
-
- const float3 axis_1 = math::normalize(v1 - v0) * radius_proj;
- const float3 axis_2 = math::normalize(
- math::cross(axis_1, math::cross(axis_1, v2 - v0))) *
- radius_proj;
-
- threading::parallel_for(IndexRange(amount), 512, [&](const IndexRange amount_range) {
- RandomNumberGenerator point_rng{rng_base_seed + looptri_index * 1000 +
- (uint32_t)amount_range.start()};
- NewPointsData &new_points = new_points_per_thread.local();
-
- for ([[maybe_unused]] const int i : amount_range) {
- const float r = std::sqrt(rng.get_float());
- const float angle = rng.get_float() * 2 * M_PI;
- const float x = r * std::cos(angle);
- const float y = r * std::sin(angle);
-
- const float3 point_pos = hit_pos_proj + axis_1 * x + axis_2 * y;
-
- if (!isect_point_tri_prism_v3(point_pos, v0, v1, v2)) {
- continue;
- }
- if (minimum_distance > 0.0f &&
- this->is_too_close_to_existing_point(point_pos, minimum_distance)) {
- continue;
- }
-
- float3 bary_coord;
- interp_weights_tri_v3(bary_coord, v0, v1, v2, point_pos);
-
- new_points.bary_coords.append(bary_coord);
- new_points.looptri_indices.append(looptri_index);
- new_points.positions.append(point_pos);
- new_points.normals.append(normal);
- }
- });
- }
- }
- });
-
- NewPointsData new_points;
- for (const NewPointsData &local_new_points : new_points_per_thread) {
- new_points.bary_coords.extend(local_new_points.bary_coords);
- new_points.looptri_indices.extend(local_new_points.looptri_indices);
- new_points.positions.extend(local_new_points.positions);
- new_points.normals.extend(local_new_points.normals);
- }
- return new_points;
- }
-
- void eliminate_too_close_points(NewPointsData &points,
- const CurvesGeometry &curves,
- const float minimum_distance)
- {
- Array<bool> elimination_mask(points.positions.size(), false);
-
- const int curves_added_previously = curves.curves_num() - old_kdtree_size_;
- KDTree_3d *new_points_kdtree = this->kdtree_from_curve_roots_and_positions(
- curves, IndexRange(old_kdtree_size_, curves_added_previously), points.positions);
-
- Array<Vector<int>> points_in_range(points.positions.size());
- threading::parallel_for(points.positions.index_range(), 256, [&](const IndexRange range) {
- for (const int point_i : range) {
- const float3 query_position = points.positions[point_i];
-
- struct CallbackData {
- int point_i;
- Vector<int> &found_indices;
- MutableSpan<bool> elimination_mask;
- } callback_data = {point_i, points_in_range[point_i], elimination_mask};
-
- BLI_kdtree_3d_range_search_cb(
- new_points_kdtree,
- query_position,
- minimum_distance,
- [](void *user_data, int index, const float *UNUSED(co), float UNUSED(dist_sq)) {
- CallbackData &data = *static_cast<CallbackData *>(user_data);
- if (index == data.point_i) {
- /* Ignore self. */
- return true;
- }
- if (index == ExistsAlreadyIndex) {
- /* An already existing point is too close, so this new point will be eliminated. */
- data.elimination_mask[data.point_i] = true;
- return false;
- }
- data.found_indices.append(index);
- return true;
- },
- &callback_data);
- }
- });
-
- for (const int point_i : points.positions.index_range()) {
- if (elimination_mask[point_i]) {
- /* Point is eliminated already. */
- continue;
- }
-
- for (const int other_point_i : points_in_range[point_i]) {
- elimination_mask[other_point_i] = true;
- }
- }
-
- BLI_kdtree_3d_free(new_points_kdtree);
- for (int i = points.positions.size() - 1; i >= 0; i--) {
- if (elimination_mask[i]) {
- points.positions.remove_and_reorder(i);
- points.bary_coords.remove_and_reorder(i);
- points.looptri_indices.remove_and_reorder(i);
- points.normals.remove_and_reorder(i);
- }
- }
- }
-
- void insert_new_curves(const NewPointsData &new_points, CurvesGeometry &curves)
- {
- const int tot_new_curves = new_points.positions.size();
-
- const int points_per_curve = 8;
- curves.resize(curves.points_num() + tot_new_curves * points_per_curve,
- curves.curves_num() + tot_new_curves);
-
- MutableSpan<int> offsets = curves.offsets_for_write();
- MutableSpan<float3> positions = curves.positions_for_write();
-
- for (const int i : IndexRange(tot_new_curves)) {
- const int curve_i = curves.curves_num() - tot_new_curves + i;
- const int first_point_i = offsets[curve_i];
- offsets[curve_i + 1] = offsets[curve_i] + points_per_curve;
-
- const float3 root = new_points.positions[i];
- const float3 tip = root + 0.1f * new_points.normals[i];
-
- for (const int j : IndexRange(points_per_curve)) {
- positions[first_point_i + j] = math::interpolate(
- root, tip, j / (float)(points_per_curve - 1));
- }
- }
- }
-};
-
static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bContext *C,
wmOperator *op)
{
@@ -518,8 +93,6 @@ static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bConte
return new_add_operation();
case CURVES_SCULPT_TOOL_GROW_SHRINK:
return new_grow_shrink_operation(mode, C);
- case CURVES_SCULPT_TOOL_TEST1:
- return std::make_unique<DensityAddOperation>();
}
BLI_assert_unreachable();
return {};
diff --git a/source/blender/editors/sculpt_paint/paint_canvas.cc b/source/blender/editors/sculpt_paint/paint_canvas.cc
index 5683e3ff741..9262cbebcac 100644
--- a/source/blender/editors/sculpt_paint/paint_canvas.cc
+++ b/source/blender/editors/sculpt_paint/paint_canvas.cc
@@ -2,20 +2,11 @@
#include "BLI_compiler_compat.h"
#include "DNA_material_types.h"
-#include "DNA_mesh_types.h"
-#include "DNA_node_types.h"
-#include "DNA_screen_types.h"
+#include "DNA_scene_types.h"
#include "DNA_workspace_types.h"
-#include "BKE_context.h"
-#include "BKE_customdata.h"
#include "BKE_material.h"
#include "BKE_paint.h"
-#include "BKE_pbvh.h"
-
-#include "DEG_depsgraph.h"
-
-#include "NOD_shader.h"
#include "WM_toolsystem.h"
@@ -43,16 +34,15 @@ static TexPaintSlot *get_active_slot(Object *ob)
extern "C" {
-using namespace blender;
using namespace blender::ed::sculpt_paint::canvas;
/* Does the paint tool with the given idname uses a canvas. */
-static bool paint_tool_uses_canvas(StringRef idname)
+static bool paint_tool_uses_canvas(blender::StringRef idname)
{
return ELEM(idname, "builtin_brush.Paint", "builtin_brush.Smear", "builtin.color_filter");
}
-static bool paint_tool_shading_color_follows_last_used(StringRef idname)
+static bool paint_tool_shading_color_follows_last_used(blender::StringRef idname)
{
/* TODO(jbakker): complete this list. */
return ELEM(idname, "builtin_brush.Mask");
@@ -147,59 +137,4 @@ eV3DShadingColorType ED_paint_shading_color_override(bContext *C,
return color_type;
}
-
-Image *ED_paint_canvas_image_get(const struct PaintModeSettings *settings, struct Object *ob)
-{
- switch (settings->canvas_source) {
- case PAINT_CANVAS_SOURCE_COLOR_ATTRIBUTE:
- return nullptr;
- case PAINT_CANVAS_SOURCE_IMAGE:
- return settings->canvas_image;
- case PAINT_CANVAS_SOURCE_MATERIAL: {
- TexPaintSlot *slot = get_active_slot(ob);
- if (slot == nullptr) {
- break;
- }
- return slot->ima;
- }
- }
- return nullptr;
-}
-
-int ED_paint_canvas_uvmap_layer_index_get(const struct PaintModeSettings *settings,
- struct Object *ob)
-{
- switch (settings->canvas_source) {
- case PAINT_CANVAS_SOURCE_COLOR_ATTRIBUTE:
- return -1;
- case PAINT_CANVAS_SOURCE_IMAGE: {
- /* Use active uv map of the object. */
- if (ob->type != OB_MESH) {
- return -1;
- }
-
- const Mesh *mesh = static_cast<Mesh *>(ob->data);
- return CustomData_get_active_layer_index(&mesh->ldata, CD_MLOOPUV);
- }
- case PAINT_CANVAS_SOURCE_MATERIAL: {
- /* Use uv map of the canvas. */
- TexPaintSlot *slot = get_active_slot(ob);
- if (slot == nullptr) {
- break;
- }
-
- if (ob->type != OB_MESH) {
- return -1;
- }
-
- if (slot->uvname == nullptr) {
- return -1;
- }
-
- const Mesh *mesh = static_cast<Mesh *>(ob->data);
- return CustomData_get_named_layer_index(&mesh->ldata, CD_MLOOPUV, slot->uvname);
- }
- }
- return -1;
-}
}
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index 24c5a9fce52..bbbed32e316 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -2188,7 +2188,8 @@ void SCULPT_calc_area_normal_and_center(
static float brush_strength(const Sculpt *sd,
const StrokeCache *cache,
const float feather,
- const UnifiedPaintSettings *ups)
+ const UnifiedPaintSettings *ups,
+ const PaintModeSettings *UNUSED(paint_mode_settings))
{
const Scene *scene = cache->vc->scene;
const Brush *brush = BKE_paint_brush((Paint *)&sd->paint);
@@ -2750,6 +2751,41 @@ static void update_brush_local_mat(Sculpt *sd, Object *ob)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Texture painting
+ * \{ */
+
+static bool sculpt_needs_pbvh_pixels(PaintModeSettings *paint_mode_settings,
+ const Brush *brush,
+ Object *ob)
+{
+ if (brush->sculpt_tool == SCULPT_TOOL_PAINT && U.experimental.use_sculpt_texture_paint) {
+ Image *image;
+ ImageUser *image_user;
+ return SCULPT_paint_image_canvas_get(paint_mode_settings, ob, &image, &image_user);
+ }
+
+ return false;
+}
+
+static void sculpt_pbvh_update_pixels(PaintModeSettings *paint_mode_settings,
+ SculptSession *ss,
+ Object *ob)
+{
+ BLI_assert(ob->type == OB_MESH);
+ Mesh *mesh = (Mesh *)ob->data;
+
+ Image *image;
+ ImageUser *image_user;
+ if (!SCULPT_paint_image_canvas_get(paint_mode_settings, ob, &image, &image_user)) {
+ return;
+ }
+
+ BKE_pbvh_build_pixels(ss->pbvh, mesh->mloop, &mesh->ldata, image, image_user);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Generic Brush Plane & Symmetry Utilities
* \{ */
@@ -3075,7 +3111,8 @@ void SCULPT_vertcos_to_key(Object *ob, KeyBlock *kb, const float (*vertCos)[3])
static void sculpt_topology_update(Sculpt *sd,
Object *ob,
Brush *brush,
- UnifiedPaintSettings *UNUSED(ups))
+ UnifiedPaintSettings *UNUSED(ups),
+ PaintModeSettings *UNUSED(paint_mode_settings))
{
SculptSession *ss = ob->sculpt;
@@ -3170,7 +3207,11 @@ static void do_brush_action_task_cb(void *__restrict userdata,
}
}
-static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSettings *ups)
+static void do_brush_action(Sculpt *sd,
+ Object *ob,
+ Brush *brush,
+ UnifiedPaintSettings *ups,
+ PaintModeSettings *paint_mode_settings)
{
SculptSession *ss = ob->sculpt;
int totnode;
@@ -3209,6 +3250,10 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
nodes = sculpt_pbvh_gather_generic(ob, sd, brush, use_original, radius_scale, &totnode);
}
+ if (sculpt_needs_pbvh_pixels(paint_mode_settings, brush, ob)) {
+ sculpt_pbvh_update_pixels(paint_mode_settings, ss, ob);
+ }
+
/* Draw Face Sets in draw mode makes a single undo push, in alt-smooth mode deforms the
* vertices and uses regular coords undo. */
/* It also assigns the paint_face_set here as it needs to be done regardless of the stroke type
@@ -3399,7 +3444,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
SCULPT_do_displacement_smear_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_PAINT:
- SCULPT_do_paint_brush(sd, ob, nodes, totnode);
+ SCULPT_do_paint_brush(paint_mode_settings, sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_SMEAR:
SCULPT_do_smear_brush(sd, ob, nodes, totnode);
@@ -3704,10 +3749,18 @@ void SCULPT_cache_calc_brushdata_symm(StrokeCache *cache,
}
}
-typedef void (*BrushActionFunc)(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSettings *ups);
+typedef void (*BrushActionFunc)(Sculpt *sd,
+ Object *ob,
+ Brush *brush,
+ UnifiedPaintSettings *ups,
+ PaintModeSettings *paint_mode_settings);
-static void do_tiled(
- Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSettings *ups, BrushActionFunc action)
+static void do_tiled(Sculpt *sd,
+ Object *ob,
+ Brush *brush,
+ UnifiedPaintSettings *ups,
+ PaintModeSettings *paint_mode_settings,
+ BrushActionFunc action)
{
SculptSession *ss = ob->sculpt;
StrokeCache *cache = ss->cache;
@@ -3741,7 +3794,7 @@ static void do_tiled(
/* First do the "un-tiled" position to initialize the stroke for this location. */
cache->tile_pass = 0;
- action(sd, ob, brush, ups);
+ action(sd, ob, brush, ups, paint_mode_settings);
/* Now do it for all the tiles. */
copy_v3_v3_int(cur, start);
@@ -3760,7 +3813,7 @@ static void do_tiled(
cache->plane_offset[dim] = cur[dim] * step[dim];
cache->initial_location[dim] = cur[dim] * step[dim] + original_initial_location[dim];
}
- action(sd, ob, brush, ups);
+ action(sd, ob, brush, ups, paint_mode_settings);
}
}
}
@@ -3770,6 +3823,7 @@ static void do_radial_symmetry(Sculpt *sd,
Object *ob,
Brush *brush,
UnifiedPaintSettings *ups,
+ PaintModeSettings *paint_mode_settings,
BrushActionFunc action,
const char symm,
const int axis,
@@ -3781,7 +3835,7 @@ static void do_radial_symmetry(Sculpt *sd,
const float angle = 2.0f * M_PI * i / sd->radial_symm[axis - 'X'];
ss->cache->radial_symmetry_pass = i;
SCULPT_cache_calc_brushdata_symm(ss->cache, symm, axis, angle);
- do_tiled(sd, ob, brush, ups, action);
+ do_tiled(sd, ob, brush, ups, paint_mode_settings, action);
}
}
@@ -3803,7 +3857,8 @@ static void sculpt_fix_noise_tear(Sculpt *sd, Object *ob)
static void do_symmetrical_brush_actions(Sculpt *sd,
Object *ob,
BrushActionFunc action,
- UnifiedPaintSettings *ups)
+ UnifiedPaintSettings *ups,
+ PaintModeSettings *paint_mode_settings)
{
Brush *brush = BKE_paint_brush(&sd->paint);
SculptSession *ss = ob->sculpt;
@@ -3812,7 +3867,7 @@ static void do_symmetrical_brush_actions(Sculpt *sd,
float feather = calc_symmetry_feather(sd, ss->cache);
- cache->bstrength = brush_strength(sd, cache, feather, ups);
+ cache->bstrength = brush_strength(sd, cache, feather, ups, paint_mode_settings);
cache->symmetry = symm;
/* `symm` is a bit combination of XYZ -
@@ -3825,11 +3880,11 @@ static void do_symmetrical_brush_actions(Sculpt *sd,
cache->radial_symmetry_pass = 0;
SCULPT_cache_calc_brushdata_symm(cache, i, 0, 0);
- do_tiled(sd, ob, brush, ups, action);
+ do_tiled(sd, ob, brush, ups, paint_mode_settings, action);
- do_radial_symmetry(sd, ob, brush, ups, action, i, 'X', feather);
- do_radial_symmetry(sd, ob, brush, ups, action, i, 'Y', feather);
- do_radial_symmetry(sd, ob, brush, ups, action, i, 'Z', feather);
+ do_radial_symmetry(sd, ob, brush, ups, paint_mode_settings, action, i, 'X', feather);
+ do_radial_symmetry(sd, ob, brush, ups, paint_mode_settings, action, i, 'Y', feather);
+ do_radial_symmetry(sd, ob, brush, ups, paint_mode_settings, action, i, 'Z', feather);
}
}
@@ -4609,7 +4664,8 @@ static bool sculpt_needs_connectivity_info(const Sculpt *sd,
SCULPT_TOOL_NEEDS_COLOR(brush->sculpt_tool) ||
(brush->sculpt_tool == SCULPT_TOOL_CLOTH) || (brush->sculpt_tool == SCULPT_TOOL_SMEAR) ||
(brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS) ||
- (brush->sculpt_tool == SCULPT_TOOL_DISPLACEMENT_SMEAR));
+ (brush->sculpt_tool == SCULPT_TOOL_DISPLACEMENT_SMEAR) ||
+ (brush->sculpt_tool == SCULPT_TOOL_PAINT));
}
void SCULPT_stroke_modifiers_check(const bContext *C, Object *ob, const Brush *brush)
@@ -5057,6 +5113,15 @@ void SCULPT_flush_update_step(bContext *C, SculptUpdateType update_flags)
multires_mark_as_modified(depsgraph, ob, MULTIRES_COORDS_MODIFIED);
}
+ if ((update_flags & SCULPT_UPDATE_IMAGE) != 0) {
+ ED_region_tag_redraw(region);
+ if (update_flags == SCULPT_UPDATE_IMAGE) {
+ /* Early exit when only need to update the images. We don't want to tag any geometry updates
+ * that would rebuilt the PBVH. */
+ return;
+ }
+ }
+
DEG_id_tag_update(&ob->id, ID_RECALC_SHADING);
/* Only current viewport matters, slower update for all viewports will
@@ -5136,6 +5201,16 @@ void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType up
}
}
}
+
+ if (update_flags & SCULPT_UPDATE_IMAGE) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ SpaceLink *sl = area->spacedata.first;
+ if (sl->spacetype != SPACE_IMAGE) {
+ continue;
+ }
+ ED_area_tag_redraw_regiontype(area, RGN_TYPE_WINDOW);
+ }
+ }
}
if (update_flags & SCULPT_UPDATE_COORDS) {
@@ -5227,6 +5302,7 @@ static void sculpt_stroke_update_step(bContext *C,
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
const Brush *brush = BKE_paint_brush(&sd->paint);
+ ToolSettings *tool_settings = CTX_data_tool_settings(C);
SCULPT_stroke_modifiers_check(C, ob, brush);
sculpt_update_cache_variants(C, sd, ob, itemptr);
@@ -5246,10 +5322,10 @@ static void sculpt_stroke_update_step(bContext *C,
}
if (SCULPT_stroke_is_dynamic_topology(ss, brush)) {
- do_symmetrical_brush_actions(sd, ob, sculpt_topology_update, ups);
+ do_symmetrical_brush_actions(sd, ob, sculpt_topology_update, ups, &tool_settings->paint_mode);
}
- do_symmetrical_brush_actions(sd, ob, do_brush_action, ups);
+ do_symmetrical_brush_actions(sd, ob, do_brush_action, ups, &tool_settings->paint_mode);
sculpt_combine_proxies(sd, ob);
/* Hack to fix noise texture tearing mesh. */
@@ -5280,7 +5356,12 @@ static void sculpt_stroke_update_step(bContext *C,
SCULPT_flush_update_step(C, SCULPT_UPDATE_MASK);
}
else if (ELEM(brush->sculpt_tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR)) {
- SCULPT_flush_update_step(C, SCULPT_UPDATE_COLOR);
+ if (SCULPT_use_image_paint_brush(&tool_settings->paint_mode, ob)) {
+ SCULPT_flush_update_step(C, SCULPT_UPDATE_IMAGE);
+ }
+ else {
+ SCULPT_flush_update_step(C, SCULPT_UPDATE_COLOR);
+ }
}
else {
SCULPT_flush_update_step(C, SCULPT_UPDATE_COORDS);
@@ -5302,6 +5383,7 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+ ToolSettings *tool_settings = CTX_data_tool_settings(C);
/* Finished. */
if (!ss->cache) {
@@ -5335,6 +5417,11 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str
if (brush->sculpt_tool == SCULPT_TOOL_MASK) {
SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK);
}
+ else if (brush->sculpt_tool == SCULPT_TOOL_PAINT) {
+ if (SCULPT_use_image_paint_brush(&tool_settings->paint_mode, ob)) {
+ SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_IMAGE);
+ }
+ }
else {
SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COORDS);
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c
index e4180a36a98..5d4a2c54832 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c
@@ -109,6 +109,7 @@ static void color_filter_task_cb(void *__restrict userdata,
}
copy_v3_v3(orig_color, orig_data.col);
+ final_color[3] = orig_data.col[3]; /* Copy alpha */
switch (mode) {
case COLOR_FILTER_FILL: {
@@ -128,8 +129,14 @@ static void color_filter_task_cb(void *__restrict userdata,
break;
case COLOR_FILTER_SATURATION:
rgb_to_hsv_v(orig_color, hsv_color);
- hsv_color[1] = clamp_f(hsv_color[1] + fade, 0.0f, 1.0f);
- hsv_to_rgb_v(hsv_color, final_color);
+
+ if (hsv_color[1] != 0.0f) {
+ hsv_color[1] = clamp_f(hsv_color[1] + fade, 0.0f, 1.0f);
+ hsv_to_rgb_v(hsv_color, final_color);
+ }
+ else {
+ copy_v3_v3(final_color, orig_color);
+ }
break;
case COLOR_FILTER_VALUE:
rgb_to_hsv_v(orig_color, hsv_color);
@@ -186,7 +193,32 @@ static void color_filter_task_cb(void *__restrict userdata,
float col[4];
SCULPT_vertex_color_get(ss, vd.index, col);
- blend_color_interpolate_float(final_color, col, smooth_color, fade);
+ if (fade < 0.0f) {
+ interp_v4_v4v4(smooth_color, smooth_color, col, 0.5f);
+ }
+
+ bool copy_alpha = col[3] == smooth_color[3];
+
+ if (fade < 0.0f) {
+ float delta_color[4];
+
+ /* Unsharp mask. */
+ copy_v4_v4(delta_color, ss->filter_cache->pre_smoothed_color[vd.index]);
+ sub_v4_v4(delta_color, smooth_color);
+
+ copy_v4_v4(final_color, col);
+ madd_v4_v4fl(final_color, delta_color, fade);
+ }
+ else {
+ blend_color_interpolate_float(final_color, col, smooth_color, fade);
+ }
+
+ CLAMP4(final_color, 0.0f, 1.0f);
+
+ /* Prevent accumulated numeric error from corrupting alpha. */
+ if (copy_alpha) {
+ final_color[3] = smooth_color[3];
+ }
break;
}
}
@@ -201,6 +233,46 @@ static void color_filter_task_cb(void *__restrict userdata,
BKE_pbvh_node_mark_update_color(data->nodes[n]);
}
+static void sculpt_color_presmooth_init(SculptSession *ss)
+{
+ int totvert = SCULPT_vertex_count_get(ss);
+
+ if (!ss->filter_cache->pre_smoothed_color) {
+ ss->filter_cache->pre_smoothed_color = MEM_malloc_arrayN(
+ totvert, sizeof(float) * 4, "ss->filter_cache->pre_smoothed_color");
+ }
+
+ for (int i = 0; i < totvert; i++) {
+ SCULPT_vertex_color_get(ss, i, ss->filter_cache->pre_smoothed_color[i]);
+ }
+
+ for (int iteration = 0; iteration < 2; iteration++) {
+ for (int i = 0; i < totvert; i++) {
+ float avg[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ int total = 0;
+
+ SculptVertexNeighborIter ni;
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) {
+ float col[4] = {0};
+
+ copy_v4_v4(col, ss->filter_cache->pre_smoothed_color[ni.index]);
+
+ add_v4_v4(avg, col);
+ total++;
+ }
+ SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+
+ if (total > 0) {
+ mul_v4_fl(avg, 1.0f / (float)total);
+ interp_v4_v4v4(ss->filter_cache->pre_smoothed_color[i],
+ ss->filter_cache->pre_smoothed_color[i],
+ avg,
+ 0.5f);
+ }
+ }
+ }
+}
+
static int sculpt_color_filter_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
Object *ob = CTX_data_active_object(C);
@@ -227,6 +299,10 @@ static int sculpt_color_filter_modal(bContext *C, wmOperator *op, const wmEvent
RNA_float_get_array(op->ptr, "fill_color", fill_color);
IMB_colormanagement_srgb_to_scene_linear_v3(fill_color);
+ if (filter_strength < 0.0 && !ss->filter_cache->pre_smoothed_color) {
+ sculpt_color_presmooth_init(ss);
+ }
+
SculptThreadedTaskData data = {
.sd = sd,
.ob = ob,
@@ -273,12 +349,7 @@ static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent
return OPERATOR_CANCELLED;
}
- if (!SCULPT_has_colors(ss)) {
- return OPERATOR_CANCELLED;
- }
-
SCULPT_undo_push_begin(ob, "color filter");
-
BKE_sculpt_color_layer_create_if_needed(ob);
/* CTX_data_ensure_evaluated_depsgraph should be used at the end to include the updates of
@@ -310,7 +381,7 @@ void SCULPT_OT_color_filter(struct wmOperatorType *ot)
/* api callbacks */
ot->invoke = sculpt_color_filter_invoke;
ot->modal = sculpt_color_filter_modal;
- ot->poll = SCULPT_vertex_colors_poll;
+ ot->poll = SCULPT_mode_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
index 4b832256dae..a32b1c3015b 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
@@ -178,6 +178,7 @@ void SCULPT_filter_cache_free(SculptSession *ss)
MEM_SAFE_FREE(ss->filter_cache->sharpen_factor);
MEM_SAFE_FREE(ss->filter_cache->detail_directions);
MEM_SAFE_FREE(ss->filter_cache->limit_surface_co);
+ MEM_SAFE_FREE(ss->filter_cache->pre_smoothed_color);
MEM_SAFE_FREE(ss->filter_cache);
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index 38905b50c59..3839c0e71e4 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -11,11 +11,13 @@
#include "DNA_key_types.h"
#include "DNA_listBase.h"
#include "DNA_meshdata_types.h"
+#include "DNA_scene_types.h"
#include "DNA_vec_types.h"
#include "BKE_paint.h"
#include "BKE_pbvh.h"
#include "BLI_bitmap.h"
+#include "BLI_compiler_attrs.h"
#include "BLI_compiler_compat.h"
#include "BLI_gsqueue.h"
#include "BLI_threads.h"
@@ -25,12 +27,13 @@ extern "C" {
#endif
struct AutomaskingCache;
+struct Image;
+struct ImageUser;
struct KeyBlock;
struct Object;
struct SculptUndoNode;
struct bContext;
-
-enum ePaintSymmetryFlags;
+struct PaintModeSettings;
/* Updates */
@@ -43,6 +46,7 @@ typedef enum SculptUpdateType {
SCULPT_UPDATE_MASK = 1 << 1,
SCULPT_UPDATE_VISIBILITY = 1 << 2,
SCULPT_UPDATE_COLOR = 1 << 3,
+ SCULPT_UPDATE_IMAGE = 1 << 4,
} SculptUpdateType;
typedef struct SculptCursorGeometryInfo {
@@ -439,6 +443,9 @@ typedef struct FilterCache {
/* Auto-masking. */
AutomaskingCache *automasking;
+
+ /* Pre-smoothed colors used by sharpening. Colors are HSL. */
+ float (*pre_smoothed_color)[4];
} FilterCache;
/**
@@ -1623,7 +1630,29 @@ void SCULPT_multiplane_scrape_preview_draw(uint gpuattr,
void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode);
/* Paint Brush. */
-void SCULPT_do_paint_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode);
+void SCULPT_do_paint_brush(struct PaintModeSettings *paint_mode_settings,
+ Sculpt *sd,
+ Object *ob,
+ PBVHNode **nodes,
+ int totnode) ATTR_NONNULL();
+
+/**
+ * @brief Get the image canvas for painting on the given object.
+ *
+ * @return #true if an image is found. The #r_image and #r_image_user fields are filled with the
+ * image and image user. Returns false when the image isn't found. In the later case the r_image
+ * and r_image_user are set to nullptr/NULL.
+ */
+bool SCULPT_paint_image_canvas_get(struct PaintModeSettings *paint_mode_settings,
+ struct Object *ob,
+ struct Image **r_image,
+ struct ImageUser **r_image_user) ATTR_NONNULL();
+void SCULPT_do_paint_brush_image(struct PaintModeSettings *paint_mode_settings,
+ Sculpt *sd,
+ Object *ob,
+ PBVHNode **nodes,
+ int totnode) ATTR_NONNULL();
+bool SCULPT_use_image_paint_brush(struct PaintModeSettings *settings, Object *ob) ATTR_NONNULL();
/* Smear Brush. */
void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode);
diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.c b/source/blender/editors/sculpt_paint/sculpt_ops.c
index f84852d1d0e..9581bf2071c 100644
--- a/source/blender/editors/sculpt_paint/sculpt_ops.c
+++ b/source/blender/editors/sculpt_paint/sculpt_ops.c
@@ -1030,14 +1030,6 @@ static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEven
return OPERATOR_CANCELLED;
}
- if (!SCULPT_has_colors(ss)) {
- return OPERATOR_CANCELLED;
- }
-
- if (SCULPT_has_loop_colors(ob)) {
- BKE_pbvh_ensure_node_loops(ss->pbvh);
- }
-
SCULPT_vertex_random_access_ensure(ss);
/* Tools that are not brushes do not have the brush gizmo to update the vertex as the mouse move,
@@ -1049,12 +1041,17 @@ static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEven
SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
SCULPT_undo_push_begin(ob, "Mask by color");
+ BKE_sculpt_color_layer_create_if_needed(ob);
const int active_vertex = SCULPT_active_vertex_get(ss);
const float threshold = RNA_float_get(op->ptr, "threshold");
const bool invert = RNA_boolean_get(op->ptr, "invert");
const bool preserve_mask = RNA_boolean_get(op->ptr, "preserve_previous_mask");
+ if (SCULPT_has_loop_colors(ob)) {
+ BKE_pbvh_ensure_node_loops(ss->pbvh);
+ }
+
if (RNA_boolean_get(op->ptr, "contiguous")) {
sculpt_mask_by_color_contiguous(ob, active_vertex, threshold, invert, preserve_mask);
}
@@ -1080,7 +1077,7 @@ static void SCULPT_OT_mask_by_color(wmOperatorType *ot)
/* api callbacks */
ot->invoke = sculpt_mask_by_color_invoke;
- ot->poll = SCULPT_vertex_colors_poll;
+ ot->poll = SCULPT_mode_poll;
ot->flag = OPTYPE_REGISTER;
diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.c b/source/blender/editors/sculpt_paint/sculpt_paint_color.c
index e7a713efa14..7a8a6e8e484 100644
--- a/source/blender/editors/sculpt_paint/sculpt_paint_color.c
+++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.c
@@ -240,8 +240,14 @@ static void sample_wet_paint_reduce(const void *__restrict UNUSED(userdata),
add_v4_v4(join->color, swptd->color);
}
-void SCULPT_do_paint_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
+void SCULPT_do_paint_brush(
+ PaintModeSettings *paint_mode_settings, Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
+ if (SCULPT_use_image_paint_brush(paint_mode_settings, ob)) {
+ SCULPT_do_paint_brush_image(paint_mode_settings, sd, ob, nodes, totnode);
+ return;
+ }
+
Brush *brush = BKE_paint_brush(&sd->paint);
SculptSession *ss = ob->sculpt;
diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc
new file mode 100644
index 00000000000..1fc7551f545
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc
@@ -0,0 +1,430 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+#include "DNA_image_types.h"
+#include "DNA_material_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_node_types.h"
+#include "DNA_object_types.h"
+
+#include "ED_paint.h"
+#include "ED_uvedit.h"
+
+#include "BLI_math.h"
+#include "BLI_math_color_blend.h"
+#include "BLI_task.h"
+
+#include "IMB_colormanagement.h"
+#include "IMB_imbuf.h"
+
+#include "BKE_brush.h"
+#include "BKE_image_wrappers.hh"
+#include "BKE_material.h"
+#include "BKE_pbvh.h"
+#include "BKE_pbvh_pixels.hh"
+
+#include "bmesh.h"
+
+#include "NOD_shader.h"
+
+#include "sculpt_intern.h"
+
+namespace blender::ed::sculpt_paint::paint::image {
+
+using namespace blender::bke::pbvh::pixels;
+using namespace blender::bke::image;
+
+struct ImageData {
+ Image *image = nullptr;
+ ImageUser *image_user = nullptr;
+
+ ~ImageData() = default;
+
+ static bool init_active_image(Object *ob,
+ ImageData *r_image_data,
+ PaintModeSettings *paint_mode_settings)
+ {
+ return BKE_paint_canvas_image_get(
+ paint_mode_settings, ob, &r_image_data->image, &r_image_data->image_user);
+ }
+};
+
+struct TexturePaintingUserData {
+ Object *ob;
+ Brush *brush;
+ PBVHNode **nodes;
+ ImageData image_data;
+};
+
+/** Reading and writing to image buffer with 4 float channels. */
+class ImageBufferFloat4 {
+ private:
+ int pixel_offset;
+
+ public:
+ void set_image_position(ImBuf *image_buffer, ushort2 image_pixel_position)
+ {
+ pixel_offset = int(image_pixel_position.y) * image_buffer->x + int(image_pixel_position.x);
+ }
+
+ void next_pixel()
+ {
+ pixel_offset += 1;
+ }
+
+ float4 read_pixel(ImBuf *image_buffer) const
+ {
+ return &image_buffer->rect_float[pixel_offset * 4];
+ }
+
+ void write_pixel(ImBuf *image_buffer, const float4 pixel_data) const
+ {
+ copy_v4_v4(&image_buffer->rect_float[pixel_offset * 4], pixel_data);
+ }
+
+ const char *get_colorspace_name(ImBuf *image_buffer)
+ {
+ return IMB_colormanagement_get_float_colorspace(image_buffer);
+ }
+};
+
+/** Reading and writing to image buffer with 4 byte channels. */
+class ImageBufferByte4 {
+ private:
+ int pixel_offset;
+
+ public:
+ void set_image_position(ImBuf *image_buffer, ushort2 image_pixel_position)
+ {
+ pixel_offset = int(image_pixel_position.y) * image_buffer->x + int(image_pixel_position.x);
+ }
+
+ void next_pixel()
+ {
+ pixel_offset += 1;
+ }
+
+ float4 read_pixel(ImBuf *image_buffer) const
+ {
+ float4 result;
+ rgba_uchar_to_float(result,
+ static_cast<const uchar *>(
+ static_cast<const void *>(&(image_buffer->rect[pixel_offset]))));
+ return result;
+ }
+
+ void write_pixel(ImBuf *image_buffer, const float4 pixel_data) const
+ {
+ rgba_float_to_uchar(
+ static_cast<uchar *>(static_cast<void *>(&image_buffer->rect[pixel_offset])), pixel_data);
+ }
+
+ const char *get_colorspace_name(ImBuf *image_buffer)
+ {
+ return IMB_colormanagement_get_rect_colorspace(image_buffer);
+ }
+};
+
+template<typename ImageBuffer> class PaintingKernel {
+ ImageBuffer image_accessor;
+
+ SculptSession *ss;
+ const Brush *brush;
+ const int thread_id;
+ const MVert *mvert;
+
+ float4 brush_color;
+ float brush_strength;
+
+ SculptBrushTestFn brush_test_fn;
+ SculptBrushTest test;
+ /* Pointer to the last used image buffer to detect when buffers are switched. */
+ void *last_used_image_buffer_ptr = nullptr;
+ const char *last_used_color_space = nullptr;
+
+ public:
+ explicit PaintingKernel(SculptSession *ss,
+ const Brush *brush,
+ const int thread_id,
+ const MVert *mvert)
+ : ss(ss), brush(brush), thread_id(thread_id), mvert(mvert)
+ {
+ init_brush_strength();
+ init_brush_test();
+ }
+
+ bool paint(const Triangles &triangles, const PackedPixelRow &pixel_row, ImBuf *image_buffer)
+ {
+ image_accessor.set_image_position(image_buffer, pixel_row.start_image_coordinate);
+ const TrianglePaintInput triangle = triangles.get_paint_input(pixel_row.triangle_index);
+ float3 pixel_pos = get_start_pixel_pos(triangle, pixel_row);
+ const float3 delta_pixel_pos = get_delta_pixel_pos(triangle, pixel_row, pixel_pos);
+ bool pixels_painted = false;
+ for (int x = 0; x < pixel_row.num_pixels; x++) {
+ if (!brush_test_fn(&test, pixel_pos)) {
+ pixel_pos += delta_pixel_pos;
+ image_accessor.next_pixel();
+ continue;
+ }
+
+ float4 color = image_accessor.read_pixel(image_buffer);
+ const float3 normal(0.0f, 0.0f, 0.0f);
+ const float3 face_normal(0.0f, 0.0f, 0.0f);
+ const float mask = 0.0f;
+ const float falloff_strength = SCULPT_brush_strength_factor(
+ ss, brush, pixel_pos, sqrtf(test.dist), normal, face_normal, mask, 0, thread_id);
+ float4 paint_color = brush_color * falloff_strength * brush_strength;
+ float4 buffer_color;
+ blend_color_mix_float(buffer_color, color, paint_color);
+ buffer_color *= brush->alpha;
+ IMB_blend_color_float(color, color, buffer_color, static_cast<IMB_BlendMode>(brush->blend));
+ image_accessor.write_pixel(image_buffer, color);
+ pixels_painted = true;
+
+ image_accessor.next_pixel();
+ pixel_pos += delta_pixel_pos;
+ }
+ return pixels_painted;
+ }
+
+ void init_brush_color(ImBuf *image_buffer)
+ {
+ const char *to_colorspace = image_accessor.get_colorspace_name(image_buffer);
+ if (last_used_color_space == to_colorspace) {
+ return;
+ }
+ copy_v3_v3(brush_color,
+ ss->cache->invert ? BKE_brush_secondary_color_get(ss->scene, brush) :
+ BKE_brush_color_get(ss->scene, brush));
+ /* NOTE: Brush colors are stored in sRGB. We use math color to follow other areas that
+ * use brush colors. From there on we use IMB_colormanagement to convert the brush color to the
+ * colorspace of the texture. This isn't ideal, but would need more refactoring to make sure
+ * that brush colors are stored in scene linear by default. */
+ srgb_to_linearrgb_v3_v3(brush_color, brush_color);
+ brush_color[3] = 1.0f;
+
+ const char *from_colorspace = IMB_colormanagement_role_colorspace_name_get(
+ COLOR_ROLE_SCENE_LINEAR);
+ ColormanageProcessor *cm_processor = IMB_colormanagement_colorspace_processor_new(
+ from_colorspace, to_colorspace);
+ IMB_colormanagement_processor_apply_v4(cm_processor, brush_color);
+ IMB_colormanagement_processor_free(cm_processor);
+ last_used_color_space = to_colorspace;
+ }
+
+ private:
+ void init_brush_strength()
+ {
+ brush_strength = ss->cache->bstrength;
+ }
+ void init_brush_test()
+ {
+ brush_test_fn = SCULPT_brush_test_init_with_falloff_shape(ss, &test, brush->falloff_shape);
+ }
+
+ /**
+ * Extract the starting pixel position from the given encoded_pixels belonging to the triangle.
+ */
+ float3 get_start_pixel_pos(const TrianglePaintInput &triangle,
+ const PackedPixelRow &encoded_pixels) const
+ {
+ return init_pixel_pos(triangle, encoded_pixels.start_barycentric_coord);
+ }
+
+ /**
+ * Extract the delta pixel position that will be used to advance a Pixel instance to the next
+ * pixel.
+ */
+ float3 get_delta_pixel_pos(const TrianglePaintInput &triangle,
+ const PackedPixelRow &encoded_pixels,
+ const float3 &start_pixel) const
+ {
+ float3 result = init_pixel_pos(
+ triangle, encoded_pixels.start_barycentric_coord + triangle.delta_barycentric_coord_u);
+ return result - start_pixel;
+ }
+
+ float3 init_pixel_pos(const TrianglePaintInput &triangle,
+ const float2 &barycentric_weights) const
+ {
+ const int3 &vert_indices = triangle.vert_indices;
+ float3 result;
+ const float3 barycentric(barycentric_weights.x,
+ barycentric_weights.y,
+ 1.0f - barycentric_weights.x - barycentric_weights.y);
+ interp_v3_v3v3v3(result,
+ mvert[vert_indices[0]].co,
+ mvert[vert_indices[1]].co,
+ mvert[vert_indices[2]].co,
+ barycentric);
+ return result;
+ }
+};
+
+static std::vector<bool> init_triangle_brush_test(SculptSession *ss,
+ Triangles &triangles,
+ const MVert *mvert)
+{
+ std::vector<bool> brush_test(triangles.size());
+ SculptBrushTest test;
+ SCULPT_brush_test_init(ss, &test);
+ float3 brush_min_bounds(test.location[0] - test.radius,
+ test.location[1] - test.radius,
+ test.location[2] - test.radius);
+ float3 brush_max_bounds(test.location[0] + test.radius,
+ test.location[1] + test.radius,
+ test.location[2] + test.radius);
+ for (int triangle_index = 0; triangle_index < triangles.size(); triangle_index++) {
+ TrianglePaintInput &triangle = triangles.get_paint_input(triangle_index);
+
+ float3 triangle_min_bounds(mvert[triangle.vert_indices[0]].co);
+ float3 triangle_max_bounds(triangle_min_bounds);
+ for (int i = 1; i < 3; i++) {
+ const float3 &pos = mvert[triangle.vert_indices[i]].co;
+ triangle_min_bounds.x = min_ff(triangle_min_bounds.x, pos.x);
+ triangle_min_bounds.y = min_ff(triangle_min_bounds.y, pos.y);
+ triangle_min_bounds.z = min_ff(triangle_min_bounds.z, pos.z);
+ triangle_max_bounds.x = max_ff(triangle_max_bounds.x, pos.x);
+ triangle_max_bounds.y = max_ff(triangle_max_bounds.y, pos.y);
+ triangle_max_bounds.z = max_ff(triangle_max_bounds.z, pos.z);
+ }
+ brush_test[triangle_index] = isect_aabb_aabb_v3(
+ brush_min_bounds, brush_max_bounds, triangle_min_bounds, triangle_max_bounds);
+ }
+ return brush_test;
+}
+
+static void do_paint_pixels(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ TexturePaintingUserData *data = static_cast<TexturePaintingUserData *>(userdata);
+ Object *ob = data->ob;
+ SculptSession *ss = ob->sculpt;
+ const Brush *brush = data->brush;
+ PBVHNode *node = data->nodes[n];
+
+ NodeData &node_data = BKE_pbvh_pixels_node_data_get(*node);
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+ MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss);
+
+ std::vector<bool> brush_test = init_triangle_brush_test(ss, node_data.triangles, mvert);
+
+ PaintingKernel<ImageBufferFloat4> kernel_float4(ss, brush, thread_id, mvert);
+ PaintingKernel<ImageBufferByte4> kernel_byte4(ss, brush, thread_id, mvert);
+
+ ImageUser image_user = *data->image_data.image_user;
+ bool pixels_updated = false;
+ for (UDIMTilePixels &tile_data : node_data.tiles) {
+ LISTBASE_FOREACH (ImageTile *, tile, &data->image_data.image->tiles) {
+ ImageTileWrapper image_tile(tile);
+ if (image_tile.get_tile_number() == tile_data.tile_number) {
+ image_user.tile = image_tile.get_tile_number();
+
+ ImBuf *image_buffer = BKE_image_acquire_ibuf(data->image_data.image, &image_user, nullptr);
+ if (image_buffer == nullptr) {
+ continue;
+ }
+
+ if (image_buffer->rect_float != nullptr) {
+ kernel_float4.init_brush_color(image_buffer);
+ }
+ else {
+ kernel_float4.init_brush_color(image_buffer);
+ }
+
+ for (const PackedPixelRow &pixel_row : tile_data.pixel_rows) {
+ if (!brush_test[pixel_row.triangle_index]) {
+ continue;
+ }
+ bool pixels_painted = false;
+ if (image_buffer->rect_float != nullptr) {
+ pixels_painted = kernel_float4.paint(node_data.triangles, pixel_row, image_buffer);
+ }
+ else {
+ pixels_painted = kernel_byte4.paint(node_data.triangles, pixel_row, image_buffer);
+ }
+
+ if (pixels_painted) {
+ tile_data.mark_dirty(pixel_row);
+ }
+ }
+
+ BKE_image_release_ibuf(data->image_data.image, image_buffer, nullptr);
+ pixels_updated |= tile_data.flags.dirty;
+ break;
+ }
+ }
+ }
+
+ node_data.flags.dirty |= pixels_updated;
+}
+
+static void do_mark_dirty_regions(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ TexturePaintingUserData *data = static_cast<TexturePaintingUserData *>(userdata);
+ PBVHNode *node = data->nodes[n];
+ BKE_pbvh_pixels_mark_image_dirty(*node, *data->image_data.image, *data->image_data.image_user);
+}
+
+} // namespace blender::ed::sculpt_paint::paint::image
+
+extern "C" {
+
+using namespace blender::ed::sculpt_paint::paint::image;
+
+bool SCULPT_paint_image_canvas_get(PaintModeSettings *paint_mode_settings,
+ Object *ob,
+ Image **r_image,
+ ImageUser **r_image_user)
+{
+ BLI_assert(r_image);
+ BLI_assert(r_image_user);
+ ImageData image_data;
+ if (!ImageData::init_active_image(ob, &image_data, paint_mode_settings)) {
+ return false;
+ }
+
+ *r_image = image_data.image;
+ *r_image_user = image_data.image_user;
+ return true;
+}
+
+bool SCULPT_use_image_paint_brush(PaintModeSettings *settings, Object *ob)
+{
+ if (!U.experimental.use_sculpt_texture_paint) {
+ return false;
+ }
+ if (ob->type != OB_MESH) {
+ return false;
+ }
+ Image *image;
+ ImageUser *image_user;
+ return BKE_paint_canvas_image_get(settings, ob, &image, &image_user);
+}
+
+void SCULPT_do_paint_brush_image(
+ PaintModeSettings *paint_mode_settings, Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
+{
+ Brush *brush = BKE_paint_brush(&sd->paint);
+
+ TexturePaintingUserData data = {nullptr};
+ data.ob = ob;
+ data.brush = brush;
+ data.nodes = nodes;
+
+ if (!ImageData::init_active_image(ob, &data.image_data, paint_mode_settings)) {
+ return;
+ }
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(0, totnode, &data, do_paint_pixels, &settings);
+
+ TaskParallelSettings settings_flush;
+ BKE_pbvh_parallel_range_settings(&settings_flush, false, totnode);
+ BLI_task_parallel_range(0, totnode, &data, do_mark_dirty_regions, &settings_flush);
+}
+}
diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c
index 1354277fbdd..eae90359dfd 100644
--- a/source/blender/editors/sculpt_paint/sculpt_undo.c
+++ b/source/blender/editors/sculpt_paint/sculpt_undo.c
@@ -343,7 +343,7 @@ static bool sculpt_undo_restore_color(bContext *C, SculptUndoNode *unode)
bool modified = false;
/* NOTE: even with loop colors we still store derived
- * vertex colors for original data lookup.*/
+ * vertex colors for original data lookup. */
if (unode->col && !unode->loop_col) {
BKE_pbvh_swap_colors(ss->pbvh, unode->index, unode->totvert, unode->col);
modified = true;
diff --git a/source/blender/editors/space_action/CMakeLists.txt b/source/blender/editors/space_action/CMakeLists.txt
index 9e292062f27..841bd5cf91b 100644
--- a/source/blender/editors/space_action/CMakeLists.txt
+++ b/source/blender/editors/space_action/CMakeLists.txt
@@ -5,6 +5,7 @@ set(INC
../../blenkernel
../../blenlib
../../blentranslation
+ ../../depsgraph
../../gpu
../../makesdna
../../makesrna
diff --git a/source/blender/editors/space_action/action_data.c b/source/blender/editors/space_action/action_data.c
index 36858d57446..8f97a58451e 100644
--- a/source/blender/editors/space_action/action_data.c
+++ b/source/blender/editors/space_action/action_data.c
@@ -45,6 +45,8 @@
#include "ED_mask.h"
#include "ED_screen.h"
+#include "DEG_depsgraph.h"
+
#include "WM_api.h"
#include "WM_types.h"
@@ -335,6 +337,13 @@ static int action_pushdown_exec(bContext *C, wmOperator *op)
/* action can be safely added */
BKE_nla_action_pushdown(adt, ID_IS_OVERRIDE_LIBRARY(adt_id_owner));
+ struct Main *bmain = CTX_data_main(C);
+ DEG_id_tag_update_ex(bmain, adt_id_owner, ID_RECALC_ANIMATION);
+
+ /* The action needs updating too, as FCurve modifiers are to be reevaluated. They won't extend
+ * beyond the NLA strip after pushing down to the NLA. */
+ DEG_id_tag_update_ex(bmain, &adt->action->id, ID_RECALC_ANIMATION);
+
/* Stop displaying this action in this editor
* NOTE: The editor itself doesn't set a user...
*/
diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c
index 1ec6832c6c3..1d0061ab7d8 100644
--- a/source/blender/editors/space_buttons/space_buttons.c
+++ b/source/blender/editors/space_buttons/space_buttons.c
@@ -861,12 +861,11 @@ static void buttons_id_remap(ScrArea *UNUSED(area),
for (int i = 0; i < path->len; i++) {
switch (BKE_id_remapper_apply(mappings, &path->ptr[i].owner_id, ID_REMAP_APPLY_DEFAULT)) {
case ID_REMAP_RESULT_SOURCE_UNASSIGNED: {
- if (i == 0) {
- MEM_SAFE_FREE(sbuts->path);
- }
- else {
+ path->len = i;
+ if (i != 0) {
+ /* If the first item in the path is cleared, the whole path is cleared, so no need to
+ * clear further items here, see also at the end of this block. */
memset(&path->ptr[i], 0, sizeof(path->ptr[i]) * (path->len - i));
- path->len = i;
}
break;
}
@@ -887,6 +886,9 @@ static void buttons_id_remap(ScrArea *UNUSED(area),
}
}
}
+ if (path->len == 0) {
+ MEM_SAFE_FREE(sbuts->path);
+ }
}
if (sbuts->texuser) {
diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c
index 0438ba8dcd2..0e2b98ca349 100644
--- a/source/blender/editors/space_file/file_draw.c
+++ b/source/blender/editors/space_file/file_draw.c
@@ -248,7 +248,7 @@ static void file_draw_string_multiline(int sx,
int font_id = style->widgetlabel.uifont_id;
int len = strlen(string);
- rctf textbox;
+ rcti textbox;
BLF_wordwrap(font_id, wrap_width);
BLF_enable(font_id, BLF_WORD_WRAP);
BLF_boundbox(font_id, string, len, &textbox);
@@ -260,7 +260,7 @@ static void file_draw_string_multiline(int sx,
rect.xmax = sx + wrap_width;
/* Need to increase the clipping rect by one more line, since the #UI_fontstyle_draw_ex() will
* actually start drawing at (ymax - line-height). */
- rect.ymin = sy - round_fl_to_int(BLI_rctf_size_y(&textbox)) - line_height;
+ rect.ymin = sy - BLI_rcti_size_y(&textbox) - line_height;
rect.ymax = sy;
struct ResultBLF result;
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index 08f4ecc0dd9..9f71d6f77c7 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.c
@@ -3403,7 +3403,7 @@ static void filelist_readjob_main_recursive(Main *bmain, FileList *filelist)
filelist->filelist.entries[20].entry->relpath = BLI_strdup("Action");
filelist->filelist.entries[21].entry->relpath = BLI_strdup("NodeTree");
filelist->filelist.entries[22].entry->relpath = BLI_strdup("Speaker");
- filelist->filelist.entries[23].entry->relpath = BLI_strdup("Hair");
+ filelist->filelist.entries[23].entry->relpath = BLI_strdup("Curves");
filelist->filelist.entries[24].entry->relpath = BLI_strdup("Point Cloud");
filelist->filelist.entries[25].entry->relpath = BLI_strdup("Volume");
# ifdef WITH_FREESTYLE
diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c
index 79bfaa92f80..2aa9b347ed7 100644
--- a/source/blender/editors/space_nla/nla_edit.c
+++ b/source/blender/editors/space_nla/nla_edit.c
@@ -98,6 +98,7 @@ static int nlaedit_enable_tweakmode_exec(bContext *C, wmOperator *op)
int filter;
const bool do_solo = RNA_boolean_get(op->ptr, "isolate_action");
+ const bool use_upper_stack_evaluation = RNA_boolean_get(op->ptr, "use_upper_stack_evaluation");
bool ok = false;
/* get editor data */
@@ -119,6 +120,13 @@ static int nlaedit_enable_tweakmode_exec(bContext *C, wmOperator *op)
for (ale = anim_data.first; ale; ale = ale->next) {
AnimData *adt = ale->data;
+ if (use_upper_stack_evaluation) {
+ adt->flag |= ADT_NLA_EVAL_UPPER_TRACKS;
+ }
+ else {
+ adt->flag &= ~ADT_NLA_EVAL_UPPER_TRACKS;
+ }
+
/* Try entering tweak-mode if valid. */
ok |= BKE_nla_tweakmode_enter(adt);
@@ -181,6 +189,13 @@ void NLA_OT_tweakmode_enter(wmOperatorType *ot)
"Enable 'solo' on the NLA Track containing the active strip, "
"to edit it without seeing the effects of the NLA stack");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+
+ prop = RNA_def_boolean(ot->srna,
+ "use_upper_stack_evaluation",
+ false,
+ "Evaluate Upper Stack",
+ "In tweak mode, display the effects of the tracks above the tweak strip");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
/** \} */
diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc
index 88ab7617932..47d35ced371 100644
--- a/source/blender/editors/space_node/node_draw.cc
+++ b/source/blender/editors/space_node/node_draw.cc
@@ -26,12 +26,10 @@
#include "BLI_span.hh"
#include "BLI_string_ref.hh"
#include "BLI_vector.hh"
-#include "BLI_vector_set.hh"
#include "BLT_translation.h"
#include "BKE_context.h"
-#include "BKE_geometry_set.hh"
#include "BKE_idtype.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
@@ -75,10 +73,9 @@
#include "node_intern.hh" /* own include */
using blender::GPointer;
-using blender::fn::FieldCPPType;
-using blender::fn::FieldInput;
using blender::fn::GField;
namespace geo_log = blender::nodes::geometry_nodes_eval_log;
+using geo_log::NamedAttributeUsage;
extern "C" {
/* XXX interface.h */
@@ -374,6 +371,8 @@ static void node_update_basis(const bContext &C, bNodeTree &ntree, bNode &node,
const char *socket_label = nodeSocketLabel(nsock);
nsock->typeinfo->draw((bContext *)&C, row, &sockptr, &nodeptr, IFACE_(socket_label));
+ node_socket_add_tooltip(&ntree, &node, nsock, row);
+
UI_block_align_end(&block);
UI_block_layout_resolve(&block, nullptr, &buty);
@@ -504,6 +503,8 @@ static void node_update_basis(const bContext &C, bNodeTree &ntree, bNode &node,
const char *socket_label = nodeSocketLabel(nsock);
nsock->typeinfo->draw((bContext *)&C, row, &sockptr, &nodeptr, IFACE_(socket_label));
+ node_socket_add_tooltip(&ntree, &node, nsock, row);
+
UI_block_align_end(&block);
UI_block_layout_resolve(&block, nullptr, &buty);
@@ -943,6 +944,10 @@ static std::optional<std::string> create_socket_inspection_string(bContext *C,
bNodeSocket &socket)
{
SpaceNode *snode = CTX_wm_space_node(C);
+ if (snode == nullptr) {
+ return {};
+ };
+
const geo_log::SocketLog *socket_log = geo_log::ModifierLog::find_socket_by_node_editor_context(
*snode, node, socket);
if (socket_log == nullptr) {
@@ -970,6 +975,78 @@ static std::optional<std::string> create_socket_inspection_string(bContext *C,
return ss.str();
}
+static bool node_socket_has_tooltip(bNodeTree *ntree, bNodeSocket *socket)
+{
+ if (ntree->type == NTREE_GEOMETRY) {
+ return true;
+ }
+
+ if (socket->declaration != nullptr) {
+ const blender::nodes::SocketDeclaration &socket_decl = *socket->declaration;
+ return !socket_decl.description().is_empty();
+ }
+
+ return false;
+}
+
+static char *node_socket_get_tooltip(bContext *C,
+ bNodeTree *ntree,
+ bNode *node,
+ bNodeSocket *socket)
+{
+ std::stringstream output;
+ if (socket->declaration != nullptr) {
+ const blender::nodes::SocketDeclaration &socket_decl = *socket->declaration;
+ blender::StringRef description = socket_decl.description();
+ if (!description.is_empty()) {
+ output << TIP_(description.data());
+ }
+ }
+
+ if (ntree->type == NTREE_GEOMETRY) {
+ if (!output.str().empty()) {
+ output << ".\n\n";
+ }
+
+ std::optional<std::string> socket_inspection_str = create_socket_inspection_string(
+ C, *node, *socket);
+ if (socket_inspection_str.has_value()) {
+ output << *socket_inspection_str;
+ }
+ else {
+ output << TIP_("The socket value has not been computed yet");
+ }
+ }
+
+ if (output.str().empty()) {
+ output << nodeSocketLabel(socket);
+ }
+
+ return BLI_strdup(output.str().c_str());
+}
+
+void node_socket_add_tooltip(bNodeTree *ntree, bNode *node, bNodeSocket *sock, uiLayout *layout)
+{
+ if (!node_socket_has_tooltip(ntree, sock)) {
+ return;
+ }
+
+ SocketTooltipData *data = MEM_cnew<SocketTooltipData>(__func__);
+ data->ntree = ntree;
+ data->node = node;
+ data->socket = sock;
+
+ uiLayoutSetTooltipFunc(
+ layout,
+ [](bContext *C, void *argN, const char *UNUSED(tip)) {
+ SocketTooltipData *data = static_cast<SocketTooltipData *>(argN);
+ return node_socket_get_tooltip(C, data->ntree, data->node, data->socket);
+ },
+ data,
+ MEM_dupallocN,
+ MEM_freeN);
+}
+
static void node_socket_draw_nested(const bContext &C,
bNodeTree &ntree,
PointerRNA &node_ptr,
@@ -1001,8 +1078,7 @@ static void node_socket_draw_nested(const bContext &C,
size_id,
outline_col_id);
- if (ntree.type != NTREE_GEOMETRY) {
- /* Only geometry nodes has socket value tooltips currently. */
+ if (!node_socket_has_tooltip(&ntree, &sock)) {
return;
}
@@ -1034,24 +1110,7 @@ static void node_socket_draw_nested(const bContext &C,
but,
[](bContext *C, void *argN, const char *UNUSED(tip)) {
SocketTooltipData *data = (SocketTooltipData *)argN;
- std::optional<std::string> socket_inspection_str = create_socket_inspection_string(
- C, *data->node, *data->socket);
-
- std::stringstream output;
- if (data->socket->declaration != nullptr) {
- const blender::nodes::SocketDeclaration &socket_decl = *data->socket->declaration;
- blender::StringRef description = socket_decl.description();
- if (!description.is_empty()) {
- output << TIP_(description.data()) << ".\n\n";
- }
- }
- if (socket_inspection_str.has_value()) {
- output << *socket_inspection_str;
- }
- else {
- output << TIP_("The socket value has not been computed yet");
- }
- return BLI_strdup(output.str().c_str());
+ return node_socket_get_tooltip(C, data->ntree, data->node, data->socket);
},
data,
MEM_freeN);
@@ -1626,10 +1685,127 @@ static std::string node_get_execution_time_label(const SpaceNode &snode, const b
struct NodeExtraInfoRow {
std::string text;
- const char *tooltip;
int icon;
+ const char *tooltip = nullptr;
+
+ uiButToolTipFunc tooltip_fn = nullptr;
+ void *tooltip_fn_arg = nullptr;
+ void (*tooltip_fn_free_arg)(void *) = nullptr;
};
+struct NamedAttributeTooltipArg {
+ Map<std::string, NamedAttributeUsage> usage_by_attribute;
+};
+
+static char *named_attribute_tooltip(bContext *UNUSED(C), void *argN, const char *UNUSED(tip))
+{
+ NamedAttributeTooltipArg &arg = *static_cast<NamedAttributeTooltipArg *>(argN);
+
+ std::stringstream ss;
+ ss << TIP_("Accessed attribute names:\n");
+ Vector<std::pair<StringRefNull, NamedAttributeUsage>> sorted_used_attribute;
+ for (auto &&item : arg.usage_by_attribute.items()) {
+ sorted_used_attribute.append({item.key, item.value});
+ }
+ std::sort(sorted_used_attribute.begin(), sorted_used_attribute.end());
+ for (const std::pair<StringRefNull, NamedAttributeUsage> &attribute : sorted_used_attribute) {
+ const StringRefNull name = attribute.first;
+ const NamedAttributeUsage usage = attribute.second;
+ ss << " \u2022 \"" << name << "\": ";
+ Vector<std::string> usages;
+ if ((usage & NamedAttributeUsage::Read) != NamedAttributeUsage::None) {
+ usages.append(TIP_("read"));
+ }
+ if ((usage & NamedAttributeUsage::Write) != NamedAttributeUsage::None) {
+ usages.append(TIP_("write"));
+ }
+ if ((usage & NamedAttributeUsage::Remove) != NamedAttributeUsage::None) {
+ usages.append(TIP_("remove"));
+ }
+ for (const int i : usages.index_range()) {
+ ss << usages[i];
+ if (i < usages.size() - 1) {
+ ss << ", ";
+ }
+ }
+ ss << "\n";
+ }
+ ss << "\n";
+ ss << TIP_(
+ "Attributes with these names used within the group may conflict with existing attributes");
+ return BLI_strdup(ss.str().c_str());
+}
+
+static NodeExtraInfoRow row_from_used_named_attribute(
+ const Map<std::string, NamedAttributeUsage> &usage_by_attribute_name)
+{
+ NodeExtraInfoRow row;
+ row.text = TIP_("Attributes");
+ row.icon = ICON_SPREADSHEET;
+ row.tooltip_fn = named_attribute_tooltip;
+ row.tooltip_fn_arg = new NamedAttributeTooltipArg{usage_by_attribute_name};
+ row.tooltip_fn_free_arg = [](void *arg) { delete static_cast<NamedAttributeTooltipArg *>(arg); };
+ return row;
+}
+
+static std::optional<NodeExtraInfoRow> node_get_accessed_attributes_row(const SpaceNode &snode,
+ const bNode &node)
+{
+ if (node.type == NODE_GROUP) {
+ const geo_log::TreeLog *root_tree_log = geo_log::ModifierLog::find_tree_by_node_editor_context(
+ snode);
+ if (root_tree_log == nullptr) {
+ return std::nullopt;
+ }
+ const geo_log::TreeLog *tree_log = root_tree_log->lookup_child_log(node.name);
+ if (tree_log == nullptr) {
+ return std::nullopt;
+ }
+
+ Map<std::string, NamedAttributeUsage> usage_by_attribute;
+ tree_log->foreach_node_log([&](const geo_log::NodeLog &node_log) {
+ for (const geo_log::UsedNamedAttribute &used_attribute : node_log.used_named_attributes()) {
+ usage_by_attribute.lookup_or_add_as(used_attribute.name,
+ used_attribute.usage) |= used_attribute.usage;
+ }
+ });
+ if (usage_by_attribute.is_empty()) {
+ return std::nullopt;
+ }
+
+ return row_from_used_named_attribute(usage_by_attribute);
+ }
+ if (ELEM(node.type,
+ GEO_NODE_STORE_NAMED_ATTRIBUTE,
+ GEO_NODE_REMOVE_ATTRIBUTE,
+ GEO_NODE_INPUT_NAMED_ATTRIBUTE)) {
+ /* Only show the overlay when the name is passed in from somewhere else. */
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node.inputs) {
+ if (STREQ(socket->name, "Name")) {
+ if ((socket->flag & SOCK_IN_USE) == 0) {
+ return std::nullopt;
+ }
+ }
+ }
+ const geo_log::NodeLog *node_log = geo_log::ModifierLog::find_node_by_node_editor_context(
+ snode, node.name);
+ if (node_log == nullptr) {
+ return std::nullopt;
+ }
+ Map<std::string, NamedAttributeUsage> usage_by_attribute;
+ for (const geo_log::UsedNamedAttribute &used_attribute : node_log->used_named_attributes()) {
+ usage_by_attribute.lookup_or_add_as(used_attribute.name,
+ used_attribute.usage) |= used_attribute.usage;
+ }
+ if (usage_by_attribute.is_empty()) {
+ return std::nullopt;
+ }
+ return row_from_used_named_attribute(usage_by_attribute);
+ }
+
+ return std::nullopt;
+}
+
static Vector<NodeExtraInfoRow> node_get_extra_info(const SpaceNode &snode, const bNode &node)
{
Vector<NodeExtraInfoRow> rows;
@@ -1660,6 +1836,14 @@ static Vector<NodeExtraInfoRow> node_get_extra_info(const SpaceNode &snode, cons
rows.append(std::move(row));
}
}
+
+ if (snode.overlay.flag & SN_OVERLAY_SHOW_NAMED_ATTRIBUTES &&
+ snode.edittree->type == NTREE_GEOMETRY) {
+ if (std::optional<NodeExtraInfoRow> row = node_get_accessed_attributes_row(snode, node)) {
+ rows.append(std::move(*row));
+ }
+ }
+
return rows;
}
@@ -1669,20 +1853,20 @@ static void node_draw_extra_info_row(const bNode &node,
const int row,
const NodeExtraInfoRow &extra_info_row)
{
- uiBut *but_timing = uiDefBut(&block,
- UI_BTYPE_LABEL,
- 0,
- extra_info_row.text.c_str(),
- (int)(rect.xmin + 4.0f * U.dpi_fac + NODE_MARGIN_X + 0.4f),
- (int)(rect.ymin + row * (20.0f * U.dpi_fac)),
- (short)(rect.xmax - rect.xmin),
- (short)NODE_DY,
- nullptr,
- 0,
- 0,
- 0,
- 0,
- "");
+ uiBut *but_text = uiDefBut(&block,
+ UI_BTYPE_LABEL,
+ 0,
+ extra_info_row.text.c_str(),
+ (int)(rect.xmin + 4.0f * U.dpi_fac + NODE_MARGIN_X + 0.4f),
+ (int)(rect.ymin + row * (20.0f * U.dpi_fac)),
+ (short)(rect.xmax - rect.xmin),
+ (short)NODE_DY,
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ "");
UI_block_emboss_set(&block, UI_EMBOSS_NONE);
uiBut *but_icon = uiDefIconBut(&block,
UI_BTYPE_BUT,
@@ -1698,9 +1882,15 @@ static void node_draw_extra_info_row(const bNode &node,
0,
0,
extra_info_row.tooltip);
+ if (extra_info_row.tooltip_fn != nullptr) {
+ UI_but_func_tooltip_set(but_icon,
+ extra_info_row.tooltip_fn,
+ extra_info_row.tooltip_fn_arg,
+ extra_info_row.tooltip_fn_free_arg);
+ }
UI_block_emboss_set(&block, UI_EMBOSS);
if (node.flag & NODE_MUTED) {
- UI_but_flag_enable(but_timing, UI_BUT_INACTIVE);
+ UI_but_flag_enable(but_text, UI_BUT_INACTIVE);
UI_but_flag_enable(but_icon, UI_BUT_INACTIVE);
}
}
diff --git a/source/blender/editors/space_node/node_intern.hh b/source/blender/editors/space_node/node_intern.hh
index 44163cd5ae3..10d4ad36d95 100644
--- a/source/blender/editors/space_node/node_intern.hh
+++ b/source/blender/editors/space_node/node_intern.hh
@@ -134,6 +134,8 @@ void node_socket_color_get(const bContext &C,
void node_draw_space(const bContext &C, ARegion &region);
+void node_socket_add_tooltip(bNodeTree *ntree, bNode *node, bNodeSocket *sock, uiLayout *layout);
+
/**
* Sort nodes by selection: unselected nodes first, then selected,
* then the active node at the very end. Relative order is kept intact.
diff --git a/source/blender/editors/space_node/node_templates.cc b/source/blender/editors/space_node/node_templates.cc
index e92e0571157..58a313c328e 100644
--- a/source/blender/editors/space_node/node_templates.cc
+++ b/source/blender/editors/space_node/node_templates.cc
@@ -852,23 +852,19 @@ static void ui_node_draw_input(
}
}
else {
- row = uiLayoutRow(row, true);
+ uiLayout *sub = uiLayoutRow(row, true);
- uiTemplateNodeLink(row, C, ntree, node, input);
+ uiTemplateNodeLink(sub, C, ntree, node, input);
if (input->flag & SOCK_HIDE_VALUE) {
add_dummy_decorator = true;
}
/* input not linked, show value */
else {
- uiLayout *sub = row;
-
switch (input->type) {
case SOCK_VECTOR:
- if (input->type == SOCK_VECTOR) {
- uiItemS(row);
- sub = uiLayoutColumn(row, true);
- }
+ uiItemS(sub);
+ sub = uiLayoutColumn(sub, true);
ATTR_FALLTHROUGH;
case SOCK_FLOAT:
case SOCK_INT:
@@ -884,7 +880,7 @@ static void ui_node_draw_input(
if (node_tree->type == NTREE_GEOMETRY && snode != nullptr) {
/* Only add the attribute search in the node editor, in other places there is not
* enough context. */
- node_geometry_add_attribute_search_button(*C, *node, inputptr, *row);
+ node_geometry_add_attribute_search_button(*C, *node, inputptr, *sub);
}
else {
uiItemR(sub, &inputptr, "default_value", 0, "", ICON_NONE);
@@ -903,6 +899,8 @@ static void ui_node_draw_input(
uiItemDecoratorR(split_wrapper.decorate_column, nullptr, nullptr, 0);
}
+ node_socket_add_tooltip(ntree, node, input, row);
+
/* clear */
node->flag &= ~NODE_TEST;
}
diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc
index 67bc0e91053..5da64177e51 100644
--- a/source/blender/editors/space_outliner/outliner_tools.cc
+++ b/source/blender/editors/space_outliner/outliner_tools.cc
@@ -217,6 +217,7 @@ static void unlink_action_fn(bContext *C,
{
/* just set action to nullptr */
BKE_animdata_set_action(CTX_wm_reports(C), tsep->id, nullptr);
+ DEG_id_tag_update(tsep->id, ID_RECALC_ANIMATION);
}
static void unlink_material_fn(bContext *UNUSED(C),
@@ -2016,7 +2017,7 @@ static const EnumPropertyItem prop_id_op_types[] = {
{OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET,
"OVERRIDE_LIBRARY_RESET",
0,
- "Reset Library Override",
+ "Reset Library Override Single",
"Reset this local override to its linked values"},
{OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY,
"OVERRIDE_LIBRARY_RESET_HIERARCHY",
@@ -2036,18 +2037,18 @@ static const EnumPropertyItem prop_id_op_types[] = {
"Rebuild this local override from its linked reference, as well as its hierarchy of "
"dependencies, enforcing that hierarchy to match the linked data (i.e. ignoring exiting "
"overrides on data-blocks pointer properties)"},
+ {OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_SINGLE,
+ "OVERRIDE_LIBRARY_CLEAR_SINGLE",
+ 0,
+ "Clear Library Override Single",
+ "Delete this local override and relink its usages to the linked data-blocks if possible, "
+ "else reset it and mark it as non editable"},
{OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_HIERARCHY,
"OVERRIDE_LIBRARY_CLEAR_HIERARCHY",
0,
"Clear Library Override Hierarchy",
"Delete this local override (including its hierarchy of override dependencies) and relink "
"its usages to the linked data-blocks"},
- {OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_SINGLE,
- "OVERRIDE_LIBRARY_CLEAR_SINGLE",
- 0,
- "Clear Single Library Override",
- "Delete this local override if possible, else reset it and mark it as non editable, and "
- "relink its usages to the linked data-blocks"},
{0, "", 0, nullptr, nullptr},
{OUTLINER_IDOP_COPY, "COPY", ICON_COPYDOWN, "Copy", ""},
{OUTLINER_IDOP_PASTE, "PASTE", ICON_PASTEDOWN, "Paste", ""},
diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
index fbdc451cf06..14b9dbe4b44 100644
--- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
+++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
@@ -264,7 +264,13 @@ Object *spreadsheet_get_object_eval(const SpaceSpreadsheet *sspreadsheet,
return nullptr;
}
Object *object_orig = (Object *)used_id;
- if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD, OB_VOLUME, OB_CURVES_LEGACY, OB_FONT)) {
+ if (!ELEM(object_orig->type,
+ OB_MESH,
+ OB_POINTCLOUD,
+ OB_VOLUME,
+ OB_CURVES_LEGACY,
+ OB_FONT,
+ OB_CURVES)) {
return nullptr;
}
diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c
index 520d234e261..03862c6bdff 100644
--- a/source/blender/editors/space_view3d/view3d_select.c
+++ b/source/blender/editors/space_view3d/view3d_select.c
@@ -2125,49 +2125,6 @@ static Base *mouse_select_eval_buffer(ViewContext *vc,
hit_index = a;
}
}
-
- /* Find the best active & non-active hits.
- * NOTE(@campbellbarton): Checking if `hits > 1` isn't a reliable way to know
- * if there are multiple objects selected since it's possible the same object
- * generates multiple hits, either from:
- * - Multiple sub-components (bones & camera tracks).
- * - Multiple selectable elements such as the object center and the geometry.
- *
- * For this reason, keep track of the best hit as well as the best hit that
- * excludes the selected & active object, using this value when it's valid. */
- if ((hit_index != -1) &&
- /* Special case, cycling away from the active object should only be done when it
- * doesn't have a bone selection, otherwise selecting sub-elements is difficult. */
- ((buffer[hit_index].id & 0xFFFF0000) == 0) &&
- /* Only exclude active object when it is selected. */
- (BASACT(view_layer) && (BASACT(view_layer)->flag & BASE_SELECTED)) &&
- /* Allow disabling this behavior entirely. */
- (U.experimental.use_select_nearest_on_first_click == false)) {
-
- const int select_id_active = BASACT(view_layer)->object->runtime.select_id;
-
- /* Check if `hit_index` is the current active object. */
- if ((buffer[hit_index].id & 0xFFFF) == select_id_active) {
- uint min_not_active = 0xFFFFFFFF;
- int hit_index_not_active = -1;
- for (a = 0; a < hits; a++) {
- /* Any object other than the active-selected. */
- if (select_id_active == (buffer[a].id & 0xFFFF)) {
- continue;
- }
- if (min_not_active > buffer[a].depth) {
- min_not_active = buffer[a].depth;
- hit_index_not_active = a;
- }
- }
-
- /* When the active was selected, first try to use the index
- * for the best non-active hit that was found. */
- if (hit_index_not_active != -1) {
- hit_index = hit_index_not_active;
- }
- }
- }
}
if (hit_index != -1) {
diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c
index a56bbb1c1df..fc88737ca70 100644
--- a/source/blender/editors/space_view3d/view3d_view.c
+++ b/source/blender/editors/space_view3d/view3d_view.c
@@ -1129,7 +1129,6 @@ void VIEW3D_OT_localview_remove_from(wmOperatorType *ot)
/* api callbacks */
ot->exec = localview_remove_from_exec;
- ot->invoke = WM_operator_confirm;
ot->poll = localview_remove_from_poll;
ot->flag = OPTYPE_UNDO;
}
diff --git a/source/blender/editors/transform/transform_convert_sequencer.c b/source/blender/editors/transform/transform_convert_sequencer.c
index c7f086e7d4b..d7d85646b08 100644
--- a/source/blender/editors/transform/transform_convert_sequencer.c
+++ b/source/blender/editors/transform/transform_convert_sequencer.c
@@ -669,8 +669,10 @@ static Sequence *effect_base_input_get(Sequence *effect, SeqInputSide side)
return input;
}
-/* Strips that aren't selected, but their position entirely depends on transformed strips.
- * This collection is used to offset animation.*/
+/**
+ * Strips that aren't selected, but their position entirely depends on transformed strips.
+ * This collection is used to offset animation.
+ */
static SeqCollection *query_time_dependent_strips_strips(TransInfo *t)
{
ListBase *seqbase = seqbase_active_get(t);
diff --git a/source/blender/editors/util/ed_draw.c b/source/blender/editors/util/ed_draw.c
index eaa1684930d..1b6a3efe19c 100644
--- a/source/blender/editors/util/ed_draw.c
+++ b/source/blender/editors/util/ed_draw.c
@@ -711,7 +711,7 @@ static float metadata_box_height_get(ImBuf *ibuf, int fontid, const bool is_top)
if (i == 4) {
struct {
struct ResultBLF info;
- rctf rect;
+ rcti rect;
} wrap;
BLF_enable(fontid, BLF_WORD_WRAP);
diff --git a/source/blender/editors/util/ed_transverts.c b/source/blender/editors/util/ed_transverts.c
index 8d8f7733877..ef57f9e9b33 100644
--- a/source/blender/editors/util/ed_transverts.c
+++ b/source/blender/editors/util/ed_transverts.c
@@ -203,7 +203,7 @@ void ED_transverts_create_from_obedit(TransVertStore *tvs, const Object *obedit,
BMesh *bm = em->bm;
BMIter iter;
void *userdata[2] = {em, NULL};
- /*int proptrans = 0; */ /*UNUSED*/
+ // int proptrans = 0; /*UNUSED*/
/* abuses vertex index all over, set, just set dirty here,
* perhaps this could use its own array instead? - campbell */
diff --git a/source/blender/functions/FN_multi_function_builder.hh b/source/blender/functions/FN_multi_function_builder.hh
index e0ccd2a482d..088a906ce02 100644
--- a/source/blender/functions/FN_multi_function_builder.hh
+++ b/source/blender/functions/FN_multi_function_builder.hh
@@ -390,39 +390,6 @@ template<typename Mut1> class CustomMF_SM : public MultiFunction {
};
/**
- * Generates a multi-function that converts between two types.
- */
-template<typename From, typename To> class CustomMF_Convert : public MultiFunction {
- public:
- CustomMF_Convert()
- {
- static MFSignature signature = create_signature();
- this->set_signature(&signature);
- }
-
- static MFSignature create_signature()
- {
- static std::string name = CPPType::get<From>().name() + " to " + CPPType::get<To>().name();
- MFSignatureBuilder signature{name.c_str()};
- signature.single_input<From>("Input");
- signature.single_output<To>("Output");
- return signature.build();
- }
-
- void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
- {
- const VArray<From> &inputs = params.readonly_single_input<From>(0);
- MutableSpan<To> outputs = params.uninitialized_single_output<To>(1);
-
- mask.to_best_mask_type([&](const auto &mask) {
- for (int64_t i : mask) {
- new (static_cast<void *>(&outputs[i])) To(inputs[i]);
- }
- });
- }
-};
-
-/**
* A multi-function that outputs the same value every time. The value is not owned by an instance
* of this function. If #make_value_copy is false, the caller is responsible for destructing and
* freeing the value.
diff --git a/source/blender/functions/tests/FN_multi_function_test.cc b/source/blender/functions/tests/FN_multi_function_test.cc
index 59d797605e7..577b09cb014 100644
--- a/source/blender/functions/tests/FN_multi_function_test.cc
+++ b/source/blender/functions/tests/FN_multi_function_test.cc
@@ -307,25 +307,6 @@ TEST(multi_function, CustomMF_GenericConstantArray)
}
}
-TEST(multi_function, CustomMF_Convert)
-{
- CustomMF_Convert<float, int> fn;
-
- Array<float> inputs = {5.4f, 7.1f, 9.0f};
- Array<int> outputs(inputs.size(), 0);
-
- MFParamsBuilder params(fn, inputs.size());
- params.add_readonly_single_input(inputs.as_span());
- params.add_uninitialized_single_output(outputs.as_mutable_span());
-
- MFContextBuilder context;
- fn.call({0, 2}, params, context);
-
- EXPECT_EQ(outputs[0], 5);
- EXPECT_EQ(outputs[1], 0);
- EXPECT_EQ(outputs[2], 9);
-}
-
TEST(multi_function, IgnoredOutputs)
{
OptionalOutputsFunction fn;
diff --git a/source/blender/geometry/intern/mesh_merge_by_distance.cc b/source/blender/geometry/intern/mesh_merge_by_distance.cc
index 6dc6271194b..9bb1cbb324e 100644
--- a/source/blender/geometry/intern/mesh_merge_by_distance.cc
+++ b/source/blender/geometry/intern/mesh_merge_by_distance.cc
@@ -104,7 +104,7 @@ struct WeldMesh {
/* References all polygons and loops that will be affected. */
Vector<WeldLoop> wloop;
Vector<WeldPoly> wpoly;
- WeldPoly *wpoly_new;
+ MutableSpan<WeldPoly> wpoly_new;
int wloop_len;
int wpoly_len;
int wpoly_new_len;
@@ -806,11 +806,9 @@ static void weld_poly_loop_ctx_alloc(Span<MPoly> mpoly,
wpoly.resize(wpoly_len + maybe_new_poly);
}
- WeldPoly *poly_new = wpoly.data() + wpoly_len;
-
r_weld_mesh->wloop = std::move(wloop);
r_weld_mesh->wpoly = std::move(wpoly);
- r_weld_mesh->wpoly_new = poly_new;
+ r_weld_mesh->wpoly_new = r_weld_mesh->wpoly.as_mutable_span().drop_front(wpoly_len);
r_weld_mesh->wloop_len = wloop_len;
r_weld_mesh->wpoly_len = wpoly_len;
r_weld_mesh->wpoly_new_len = 0;
@@ -830,133 +828,134 @@ static void weld_poly_split_recursive(Span<int> vert_dest_map,
int *r_loop_kill)
{
int poly_len = r_wp->len;
- if (poly_len > 3 && ctx_verts_len > 1) {
- const int ctx_loops_len = r_wp->loops.len;
- const int ctx_loops_ofs = r_wp->loops.ofs;
- MutableSpan<WeldLoop> wloop = r_weld_mesh->wloop;
- WeldPoly *wpoly_new = r_weld_mesh->wpoly_new;
-
- int loop_kill = 0;
-
- WeldLoop *poly_loops = &wloop[ctx_loops_ofs];
- WeldLoop *wla = &poly_loops[0];
- WeldLoop *wla_prev = &poly_loops[ctx_loops_len - 1];
- while (wla_prev->flag == ELEM_COLLAPSED) {
- wla_prev--;
- }
- const int la_len = ctx_loops_len - 1;
- for (int la = 0; la < la_len; la++, wla++) {
- wa_continue:
- if (wla->flag == ELEM_COLLAPSED) {
- continue;
- }
- int vert_a = wla->vert;
- /* Only test vertices that will be merged. */
- if (vert_dest_map[vert_a] != OUT_OF_CONTEXT) {
- int lb = la + 1;
- WeldLoop *wlb = wla + 1;
- WeldLoop *wlb_prev = wla;
- int killed_ab = 0;
- ctx_verts_len = 1;
- for (; lb < ctx_loops_len; lb++, wlb++) {
- BLI_assert(wlb->loop_skip_to == OUT_OF_CONTEXT);
- if (wlb->flag == ELEM_COLLAPSED) {
- killed_ab++;
- continue;
- }
- int vert_b = wlb->vert;
- if (vert_dest_map[vert_b] != OUT_OF_CONTEXT) {
- ctx_verts_len++;
+ if (poly_len < 3 || ctx_verts_len < 1) {
+ return;
+ }
+
+ const int ctx_loops_len = r_wp->loops.len;
+ const int ctx_loops_ofs = r_wp->loops.ofs;
+ MutableSpan<WeldLoop> wloop = r_weld_mesh->wloop;
+
+ int loop_kill = 0;
+
+ WeldLoop *poly_loops = &wloop[ctx_loops_ofs];
+ WeldLoop *wla = &poly_loops[0];
+ WeldLoop *wla_prev = &poly_loops[ctx_loops_len - 1];
+ while (wla_prev->flag == ELEM_COLLAPSED) {
+ wla_prev--;
+ }
+ const int la_len = ctx_loops_len - 1;
+ for (int la = 0; la < la_len; la++, wla++) {
+ wa_continue:
+ if (wla->flag == ELEM_COLLAPSED) {
+ continue;
+ }
+ int vert_a = wla->vert;
+ /* Only test vertices that will be merged. */
+ if (vert_dest_map[vert_a] != OUT_OF_CONTEXT) {
+ int lb = la + 1;
+ WeldLoop *wlb = wla + 1;
+ WeldLoop *wlb_prev = wla;
+ int killed_ab = 0;
+ ctx_verts_len = 1;
+ for (; lb < ctx_loops_len; lb++, wlb++) {
+ BLI_assert(wlb->loop_skip_to == OUT_OF_CONTEXT);
+ if (wlb->flag == ELEM_COLLAPSED) {
+ killed_ab++;
+ continue;
+ }
+ int vert_b = wlb->vert;
+ if (vert_dest_map[vert_b] != OUT_OF_CONTEXT) {
+ ctx_verts_len++;
+ }
+ if (vert_a == vert_b) {
+ const int dist_a = wlb->loop_orig - wla->loop_orig - killed_ab;
+ const int dist_b = poly_len - dist_a;
+
+ BLI_assert(dist_a != 0 && dist_b != 0);
+ if (dist_a == 1 || dist_b == 1) {
+ BLI_assert(dist_a != dist_b);
+ BLI_assert((wla->flag == ELEM_COLLAPSED) || (wlb->flag == ELEM_COLLAPSED));
}
- if (vert_a == vert_b) {
- const int dist_a = wlb->loop_orig - wla->loop_orig - killed_ab;
- const int dist_b = poly_len - dist_a;
-
- BLI_assert(dist_a != 0 && dist_b != 0);
- if (dist_a == 1 || dist_b == 1) {
- BLI_assert(dist_a != dist_b);
- BLI_assert((wla->flag == ELEM_COLLAPSED) || (wlb->flag == ELEM_COLLAPSED));
+ else {
+ WeldLoop *wl_tmp = nullptr;
+ if (dist_a == 2) {
+ wl_tmp = wlb_prev;
+ BLI_assert(wla->flag != ELEM_COLLAPSED);
+ BLI_assert(wl_tmp->flag != ELEM_COLLAPSED);
+ wla->flag = ELEM_COLLAPSED;
+ wl_tmp->flag = ELEM_COLLAPSED;
+ loop_kill += 2;
+ poly_len -= 2;
}
- else {
- WeldLoop *wl_tmp = nullptr;
- if (dist_a == 2) {
- wl_tmp = wlb_prev;
- BLI_assert(wla->flag != ELEM_COLLAPSED);
+ if (dist_b == 2) {
+ if (wl_tmp != nullptr) {
+ r_wp->flag = ELEM_COLLAPSED;
+ *r_poly_kill += 1;
+ }
+ else {
+ wl_tmp = wla_prev;
+ BLI_assert(wlb->flag != ELEM_COLLAPSED);
BLI_assert(wl_tmp->flag != ELEM_COLLAPSED);
- wla->flag = ELEM_COLLAPSED;
+ wlb->flag = ELEM_COLLAPSED;
wl_tmp->flag = ELEM_COLLAPSED;
- loop_kill += 2;
- poly_len -= 2;
- }
- if (dist_b == 2) {
- if (wl_tmp != nullptr) {
- r_wp->flag = ELEM_COLLAPSED;
- *r_poly_kill += 1;
- }
- else {
- wl_tmp = wla_prev;
- BLI_assert(wlb->flag != ELEM_COLLAPSED);
- BLI_assert(wl_tmp->flag != ELEM_COLLAPSED);
- wlb->flag = ELEM_COLLAPSED;
- wl_tmp->flag = ELEM_COLLAPSED;
- }
- loop_kill += 2;
- poly_len -= 2;
}
- if (wl_tmp == nullptr) {
- const int new_loops_len = lb - la;
- const int new_loops_ofs = ctx_loops_ofs + la;
-
- WeldPoly *new_wp = &wpoly_new[r_weld_mesh->wpoly_new_len++];
- new_wp->poly_dst = OUT_OF_CONTEXT;
- new_wp->poly_orig = r_wp->poly_orig;
- new_wp->loops.len = new_loops_len;
- new_wp->loops.ofs = new_loops_ofs;
- new_wp->loop_start = wla->loop_orig;
- new_wp->loop_end = wlb_prev->loop_orig;
- new_wp->len = dist_a;
- weld_poly_split_recursive(vert_dest_map,
+ loop_kill += 2;
+ poly_len -= 2;
+ }
+ if (wl_tmp == nullptr) {
+ const int new_loops_len = lb - la;
+ const int new_loops_ofs = ctx_loops_ofs + la;
+
+ WeldPoly *new_wp = &r_weld_mesh->wpoly_new[r_weld_mesh->wpoly_new_len++];
+ new_wp->poly_dst = OUT_OF_CONTEXT;
+ new_wp->poly_orig = r_wp->poly_orig;
+ new_wp->loops.len = new_loops_len;
+ new_wp->loops.ofs = new_loops_ofs;
+ new_wp->loop_start = wla->loop_orig;
+ new_wp->loop_end = wlb_prev->loop_orig;
+ new_wp->len = dist_a;
+ weld_poly_split_recursive(vert_dest_map,
#ifdef USE_WELD_DEBUG
- mloop,
+ mloop,
#endif
- ctx_verts_len,
- new_wp,
- r_weld_mesh,
- r_poly_kill,
- r_loop_kill);
- BLI_assert(dist_b == poly_len - dist_a);
- poly_len = dist_b;
- if (wla_prev->loop_orig > wla->loop_orig) {
- /* New start. */
- r_wp->loop_start = wlb->loop_orig;
- }
- else {
- /* The `loop_start` doesn't change but some loops must be skipped. */
- wla_prev->loop_skip_to = wlb->loop_orig;
- }
- wla = wlb;
- la = lb;
- goto wa_continue;
+ ctx_verts_len,
+ new_wp,
+ r_weld_mesh,
+ r_poly_kill,
+ r_loop_kill);
+ BLI_assert(dist_b == poly_len - dist_a);
+ poly_len = dist_b;
+ if (wla_prev->loop_orig > wla->loop_orig) {
+ /* New start. */
+ r_wp->loop_start = wlb->loop_orig;
}
- break;
+ else {
+ /* The `loop_start` doesn't change but some loops must be skipped. */
+ wla_prev->loop_skip_to = wlb->loop_orig;
+ }
+ wla = wlb;
+ la = lb;
+ goto wa_continue;
}
- }
- if (wlb->flag != ELEM_COLLAPSED) {
- wlb_prev = wlb;
+ break;
}
}
+ if (wlb->flag != ELEM_COLLAPSED) {
+ wlb_prev = wlb;
+ }
}
- if (wla->flag != ELEM_COLLAPSED) {
- wla_prev = wla;
- }
}
- r_wp->len = poly_len;
- *r_loop_kill += loop_kill;
+ if (wla->flag != ELEM_COLLAPSED) {
+ wla_prev = wla;
+ }
+ }
+ r_wp->len = poly_len;
+ *r_loop_kill += loop_kill;
#ifdef USE_WELD_DEBUG
- weld_assert_poly_no_vert_repetition(*r_wp, wloop, mloop, r_weld_mesh->loop_map);
+ weld_assert_poly_no_vert_repetition(*r_wp, wloop, mloop, r_weld_mesh->loop_map);
#endif
- }
}
static void weld_poly_loop_ctx_setup(Span<MLoop> mloop,
@@ -971,7 +970,6 @@ static void weld_poly_loop_ctx_setup(Span<MLoop> mloop,
{
MutableSpan<WeldPoly> wpoly = r_weld_mesh->wpoly;
MutableSpan<WeldLoop> wloop = r_weld_mesh->wloop;
- WeldPoly *wpoly_new = r_weld_mesh->wpoly_new;
int wpoly_len = r_weld_mesh->wpoly_len;
int wpoly_new_len = 0;
int poly_kill_len = 0;
@@ -1034,7 +1032,7 @@ static void weld_poly_loop_ctx_setup(Span<MLoop> mloop,
#ifdef USE_WELD_DEBUG
weld_assert_poly_and_loop_kill_len(wpoly,
- {wpoly_new, wpoly_new_len},
+ r_weld_mesh->wpoly_new,
wloop,
mloop,
loop_map,
@@ -1170,7 +1168,7 @@ static void weld_poly_loop_ctx_setup(Span<MLoop> mloop,
#ifdef USE_WELD_DEBUG
weld_assert_poly_and_loop_kill_len(wpoly,
- {wpoly_new, wpoly_new_len},
+ r_weld_mesh->wpoly_new,
wloop,
mloop,
loop_map,
@@ -1180,7 +1178,6 @@ static void weld_poly_loop_ctx_setup(Span<MLoop> mloop,
loop_kill_len);
#endif
- r_weld_mesh->wpoly_new = wpoly_new;
r_weld_mesh->poly_kill_len = poly_kill_len;
r_weld_mesh->loop_kill_len = loop_kill_len;
}
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilenvelope.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilenvelope.c
index efbec4222aa..8b0a6ee84a2 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilenvelope.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilenvelope.c
@@ -344,6 +344,7 @@ static void add_stroke(Object *ob,
const int size = size1 + size2;
bGPdata *gpd = ob->data;
bGPDstroke *gps_dst = BKE_gpencil_stroke_new(mat_nr, size, gps->thickness);
+ gps_dst->runtime.gps_orig = gps->runtime.gps_orig;
memcpy(&gps_dst->points[0], &gps->points[connection_index], size1 * sizeof(bGPDspoint));
memcpy(&gps_dst->points[size1], &gps->points[point_index], size2 * sizeof(bGPDspoint));
@@ -351,7 +352,6 @@ static void add_stroke(Object *ob,
for (int i = 0; i < size; i++) {
gps_dst->points[i].pressure *= thickness;
gps_dst->points[i].strength *= strength;
- memset(&gps_dst->points[i].runtime, 0, sizeof(bGPDspoint_Runtime));
}
if (gps->dvert != NULL) {
@@ -378,6 +378,8 @@ static void add_stroke_cyclic(Object *ob,
{
bGPdata *gpd = ob->data;
bGPDstroke *gps_dst = BKE_gpencil_stroke_new(mat_nr, size * 2, gps->thickness);
+ gps_dst->runtime.gps_orig = gps->runtime.gps_orig;
+
if (gps->dvert != NULL) {
gps_dst->dvert = MEM_malloc_arrayN(size * 2, sizeof(MDeformVert), __func__);
}
@@ -387,7 +389,16 @@ static void add_stroke_cyclic(Object *ob,
int b = (point_index + i) % gps->totpoints;
gps_dst->points[i] = gps->points[a];
+ bGPDspoint *pt_dst = &gps_dst->points[i];
+ bGPDspoint *pt_orig = &gps->points[a];
+ pt_dst->runtime.pt_orig = pt_orig->runtime.pt_orig;
+ pt_dst->runtime.idx_orig = pt_orig->runtime.idx_orig;
+
gps_dst->points[size + i] = gps->points[b];
+ pt_dst = &gps_dst->points[size + i];
+ pt_orig = &gps->points[b];
+ pt_dst->runtime.pt_orig = pt_orig->runtime.pt_orig;
+ pt_dst->runtime.idx_orig = pt_orig->runtime.idx_orig;
if (gps->dvert != NULL) {
BKE_defvert_array_copy(&gps_dst->dvert[i], &gps->dvert[a], 1);
@@ -417,15 +428,23 @@ static void add_stroke_simple(Object *ob,
{
bGPdata *gpd = ob->data;
bGPDstroke *gps_dst = BKE_gpencil_stroke_new(mat_nr, 2, gps->thickness);
+ gps_dst->runtime.gps_orig = gps->runtime.gps_orig;
gps_dst->points[0] = gps->points[connection_index];
gps_dst->points[0].pressure *= thickness;
gps_dst->points[0].strength *= strength;
- memset(&gps_dst->points[0].runtime, 0, sizeof(bGPDspoint_Runtime));
+ bGPDspoint *pt_dst = &gps_dst->points[0];
+ bGPDspoint *pt_orig = &gps->points[connection_index];
+ pt_dst->runtime.pt_orig = pt_orig->runtime.pt_orig;
+ pt_dst->runtime.idx_orig = pt_orig->runtime.idx_orig;
+
gps_dst->points[1] = gps->points[point_index];
gps_dst->points[1].pressure *= thickness;
gps_dst->points[1].strength *= strength;
- memset(&gps_dst->points[1].runtime, 0, sizeof(bGPDspoint_Runtime));
+ pt_dst = &gps_dst->points[1];
+ pt_orig = &gps->points[point_index];
+ pt_dst->runtime.pt_orig = pt_orig->runtime.pt_orig;
+ pt_dst->runtime.idx_orig = pt_orig->runtime.idx_orig;
if (gps->dvert != NULL) {
gps_dst->dvert = MEM_malloc_arrayN(2, sizeof(MDeformVert), __func__);
diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt
index 3e12426acdc..98d15a1024c 100644
--- a/source/blender/gpu/CMakeLists.txt
+++ b/source/blender/gpu/CMakeLists.txt
@@ -46,7 +46,7 @@ set(SRC
intern/gpu_batch_utils.c
intern/gpu_buffers.c
intern/gpu_capabilities.cc
- intern/gpu_codegen.c
+ intern/gpu_codegen.cc
intern/gpu_compute.cc
intern/gpu_context.cc
intern/gpu_debug.cc
@@ -57,7 +57,6 @@ set(SRC
intern/gpu_index_buffer.cc
intern/gpu_init_exit.c
intern/gpu_material.c
- intern/gpu_material_library.c
intern/gpu_matrix.cc
intern/gpu_node_graph.c
intern/gpu_platform.cc
@@ -312,6 +311,7 @@ set(GLSL_SRC
shaders/material/gpu_shader_material_glass.glsl
shaders/material/gpu_shader_material_glossy.glsl
shaders/material/gpu_shader_material_hair_info.glsl
+ shaders/material/gpu_shader_material_hair.glsl
shaders/material/gpu_shader_material_hash.glsl
shaders/material/gpu_shader_material_holdout.glsl
shaders/material/gpu_shader_material_hue_sat_val.glsl
diff --git a/source/blender/gpu/GPU_immediate_util.h b/source/blender/gpu/GPU_immediate_util.h
index 924a735af79..cb7ca877b1d 100644
--- a/source/blender/gpu/GPU_immediate_util.h
+++ b/source/blender/gpu/GPU_immediate_util.h
@@ -80,6 +80,8 @@ void imm_draw_circle_fill_3d(uint pos, float x, float y, float radius, int nsegm
*/
void imm_draw_circle_partial_wire_2d(
uint pos, float x, float y, float radius, int nsegments, float start, float sweep);
+void imm_draw_circle_partial_wire_3d(
+ uint pos, float x, float y, float z, float radius, int nsegments, float start, float sweep);
/**
* Draw a filled arc with the given inner and outer radius.
@@ -104,6 +106,15 @@ void imm_draw_disk_partial_fill_2d(uint pos,
int nsegments,
float start,
float sweep);
+void imm_draw_disk_partial_fill_3d(uint pos,
+ float x,
+ float y,
+ float z,
+ float rad_inner,
+ float rad_outer,
+ int nsegments,
+ float start,
+ float sweep);
/**
* Draw a lined box.
diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h
index 9b63d0feb1e..f38b9681ad7 100644
--- a/source/blender/gpu/GPU_material.h
+++ b/source/blender/gpu/GPU_material.h
@@ -13,6 +13,7 @@
#include "BLI_sys_types.h" /* for bool */
+#include "GPU_shader.h" /* for GPUShaderCreateInfo */
#include "GPU_texture.h" /* for eGPUSamplerState */
#ifdef __cplusplus
@@ -58,8 +59,6 @@ typedef enum eGPUType {
GPU_TEX2D = 1002,
GPU_TEX2D_ARRAY = 1003,
GPU_TEX3D = 1004,
- GPU_SHADOW2D = 1005,
- GPU_TEXCUBE = 1006,
/* GLSL Struct types */
GPU_CLOSURE = 1007,
@@ -68,35 +67,30 @@ typedef enum eGPUType {
GPU_ATTR = 3001,
} eGPUType;
-typedef enum eGPUBuiltin {
- GPU_VIEW_MATRIX = (1 << 0),
- GPU_OBJECT_MATRIX = (1 << 1),
- GPU_INVERSE_VIEW_MATRIX = (1 << 2),
- GPU_INVERSE_OBJECT_MATRIX = (1 << 3),
- GPU_VIEW_POSITION = (1 << 4),
- GPU_VIEW_NORMAL = (1 << 5),
- GPU_OBJECT_COLOR = (1 << 6),
- GPU_AUTO_BUMPSCALE = (1 << 7),
- GPU_CAMERA_TEXCO_FACTORS = (1 << 8),
- GPU_PARTICLE_SCALAR_PROPS = (1 << 9),
- GPU_PARTICLE_LOCATION = (1 << 10),
- GPU_PARTICLE_VELOCITY = (1 << 11),
- GPU_PARTICLE_ANG_VELOCITY = (1 << 12),
- GPU_LOC_TO_VIEW_MATRIX = (1 << 13),
- GPU_INVERSE_LOC_TO_VIEW_MATRIX = (1 << 14),
- GPU_OBJECT_INFO = (1 << 15),
- GPU_BARYCENTRIC_TEXCO = (1 << 16),
- GPU_BARYCENTRIC_DIST = (1 << 17),
- GPU_WORLD_NORMAL = (1 << 18),
-} eGPUBuiltin;
-
-typedef enum eGPUMatFlag {
+typedef enum eGPUMaterialFlag {
GPU_MATFLAG_DIFFUSE = (1 << 0),
- GPU_MATFLAG_GLOSSY = (1 << 1),
- GPU_MATFLAG_REFRACT = (1 << 2),
- GPU_MATFLAG_SSS = (1 << 3),
- GPU_MATFLAG_BARYCENTRIC = (1 << 4),
-} eGPUMatFlag;
+ GPU_MATFLAG_SUBSURFACE = (1 << 1),
+ GPU_MATFLAG_GLOSSY = (1 << 2),
+ GPU_MATFLAG_REFRACT = (1 << 3),
+ GPU_MATFLAG_EMISSION = (1 << 4),
+ GPU_MATFLAG_TRANSPARENT = (1 << 5),
+ GPU_MATFLAG_HOLDOUT = (1 << 6),
+ GPU_MATFLAG_SHADER_TO_RGBA = (1 << 7),
+ GPU_MATFLAG_AO = (1 << 8),
+
+ GPU_MATFLAG_OBJECT_INFO = (1 << 10),
+ GPU_MATFLAG_AOV = (1 << 11),
+
+ GPU_MATFLAG_BARYCENTRIC = (1 << 20),
+
+ /* Tells the render engine the material was just compiled or updated. */
+ GPU_MATFLAG_UPDATED = (1 << 29),
+
+ /* HACK(fclem) Tells the environment texture node to not bail out if empty. */
+ GPU_MATFLAG_LOOKDEV_HACK = (1 << 30),
+} eGPUMaterialFlag;
+
+ENUM_OPERATORS(eGPUMaterialFlag, GPU_MATFLAG_LOOKDEV_HACK);
typedef struct GPUNodeStack {
eGPUType type;
@@ -110,6 +104,7 @@ typedef struct GPUNodeStack {
typedef enum eGPUMaterialStatus {
GPU_MAT_FAILED = 0,
+ GPU_MAT_CREATED,
GPU_MAT_QUEUED,
GPU_MAT_SUCCESS,
} eGPUMaterialStatus;
@@ -119,12 +114,19 @@ typedef enum eGPUVolumeDefaultValue {
GPU_VOLUME_DEFAULT_1,
} eGPUVolumeDefaultValue;
-typedef void (*GPUMaterialEvalCallbackFn)(GPUMaterial *mat,
- int options,
- const char **vert_code,
- const char **geom_code,
- const char **frag_lib,
- const char **defines);
+typedef struct GPUCodegenOutput {
+ char *attr_load;
+ /* Nodetree functions calls. */
+ char *displacement;
+ char *surface;
+ char *volume;
+ char *thickness;
+ char *material_functions;
+
+ GPUShaderCreateInfo *create_info;
+} GPUCodegenOutput;
+
+typedef void (*GPUCodegenCallbackFn)(void *thunk, GPUMaterial *mat, GPUCodegenOutput *codegen);
GPUNodeLink *GPU_constant(const float *num);
GPUNodeLink *GPU_uniform(const float *num);
@@ -143,7 +145,12 @@ GPUNodeLink *GPU_color_band(GPUMaterial *mat, int size, float *pixels, float *ro
GPUNodeLink *GPU_volume_grid(GPUMaterial *mat,
const char *name,
eGPUVolumeDefaultValue default_value);
-GPUNodeLink *GPU_builtin(eGPUBuiltin builtin);
+/**
+ * Create an implementation defined differential calculation of a float function.
+ * The given function should return a float.
+ * The result will be a vec2 containing dFdx and dFdy result of that function.
+ */
+GPUNodeLink *GPU_differentiate_float_function(const char *function_name);
bool GPU_link(GPUMaterial *mat, const char *name, ...);
bool GPU_stack_link(GPUMaterial *mat,
@@ -157,10 +164,26 @@ GPUNodeLink *GPU_uniformbuf_link_out(struct GPUMaterial *mat,
struct GPUNodeStack *stack,
int index);
-void GPU_material_output_link(GPUMaterial *material, GPUNodeLink *link);
+void GPU_material_output_surface(GPUMaterial *material, GPUNodeLink *link);
+void GPU_material_output_volume(GPUMaterial *material, GPUNodeLink *link);
+void GPU_material_output_displacement(GPUMaterial *material, GPUNodeLink *link);
+void GPU_material_output_thickness(GPUMaterial *material, GPUNodeLink *link);
+
void GPU_material_add_output_link_aov(GPUMaterial *material, GPUNodeLink *link, int hash);
-void GPU_material_sss_profile_create(GPUMaterial *material, float radii[3]);
+/**
+ * Wrap a part of the material graph into a function. You need then need to call the function by
+ * using something like #GPU_differentiate_float_function.
+ * \note This replace the link by a constant to break the link with the main graph.
+ * \param return_type: sub function return type. Output is cast to this type.
+ * \param link: link to use as the sub function output.
+ * \return the name of the generated function.
+ */
+char *GPU_material_split_sub_function(GPUMaterial *material,
+ eGPUType return_type,
+ GPUNodeLink **link);
+
+bool GPU_material_sss_profile_create(GPUMaterial *material, float radii[3]);
struct GPUUniformBuf *GPU_material_sss_profile_get(GPUMaterial *material,
int sample_len,
struct GPUTexture **tex_profile);
@@ -180,15 +203,13 @@ GPUMaterial *GPU_material_from_nodetree(struct Scene *scene,
struct Material *ma,
struct bNodeTree *ntree,
struct ListBase *gpumaterials,
- const void *engine_type,
- int options,
- bool is_volume_shader,
- const char *vert_code,
- const char *geom_code,
- const char *frag_lib,
- const char *defines,
const char *name,
- GPUMaterialEvalCallbackFn callback);
+ uint64_t shader_uuid,
+ bool is_volume_shader,
+ bool is_lookdev,
+ GPUCodegenCallbackFn callback,
+ void *thunk);
+
void GPU_material_compile(GPUMaterial *mat);
void GPU_material_free(struct ListBase *gpumaterial);
@@ -205,6 +226,7 @@ struct Material *GPU_material_get_material(GPUMaterial *material);
* Return true if the material compilation has not yet begin or begin.
*/
eGPUMaterialStatus GPU_material_status(GPUMaterial *mat);
+void GPU_material_status_set(GPUMaterial *mat, eGPUMaterialStatus status);
struct GPUUniformBuf *GPU_material_uniform_buffer_get(GPUMaterial *material);
/**
@@ -215,13 +237,15 @@ struct GPUUniformBuf *GPU_material_uniform_buffer_get(GPUMaterial *material);
void GPU_material_uniform_buffer_create(GPUMaterial *material, ListBase *inputs);
struct GPUUniformBuf *GPU_material_create_sss_profile_ubo(void);
+bool GPU_material_is_volume_shader(GPUMaterial *mat);
bool GPU_material_has_surface_output(GPUMaterial *mat);
bool GPU_material_has_volume_output(GPUMaterial *mat);
-bool GPU_material_is_volume_shader(GPUMaterial *mat);
-
-void GPU_material_flag_set(GPUMaterial *mat, eGPUMatFlag flag);
-bool GPU_material_flag_get(GPUMaterial *mat, eGPUMatFlag flag);
+void GPU_material_flag_set(GPUMaterial *mat, eGPUMaterialFlag flag);
+bool GPU_material_flag_get(const GPUMaterial *mat, eGPUMaterialFlag flag);
+eGPUMaterialFlag GPU_material_flag(const GPUMaterial *mat);
+bool GPU_material_recalc_flag_get(GPUMaterial *mat);
+uint64_t GPU_material_uuid_get(GPUMaterial *mat);
void GPU_pass_cache_init(void);
void GPU_pass_cache_garbage_collect(void);
diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h
index c0c25022836..eed7685bf01 100644
--- a/source/blender/gpu/GPU_shader.h
+++ b/source/blender/gpu/GPU_shader.h
@@ -56,6 +56,7 @@ GPUShader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info);
GPUShader *GPU_shader_create_from_info_name(const char *info_name);
const GPUShaderCreateInfo *GPU_shader_create_info_get(const char *info_name);
+bool GPU_shader_create_info_check_error(const GPUShaderCreateInfo *_info, char r_error[128]);
struct GPU_ShaderCreateFromArray_Params {
const char **vert, **geom, **frag, **defs;
diff --git a/source/blender/gpu/GPU_uniform_buffer.h b/source/blender/gpu/GPU_uniform_buffer.h
index c47be05a646..f78719d1963 100644
--- a/source/blender/gpu/GPU_uniform_buffer.h
+++ b/source/blender/gpu/GPU_uniform_buffer.h
@@ -42,8 +42,8 @@ void GPU_uniformbuf_bind(GPUUniformBuf *ubo, int slot);
void GPU_uniformbuf_unbind(GPUUniformBuf *ubo);
void GPU_uniformbuf_unbind_all(void);
-#define GPU_UBO_BLOCK_NAME "nodeTree"
-#define GPU_ATTRIBUTE_UBO_BLOCK_NAME "uniformAttrs"
+#define GPU_UBO_BLOCK_NAME "node_tree"
+#define GPU_ATTRIBUTE_UBO_BLOCK_NAME "unf_attrs"
#ifdef __cplusplus
}
diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c
deleted file mode 100644
index e462308d3ee..00000000000
--- a/source/blender/gpu/intern/gpu_codegen.c
+++ /dev/null
@@ -1,1123 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later
- * Copyright 2005 Blender Foundation. All rights reserved. */
-
-/** \file
- * \ingroup gpu
- *
- * Convert material node-trees to GLSL.
- */
-
-#include "MEM_guardedalloc.h"
-
-#include "DNA_customdata_types.h"
-#include "DNA_image_types.h"
-
-#include "BLI_blenlib.h"
-#include "BLI_dynstr.h"
-#include "BLI_ghash.h"
-#include "BLI_hash_mm2a.h"
-#include "BLI_link_utils.h"
-#include "BLI_threads.h"
-#include "BLI_utildefines.h"
-
-#include "PIL_time.h"
-
-#include "BKE_material.h"
-
-#include "GPU_capabilities.h"
-#include "GPU_material.h"
-#include "GPU_shader.h"
-#include "GPU_uniform_buffer.h"
-#include "GPU_vertex_format.h"
-
-#include "BLI_sys_types.h" /* for intptr_t support */
-
-#include "gpu_codegen.h"
-#include "gpu_material_library.h"
-#include "gpu_node_graph.h"
-
-#include <stdarg.h>
-#include <string.h>
-
-extern char datatoc_gpu_shader_codegen_lib_glsl[];
-extern char datatoc_gpu_shader_common_obinfos_lib_glsl[];
-
-/* -------------------- GPUPass Cache ------------------ */
-/**
- * Internal shader cache: This prevent the shader recompilation / stall when
- * using undo/redo AND also allows for GPUPass reuse if the Shader code is the
- * same for 2 different Materials. Unused GPUPasses are free by Garbage collection.
- */
-
-/* Only use one linked-list that contains the GPUPasses grouped by hash. */
-static GPUPass *pass_cache = NULL;
-static SpinLock pass_cache_spin;
-
-static uint32_t gpu_pass_hash(const char *frag_gen, const char *defs, ListBase *attributes)
-{
- BLI_HashMurmur2A hm2a;
- BLI_hash_mm2a_init(&hm2a, 0);
- BLI_hash_mm2a_add(&hm2a, (uchar *)frag_gen, strlen(frag_gen));
- LISTBASE_FOREACH (GPUMaterialAttribute *, attr, attributes) {
- BLI_hash_mm2a_add(&hm2a, (uchar *)attr->name, strlen(attr->name));
- }
- if (defs) {
- BLI_hash_mm2a_add(&hm2a, (uchar *)defs, strlen(defs));
- }
-
- return BLI_hash_mm2a_end(&hm2a);
-}
-
-/* Search by hash only. Return first pass with the same hash.
- * There is hash collision if (pass->next && pass->next->hash == hash) */
-static GPUPass *gpu_pass_cache_lookup(uint32_t hash)
-{
- BLI_spin_lock(&pass_cache_spin);
- /* Could be optimized with a Lookup table. */
- for (GPUPass *pass = pass_cache; pass; pass = pass->next) {
- if (pass->hash == hash) {
- BLI_spin_unlock(&pass_cache_spin);
- return pass;
- }
- }
- BLI_spin_unlock(&pass_cache_spin);
- return NULL;
-}
-
-/* Check all possible passes with the same hash. */
-static GPUPass *gpu_pass_cache_resolve_collision(GPUPass *pass,
- const char *vert,
- const char *geom,
- const char *frag,
- const char *defs,
- uint32_t hash)
-{
- BLI_spin_lock(&pass_cache_spin);
- /* Collision, need to `strcmp` the whole shader. */
- for (; pass && (pass->hash == hash); pass = pass->next) {
- if ((defs != NULL) && (!STREQ(pass->defines, defs))) { /* Pass */
- }
- else if ((geom != NULL) && (!STREQ(pass->geometrycode, geom))) { /* Pass */
- }
- else if ((!STREQ(pass->fragmentcode, frag) == 0) && (STREQ(pass->vertexcode, vert))) {
- BLI_spin_unlock(&pass_cache_spin);
- return pass;
- }
- }
- BLI_spin_unlock(&pass_cache_spin);
- return NULL;
-}
-
-/* GLSL code generation */
-
-static void codegen_convert_datatype(DynStr *ds, int from, int to, const char *tmp, int id)
-{
- char name[1024];
-
- BLI_snprintf(name, sizeof(name), "%s%d", tmp, id);
-
- if (from == to) {
- BLI_dynstr_append(ds, name);
- }
- else if (to == GPU_FLOAT) {
- if (from == GPU_VEC4) {
- BLI_dynstr_appendf(ds, "dot(%s.rgb, vec3(0.2126, 0.7152, 0.0722))", name);
- }
- else if (from == GPU_VEC3) {
- BLI_dynstr_appendf(ds, "(%s.r + %s.g + %s.b) / 3.0", name, name, name);
- }
- else if (from == GPU_VEC2) {
- BLI_dynstr_appendf(ds, "%s.r", name);
- }
- }
- else if (to == GPU_VEC2) {
- if (from == GPU_VEC4) {
- BLI_dynstr_appendf(ds, "vec2((%s.r + %s.g + %s.b) / 3.0, %s.a)", name, name, name, name);
- }
- else if (from == GPU_VEC3) {
- BLI_dynstr_appendf(ds, "vec2((%s.r + %s.g + %s.b) / 3.0, 1.0)", name, name, name);
- }
- else if (from == GPU_FLOAT) {
- BLI_dynstr_appendf(ds, "vec2(%s, 1.0)", name);
- }
- }
- else if (to == GPU_VEC3) {
- if (from == GPU_VEC4) {
- BLI_dynstr_appendf(ds, "%s.rgb", name);
- }
- else if (from == GPU_VEC2) {
- BLI_dynstr_appendf(ds, "vec3(%s.r, %s.r, %s.r)", name, name, name);
- }
- else if (from == GPU_FLOAT) {
- BLI_dynstr_appendf(ds, "vec3(%s, %s, %s)", name, name, name);
- }
- }
- else if (to == GPU_VEC4) {
- if (from == GPU_VEC3) {
- BLI_dynstr_appendf(ds, "vec4(%s, 1.0)", name);
- }
- else if (from == GPU_VEC2) {
- BLI_dynstr_appendf(ds, "vec4(%s.r, %s.r, %s.r, %s.g)", name, name, name, name);
- }
- else if (from == GPU_FLOAT) {
- BLI_dynstr_appendf(ds, "vec4(%s, %s, %s, 1.0)", name, name, name);
- }
- }
- else if (to == GPU_CLOSURE) {
- if (from == GPU_VEC4) {
- BLI_dynstr_appendf(ds, "closure_emission(%s.rgb)", name);
- }
- else if (from == GPU_VEC3) {
- BLI_dynstr_appendf(ds, "closure_emission(%s.rgb)", name);
- }
- else if (from == GPU_VEC2) {
- BLI_dynstr_appendf(ds, "closure_emission(%s.rrr)", name);
- }
- else if (from == GPU_FLOAT) {
- BLI_dynstr_appendf(ds, "closure_emission(vec3(%s, %s, %s))", name, name, name);
- }
- }
- else {
- BLI_dynstr_append(ds, name);
- }
-}
-
-static void codegen_print_datatype(DynStr *ds, const eGPUType type, float *data)
-{
- int i;
-
- BLI_dynstr_appendf(ds, "%s(", gpu_data_type_to_string(type));
-
- for (i = 0; i < type; i++) {
- BLI_dynstr_appendf(ds, "%.12f", data[i]);
- if (i == type - 1) {
- BLI_dynstr_append(ds, ")");
- }
- else {
- BLI_dynstr_append(ds, ", ");
- }
- }
-}
-
-static const char *gpu_builtin_name(eGPUBuiltin builtin)
-{
- if (builtin == GPU_VIEW_MATRIX) {
- return "unfviewmat";
- }
- if (builtin == GPU_OBJECT_MATRIX) {
- return "unfobmat";
- }
- if (builtin == GPU_INVERSE_VIEW_MATRIX) {
- return "unfinvviewmat";
- }
- if (builtin == GPU_INVERSE_OBJECT_MATRIX) {
- return "unfinvobmat";
- }
- if (builtin == GPU_LOC_TO_VIEW_MATRIX) {
- return "unflocaltoviewmat";
- }
- if (builtin == GPU_INVERSE_LOC_TO_VIEW_MATRIX) {
- return "unfinvlocaltoviewmat";
- }
- if (builtin == GPU_VIEW_POSITION) {
- return "varposition";
- }
- if (builtin == GPU_WORLD_NORMAL) {
- return "varwnormal";
- }
- if (builtin == GPU_VIEW_NORMAL) {
- return "varnormal";
- }
- if (builtin == GPU_OBJECT_COLOR) {
- return "unfobjectcolor";
- }
- if (builtin == GPU_AUTO_BUMPSCALE) {
- return "unfobautobumpscale";
- }
- if (builtin == GPU_CAMERA_TEXCO_FACTORS) {
- return "unfcameratexfactors";
- }
- if (builtin == GPU_PARTICLE_SCALAR_PROPS) {
- return "unfparticlescalarprops";
- }
- if (builtin == GPU_PARTICLE_LOCATION) {
- return "unfparticleco";
- }
- if (builtin == GPU_PARTICLE_VELOCITY) {
- return "unfparticlevel";
- }
- if (builtin == GPU_PARTICLE_ANG_VELOCITY) {
- return "unfparticleangvel";
- }
- if (builtin == GPU_OBJECT_INFO) {
- return "unfobjectinfo";
- }
- if (builtin == GPU_BARYCENTRIC_TEXCO) {
- return "unfbarycentrictex";
- }
- if (builtin == GPU_BARYCENTRIC_DIST) {
- return "unfbarycentricdist";
- }
- return "";
-}
-
-static void codegen_set_unique_ids(GPUNodeGraph *graph)
-{
- int id = 1;
-
- LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) {
- LISTBASE_FOREACH (GPUInput *, input, &node->inputs) {
- /* set id for unique names of uniform variables */
- input->id = id++;
- }
-
- LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) {
- /* set id for unique names of tmp variables storing output */
- output->id = id++;
- }
- }
-}
-
-/**
- * It will create an UBO for GPUMaterial if there is any GPU_DYNAMIC_UBO.
- */
-static int codegen_process_uniforms_functions(GPUMaterial *material,
- DynStr *ds,
- GPUNodeGraph *graph)
-{
- const char *name;
- int builtins = 0;
- ListBase ubo_inputs = {NULL, NULL};
-
- /* Textures */
- LISTBASE_FOREACH (GPUMaterialTexture *, tex, &graph->textures) {
- if (tex->colorband) {
- BLI_dynstr_appendf(ds, "uniform sampler1DArray %s;\n", tex->sampler_name);
- }
- else if (tex->tiled_mapping_name[0]) {
- BLI_dynstr_appendf(ds, "uniform sampler2DArray %s;\n", tex->sampler_name);
- BLI_dynstr_appendf(ds, "uniform sampler1DArray %s;\n", tex->tiled_mapping_name);
- }
- else {
- BLI_dynstr_appendf(ds, "uniform sampler2D %s;\n", tex->sampler_name);
- }
- }
-
- /* Volume Grids */
- LISTBASE_FOREACH (GPUMaterialVolumeGrid *, grid, &graph->volume_grids) {
- BLI_dynstr_appendf(ds, "uniform sampler3D %s;\n", grid->sampler_name);
- BLI_dynstr_appendf(ds, "uniform mat4 %s = mat4(0.0);\n", grid->transform_name);
- }
-
- /* Print other uniforms */
-
- LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) {
- LISTBASE_FOREACH (GPUInput *, input, &node->inputs) {
- if (input->source == GPU_SOURCE_BUILTIN) {
- /* only define each builtin uniform/varying once */
- if (!(builtins & input->builtin)) {
- builtins |= input->builtin;
- name = gpu_builtin_name(input->builtin);
-
- if (BLI_str_startswith(name, "unf")) {
- BLI_dynstr_appendf(ds, "uniform %s %s;\n", gpu_data_type_to_string(input->type), name);
- }
- else {
- BLI_dynstr_appendf(ds, "in %s %s;\n", gpu_data_type_to_string(input->type), name);
- }
- }
- }
- else if (input->source == GPU_SOURCE_STRUCT) {
- /* Add other struct here if needed. */
- BLI_dynstr_appendf(ds, "Closure strct%d = CLOSURE_DEFAULT;\n", input->id);
- }
- else if (input->source == GPU_SOURCE_UNIFORM) {
- if (!input->link) {
- /* We handle the UBOuniforms separately. */
- BLI_addtail(&ubo_inputs, BLI_genericNodeN(input));
- }
- }
- else if (input->source == GPU_SOURCE_CONSTANT) {
- BLI_dynstr_appendf(
- ds, "const %s cons%d = ", gpu_data_type_to_string(input->type), input->id);
- codegen_print_datatype(ds, input->type, input->vec);
- BLI_dynstr_append(ds, ";\n");
- }
- }
- }
-
- /* Handle the UBO block separately. */
- if ((material != NULL) && !BLI_listbase_is_empty(&ubo_inputs)) {
- GPU_material_uniform_buffer_create(material, &ubo_inputs);
-
- /* Inputs are sorted */
- BLI_dynstr_appendf(ds, "\nlayout (std140) uniform %s {\n", GPU_UBO_BLOCK_NAME);
-
- LISTBASE_FOREACH (LinkData *, link, &ubo_inputs) {
- GPUInput *input = (GPUInput *)(link->data);
- BLI_dynstr_appendf(ds, " %s unf%d;\n", gpu_data_type_to_string(input->type), input->id);
- }
- BLI_dynstr_append(ds, "};\n");
- BLI_freelistN(&ubo_inputs);
- }
-
- /* Generate the uniform attribute UBO if necessary. */
- if (!BLI_listbase_is_empty(&graph->uniform_attrs.list)) {
- BLI_dynstr_append(ds, "\nstruct UniformAttributes {\n");
- LISTBASE_FOREACH (GPUUniformAttr *, attr, &graph->uniform_attrs.list) {
- BLI_dynstr_appendf(ds, " vec4 attr%d;\n", attr->id);
- }
- BLI_dynstr_append(ds, "};\n");
- BLI_dynstr_appendf(ds, "layout (std140) uniform %s {\n", GPU_ATTRIBUTE_UBO_BLOCK_NAME);
- BLI_dynstr_append(ds, " UniformAttributes uniform_attrs[DRW_RESOURCE_CHUNK_LEN];\n");
- BLI_dynstr_append(ds, "};\n");
- BLI_dynstr_append(ds, "#define GET_UNIFORM_ATTR(name) (uniform_attrs[resource_id].name)\n");
- }
-
- BLI_dynstr_append(ds, "\n");
-
- return builtins;
-}
-
-static void codegen_declare_tmps(DynStr *ds, GPUNodeGraph *graph)
-{
- LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) {
- /* declare temporary variables for node output storage */
- LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) {
- if (output->type == GPU_CLOSURE) {
- BLI_dynstr_appendf(ds, " Closure tmp%d;\n", output->id);
- }
- else {
- BLI_dynstr_appendf(ds, " %s tmp%d;\n", gpu_data_type_to_string(output->type), output->id);
- }
- }
- }
- BLI_dynstr_append(ds, "\n");
-}
-
-static void codegen_call_functions(DynStr *ds, GPUNodeGraph *graph)
-{
- LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) {
- BLI_dynstr_appendf(ds, " %s(", node->name);
-
- LISTBASE_FOREACH (GPUInput *, input, &node->inputs) {
- if (input->source == GPU_SOURCE_TEX) {
- BLI_dynstr_append(ds, input->texture->sampler_name);
- }
- else if (input->source == GPU_SOURCE_TEX_TILED_MAPPING) {
- BLI_dynstr_append(ds, input->texture->tiled_mapping_name);
- }
- else if (input->source == GPU_SOURCE_VOLUME_GRID) {
- BLI_dynstr_append(ds, input->volume_grid->sampler_name);
- }
- else if (input->source == GPU_SOURCE_VOLUME_GRID_TRANSFORM) {
- BLI_dynstr_append(ds, input->volume_grid->transform_name);
- }
- else if (input->source == GPU_SOURCE_OUTPUT) {
- codegen_convert_datatype(
- ds, input->link->output->type, input->type, "tmp", input->link->output->id);
- }
- else if (input->source == GPU_SOURCE_BUILTIN) {
- /* TODO(fclem): get rid of that. */
- if (input->builtin == GPU_INVERSE_VIEW_MATRIX) {
- BLI_dynstr_append(ds, "viewinv");
- }
- else if (input->builtin == GPU_VIEW_MATRIX) {
- BLI_dynstr_append(ds, "viewmat");
- }
- else if (input->builtin == GPU_CAMERA_TEXCO_FACTORS) {
- BLI_dynstr_append(ds, "camtexfac");
- }
- else if (input->builtin == GPU_LOC_TO_VIEW_MATRIX) {
- BLI_dynstr_append(ds, "localtoviewmat");
- }
- else if (input->builtin == GPU_INVERSE_LOC_TO_VIEW_MATRIX) {
- BLI_dynstr_append(ds, "invlocaltoviewmat");
- }
- else if (input->builtin == GPU_BARYCENTRIC_DIST) {
- BLI_dynstr_append(ds, "barycentricDist");
- }
- else if (input->builtin == GPU_BARYCENTRIC_TEXCO) {
- BLI_dynstr_append(ds, "barytexco");
- }
- else if (input->builtin == GPU_OBJECT_MATRIX) {
- BLI_dynstr_append(ds, "objmat");
- }
- else if (input->builtin == GPU_OBJECT_INFO) {
- BLI_dynstr_append(ds, "ObjectInfo");
- }
- else if (input->builtin == GPU_OBJECT_COLOR) {
- BLI_dynstr_append(ds, "ObjectColor");
- }
- else if (input->builtin == GPU_INVERSE_OBJECT_MATRIX) {
- BLI_dynstr_append(ds, "objinv");
- }
- else if (input->builtin == GPU_VIEW_POSITION) {
- BLI_dynstr_append(ds, "viewposition");
- }
- else if (input->builtin == GPU_VIEW_NORMAL) {
- BLI_dynstr_append(ds, "facingnormal");
- }
- else if (input->builtin == GPU_WORLD_NORMAL) {
- BLI_dynstr_append(ds, "facingwnormal");
- }
- else {
- BLI_dynstr_append(ds, gpu_builtin_name(input->builtin));
- }
- }
- else if (input->source == GPU_SOURCE_STRUCT) {
- BLI_dynstr_appendf(ds, "strct%d", input->id);
- }
- else if (input->source == GPU_SOURCE_UNIFORM) {
- BLI_dynstr_appendf(ds, "unf%d", input->id);
- }
- else if (input->source == GPU_SOURCE_CONSTANT) {
- BLI_dynstr_appendf(ds, "cons%d", input->id);
- }
- else if (input->source == GPU_SOURCE_ATTR) {
- codegen_convert_datatype(ds, input->attr->gputype, input->type, "var", input->attr->id);
- }
- else if (input->source == GPU_SOURCE_UNIFORM_ATTR) {
- BLI_dynstr_appendf(ds, "GET_UNIFORM_ATTR(attr%d)", input->uniform_attr->id);
- }
-
- BLI_dynstr_append(ds, ", ");
- }
-
- LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) {
- BLI_dynstr_appendf(ds, "tmp%d", output->id);
- if (output->next) {
- BLI_dynstr_append(ds, ", ");
- }
- }
-
- BLI_dynstr_append(ds, ");\n");
- }
-}
-
-static void codegen_final_output(DynStr *ds, GPUOutput *finaloutput)
-{
- BLI_dynstr_appendf(ds, "return tmp%d;\n", finaloutput->id);
-}
-
-static char *code_generate_fragment(GPUMaterial *material,
- GPUNodeGraph *graph,
- const char *interface_str)
-{
- DynStr *ds = BLI_dynstr_new();
- char *code;
- int builtins;
-
- codegen_set_unique_ids(graph);
-
- /* Attributes, Shader stage interface. */
- if (interface_str) {
- BLI_dynstr_appendf(ds, "in codegenInterface {%s};\n\n", interface_str);
- }
-
- builtins = codegen_process_uniforms_functions(material, ds, graph);
-
- if (builtins & (GPU_OBJECT_INFO | GPU_OBJECT_COLOR)) {
- BLI_dynstr_append(ds, datatoc_gpu_shader_common_obinfos_lib_glsl);
- }
-
- if (builtins & GPU_BARYCENTRIC_TEXCO) {
- BLI_dynstr_append(ds, datatoc_gpu_shader_codegen_lib_glsl);
- }
-
- BLI_dynstr_append(ds, "Closure nodetree_exec(void)\n{\n");
-
- if (builtins & GPU_BARYCENTRIC_TEXCO) {
- BLI_dynstr_append(ds, " vec2 barytexco = barycentric_resolve(barycentricTexCo);\n");
- }
- /* TODO(fclem): get rid of that. */
- if (builtins & GPU_VIEW_MATRIX) {
- BLI_dynstr_append(ds, " #define viewmat ViewMatrix\n");
- }
- if (builtins & GPU_CAMERA_TEXCO_FACTORS) {
- BLI_dynstr_append(ds, " #define camtexfac CameraTexCoFactors\n");
- }
- if (builtins & GPU_OBJECT_MATRIX) {
- BLI_dynstr_append(ds, " #define objmat ModelMatrix\n");
- }
- if (builtins & GPU_INVERSE_OBJECT_MATRIX) {
- BLI_dynstr_append(ds, " #define objinv ModelMatrixInverse\n");
- }
- if (builtins & GPU_INVERSE_VIEW_MATRIX) {
- BLI_dynstr_append(ds, " #define viewinv ViewMatrixInverse\n");
- }
- if (builtins & GPU_LOC_TO_VIEW_MATRIX) {
- BLI_dynstr_append(ds, " #define localtoviewmat (ViewMatrix * ModelMatrix)\n");
- }
- if (builtins & GPU_INVERSE_LOC_TO_VIEW_MATRIX) {
- BLI_dynstr_append(ds,
- " #define invlocaltoviewmat (ModelMatrixInverse * ViewMatrixInverse)\n");
- }
- if (builtins & GPU_VIEW_NORMAL) {
- BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n");
- BLI_dynstr_append(ds, " vec3 n;\n");
- BLI_dynstr_append(ds, " world_normals_get(n);\n");
- BLI_dynstr_append(ds, " vec3 facingnormal = transform_direction(ViewMatrix, n);\n");
- BLI_dynstr_append(ds, "#else\n");
- BLI_dynstr_append(ds, " vec3 facingnormal = gl_FrontFacing ? viewNormal: -viewNormal;\n");
- BLI_dynstr_append(ds, "#endif\n");
- }
- if (builtins & GPU_WORLD_NORMAL) {
- BLI_dynstr_append(ds, " vec3 facingwnormal;\n");
- if (builtins & GPU_VIEW_NORMAL) {
- BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n");
- BLI_dynstr_append(ds, " facingwnormal = n;\n");
- BLI_dynstr_append(ds, "#else\n");
- BLI_dynstr_append(ds, " world_normals_get(facingwnormal);\n");
- BLI_dynstr_append(ds, "#endif\n");
- }
- else {
- BLI_dynstr_append(ds, " world_normals_get(facingwnormal);\n");
- }
- }
- if (builtins & GPU_VIEW_POSITION) {
- BLI_dynstr_append(ds, " #define viewposition viewPosition\n");
- }
-
- codegen_declare_tmps(ds, graph);
- codegen_call_functions(ds, graph);
-
- BLI_dynstr_append(ds, " #ifndef VOLUMETRICS\n");
- BLI_dynstr_append(ds, " if (renderPassAOV) {\n");
- BLI_dynstr_append(ds, " switch (render_pass_aov_hash()) {\n");
- GSet *aovhashes_added = BLI_gset_int_new(__func__);
- LISTBASE_FOREACH (GPUNodeGraphOutputLink *, aovlink, &graph->outlink_aovs) {
- void *aov_key = POINTER_FROM_INT(aovlink->hash);
- if (BLI_gset_haskey(aovhashes_added, aov_key)) {
- continue;
- }
- BLI_dynstr_appendf(ds, " case %d: {\n ", aovlink->hash);
- codegen_final_output(ds, aovlink->outlink->output);
- BLI_dynstr_append(ds, " }\n");
- BLI_gset_add(aovhashes_added, aov_key);
- }
- BLI_gset_free(aovhashes_added, NULL);
- BLI_dynstr_append(ds, " default: {\n");
- BLI_dynstr_append(ds, " Closure no_aov = CLOSURE_DEFAULT;\n");
- BLI_dynstr_append(ds, " no_aov.holdout = 1.0;\n");
- BLI_dynstr_append(ds, " return no_aov;\n");
- BLI_dynstr_append(ds, " }\n");
- BLI_dynstr_append(ds, " }\n");
- BLI_dynstr_append(ds, " } else {\n");
- BLI_dynstr_append(ds, " #else /* VOLUMETRICS */\n");
- BLI_dynstr_append(ds, " {\n");
- BLI_dynstr_append(ds, " #endif /* VOLUMETRICS */\n ");
- codegen_final_output(ds, graph->outlink->output);
- BLI_dynstr_append(ds, " }\n");
-
- BLI_dynstr_append(ds, "}\n");
-
- /* create shader */
- code = BLI_dynstr_get_cstring(ds);
- BLI_dynstr_free(ds);
-
-#if 0
- if (G.debug & G_DEBUG) {
- printf("%s\n", code);
- }
-#endif
-
- return code;
-}
-
-static const char *attr_prefix_get(CustomDataType type)
-{
- switch (type) {
- case CD_ORCO:
- return "orco";
- case CD_MTFACE:
- return "u";
- case CD_TANGENT:
- return "t";
- case CD_MCOL:
- case CD_MLOOPCOL:
- return "c";
- case CD_PROP_COLOR:
- return "c";
- case CD_AUTO_FROM_NAME:
- return "a";
- case CD_HAIRLENGTH:
- return "hl";
- default:
- BLI_assert_msg(0, "GPUVertAttr Prefix type not found : This should not happen!");
- return "";
- }
-}
-
-/* We talk about shader stage interface, not to be mistaken with GPUShaderInterface. */
-static char *code_generate_interface(GPUNodeGraph *graph, int builtins)
-{
- if (BLI_listbase_is_empty(&graph->attributes) &&
- (builtins & (GPU_BARYCENTRIC_DIST | GPU_BARYCENTRIC_TEXCO)) == 0) {
- return NULL;
- }
-
- DynStr *ds = BLI_dynstr_new();
-
- BLI_dynstr_append(ds, "\n");
-
- LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) {
- if (attr->type == CD_HAIRLENGTH) {
- BLI_dynstr_appendf(ds, "float var%d;\n", attr->id);
- }
- else {
- BLI_dynstr_appendf(ds, "%s var%d;\n", gpu_data_type_to_string(attr->gputype), attr->id);
- }
- }
- if (builtins & GPU_BARYCENTRIC_TEXCO) {
- BLI_dynstr_append(ds, "vec2 barycentricTexCo;\n");
- }
- if (builtins & GPU_BARYCENTRIC_DIST) {
- BLI_dynstr_append(ds, "vec3 barycentricDist;\n");
- }
-
- char *code = BLI_dynstr_get_cstring(ds);
-
- BLI_dynstr_free(ds);
-
- return code;
-}
-
-static char *code_generate_vertex(GPUNodeGraph *graph,
- const char *interface_str,
- const char *vert_code,
- int builtins)
-{
- DynStr *ds = BLI_dynstr_new();
-
- BLI_dynstr_append(ds, datatoc_gpu_shader_codegen_lib_glsl);
-
- /* Inputs */
- LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) {
- const char *type_str = gpu_data_type_to_string(attr->gputype);
- const char *prefix = attr_prefix_get(attr->type);
- /* XXX FIXME: see notes in mesh_render_data_create() */
- /* NOTE: Replicate changes to mesh_render_data_create() in draw_cache_impl_mesh.c */
- if (attr->type == CD_ORCO) {
- /* OPTI: orco is computed from local positions, but only if no modifier is present. */
- BLI_dynstr_append(ds, datatoc_gpu_shader_common_obinfos_lib_glsl);
- BLI_dynstr_append(ds, "DEFINE_ATTR(vec4, orco);\n");
- }
- else if (attr->type == CD_HAIRLENGTH) {
- BLI_dynstr_append(ds, datatoc_gpu_shader_common_obinfos_lib_glsl);
- BLI_dynstr_append(ds, "DEFINE_ATTR(float, hairLen);\n");
- }
- else if (attr->name[0] == '\0') {
- BLI_dynstr_appendf(ds, "DEFINE_ATTR(%s, %s);\n", type_str, prefix);
- BLI_dynstr_appendf(ds, "#define att%d %s\n", attr->id, prefix);
- }
- else {
- char attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
- GPU_vertformat_safe_attr_name(attr->name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
- BLI_dynstr_appendf(ds, "DEFINE_ATTR(%s, %s%s);\n", type_str, prefix, attr_safe_name);
- BLI_dynstr_appendf(ds, "#define att%d %s%s\n", attr->id, prefix, attr_safe_name);
- }
- }
-
- /* Outputs interface */
- if (interface_str) {
- BLI_dynstr_appendf(ds, "out codegenInterface {%s};\n\n", interface_str);
- }
-
- /* Prototype. Needed for hair functions. */
- BLI_dynstr_append(ds, "void pass_attr(vec3 position, mat3 normalmat, mat4 modelmatinv);\n");
- BLI_dynstr_append(ds, "#define USE_ATTR\n\n");
-
- BLI_dynstr_append(ds, vert_code);
- BLI_dynstr_append(ds, "\n\n");
-
- BLI_dynstr_append(ds, "void pass_attr(vec3 position, mat3 normalmat, mat4 modelmatinv) {\n");
-
- /* GPU_BARYCENTRIC_TEXCO cannot be computed based on gl_VertexID
- * for MESH_SHADER because of indexed drawing. In this case a
- * geometry shader is needed. */
- if (builtins & GPU_BARYCENTRIC_TEXCO) {
- BLI_dynstr_appendf(ds, " barycentricTexCo = barycentric_get();\n");
- }
- if (builtins & GPU_BARYCENTRIC_DIST) {
- BLI_dynstr_appendf(ds, " barycentricDist = vec3(0);\n");
- }
-
- LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) {
- if (attr->type == CD_TANGENT) { /* silly exception */
- BLI_dynstr_appendf(ds, " var%d = tangent_get(att%d, normalmat);\n", attr->id, attr->id);
- }
- else if (attr->type == CD_ORCO) {
- BLI_dynstr_appendf(
- ds, " var%d = orco_get(position, modelmatinv, OrcoTexCoFactors, orco);\n", attr->id);
- }
- else if (attr->type == CD_HAIRLENGTH) {
- BLI_dynstr_appendf(ds, " var%d = hair_len_get(hair_get_strand_id(), hairLen);\n", attr->id);
- }
- else {
- const char *type_str = gpu_data_type_to_string(attr->gputype);
- BLI_dynstr_appendf(ds, " var%d = GET_ATTR(%s, att%d);\n", attr->id, type_str, attr->id);
- }
- }
-
- BLI_dynstr_append(ds, "}\n");
-
- char *code = BLI_dynstr_get_cstring(ds);
-
- BLI_dynstr_free(ds);
-
-#if 0
- if (G.debug & G_DEBUG) {
- printf("%s\n", code);
- }
-#endif
-
- return code;
-}
-
-static char *code_generate_geometry(GPUNodeGraph *graph,
- const char *interface_str,
- const char *geom_code,
- int builtins)
-{
- if (!geom_code) {
- return NULL;
- }
-
- DynStr *ds = BLI_dynstr_new();
-
- /* Attributes, Shader interface; */
- if (interface_str) {
- BLI_dynstr_appendf(ds, "in codegenInterface {%s} dataAttrIn[];\n\n", interface_str);
- BLI_dynstr_appendf(ds, "out codegenInterface {%s} dataAttrOut;\n\n", interface_str);
- }
-
- BLI_dynstr_append(ds, datatoc_gpu_shader_codegen_lib_glsl);
-
- if (builtins & GPU_BARYCENTRIC_DIST) {
- /* geom_code should do something with this, but may not. */
- BLI_dynstr_append(ds, "#define DO_BARYCENTRIC_DISTANCES\n");
- }
-
- /* Generate varying assignments. */
- BLI_dynstr_append(ds, "#define USE_ATTR\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");
- }
-
- 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_append(ds, "}\n\n");
-
- BLI_dynstr_append(ds, geom_code);
-
- char *code = BLI_dynstr_get_cstring(ds);
- BLI_dynstr_free(ds);
-
- return code;
-}
-
-GPUShader *GPU_pass_shader_get(GPUPass *pass)
-{
- return pass->shader;
-}
-
-/* Pass create/free */
-
-static bool gpu_pass_is_valid(GPUPass *pass)
-{
- /* Shader is not null if compilation is successful. */
- return (pass->compiled == false || pass->shader != NULL);
-}
-
-GPUPass *GPU_generate_pass(GPUMaterial *material,
- GPUNodeGraph *graph,
- const char *vert_code,
- const char *geom_code,
- const char *frag_lib,
- const char *defines)
-{
- /* Prune the unused nodes and extract attributes before compiling so the
- * generated VBOs are ready to accept the future shader. */
- gpu_node_graph_prune_unused(graph);
- gpu_node_graph_finalize_uniform_attrs(graph);
-
- int builtins = 0;
- LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) {
- LISTBASE_FOREACH (GPUInput *, input, &node->inputs) {
- if (input->source == GPU_SOURCE_BUILTIN) {
- builtins |= input->builtin;
- }
- }
- }
- /* generate code */
- char *interface_str = code_generate_interface(graph, builtins);
- char *fragmentgen = code_generate_fragment(material, graph, interface_str);
-
- /* Cache lookup: Reuse shaders already compiled */
- uint32_t hash = gpu_pass_hash(fragmentgen, defines, &graph->attributes);
- GPUPass *pass_hash = gpu_pass_cache_lookup(hash);
-
- if (pass_hash && (pass_hash->next == NULL || pass_hash->next->hash != hash)) {
- /* No collision, just return the pass. */
- MEM_SAFE_FREE(interface_str);
- MEM_freeN(fragmentgen);
- if (!gpu_pass_is_valid(pass_hash)) {
- /* Shader has already been created but failed to compile. */
- return NULL;
- }
- pass_hash->refcount += 1;
- return pass_hash;
- }
-
- /* Either the shader is not compiled or there is a hash collision...
- * continue generating the shader strings. */
- GSet *used_libraries = gpu_material_used_libraries(material);
- char *tmp = gpu_material_library_generate_code(used_libraries, frag_lib);
-
- char *geometrycode = code_generate_geometry(graph, interface_str, geom_code, builtins);
- char *vertexcode = code_generate_vertex(graph, interface_str, vert_code, builtins);
- char *fragmentcode = BLI_strdupcat(tmp, fragmentgen);
-
- MEM_SAFE_FREE(interface_str);
- MEM_freeN(fragmentgen);
- MEM_freeN(tmp);
-
- GPUPass *pass = NULL;
- if (pass_hash) {
- /* Cache lookup: Reuse shaders already compiled */
- pass = gpu_pass_cache_resolve_collision(
- pass_hash, vertexcode, geometrycode, fragmentcode, defines, hash);
- }
-
- if (pass) {
- MEM_SAFE_FREE(vertexcode);
- MEM_SAFE_FREE(fragmentcode);
- MEM_SAFE_FREE(geometrycode);
-
- /* Cache hit. Reuse the same GPUPass and GPUShader. */
- if (!gpu_pass_is_valid(pass)) {
- /* Shader has already been created but failed to compile. */
- return NULL;
- }
-
- pass->refcount += 1;
- }
- else {
- /* We still create a pass even if shader compilation
- * fails to avoid trying to compile again and again. */
- pass = MEM_callocN(sizeof(GPUPass), "GPUPass");
- pass->shader = NULL;
- pass->refcount = 1;
- pass->hash = hash;
- pass->vertexcode = vertexcode;
- pass->fragmentcode = fragmentcode;
- pass->geometrycode = geometrycode;
- pass->defines = (defines) ? BLI_strdup(defines) : NULL;
- pass->compiled = false;
-
- BLI_spin_lock(&pass_cache_spin);
- if (pass_hash != NULL) {
- /* Add after the first pass having the same hash. */
- pass->next = pass_hash->next;
- pass_hash->next = pass;
- }
- else {
- /* No other pass have same hash, just prepend to the list. */
- BLI_LINKS_PREPEND(pass_cache, pass);
- }
- BLI_spin_unlock(&pass_cache_spin);
- }
-
- return pass;
-}
-
-static int count_active_texture_sampler(GPUShader *shader, const char *source)
-{
- const char *code = source;
-
- /* Remember this is per stage. */
- GSet *sampler_ids = BLI_gset_int_new(__func__);
- int num_samplers = 0;
-
- while ((code = strstr(code, "uniform "))) {
- /* Move past "uniform". */
- code += 7;
- /* Skip following spaces. */
- while (*code == ' ') {
- code++;
- }
- /* Skip "i" from potential isamplers. */
- if (*code == 'i') {
- code++;
- }
- /* Skip following spaces. */
- if (BLI_str_startswith(code, "sampler")) {
- /* Move past "uniform". */
- code += 7;
- /* Skip sampler type suffix. */
- while (!ELEM(*code, ' ', '\0')) {
- code++;
- }
- /* Skip following spaces. */
- while (*code == ' ') {
- code++;
- }
-
- if (*code != '\0') {
- char sampler_name[64];
- code = gpu_str_skip_token(code, sampler_name, sizeof(sampler_name));
- int id = GPU_shader_get_uniform(shader, sampler_name);
-
- if (id == -1) {
- continue;
- }
- /* Catch duplicates. */
- if (BLI_gset_add(sampler_ids, POINTER_FROM_INT(id))) {
- num_samplers++;
- }
- }
- }
- }
-
- BLI_gset_free(sampler_ids, NULL);
-
- return num_samplers;
-}
-
-static bool gpu_pass_shader_validate(GPUPass *pass, GPUShader *shader)
-{
- if (shader == NULL) {
- return false;
- }
-
- /* NOTE: The only drawback of this method is that it will count a sampler
- * used in the fragment shader and only declared (but not used) in the vertex
- * shader as used by both. But this corner case is not happening for now. */
- int vert_samplers_len = count_active_texture_sampler(shader, pass->vertexcode);
- int frag_samplers_len = count_active_texture_sampler(shader, pass->fragmentcode);
-
- int total_samplers_len = vert_samplers_len + frag_samplers_len;
-
- /* Validate against opengl limit. */
- if ((frag_samplers_len > GPU_max_textures_frag()) ||
- (vert_samplers_len > GPU_max_textures_vert())) {
- return false;
- }
-
- if (pass->geometrycode) {
- int geom_samplers_len = count_active_texture_sampler(shader, pass->geometrycode);
- total_samplers_len += geom_samplers_len;
- if (geom_samplers_len > GPU_max_textures_geom()) {
- return false;
- }
- }
-
- return (total_samplers_len <= GPU_max_textures());
-}
-
-bool GPU_pass_compile(GPUPass *pass, const char *shname)
-{
- bool success = true;
- if (!pass->compiled) {
- GPUShader *shader = GPU_shader_create(
- pass->vertexcode, pass->fragmentcode, pass->geometrycode, NULL, pass->defines, shname);
-
- /* NOTE: Some drivers / gpu allows more active samplers than the opengl limit.
- * We need to make sure to count active samplers to avoid undefined behavior. */
- if (!gpu_pass_shader_validate(pass, shader)) {
- success = false;
- if (shader != NULL) {
- fprintf(stderr, "GPUShader: error: too many samplers in shader.\n");
- GPU_shader_free(shader);
- shader = NULL;
- }
- }
- pass->shader = shader;
- pass->compiled = true;
- }
-
- return success;
-}
-
-void GPU_pass_release(GPUPass *pass)
-{
- BLI_assert(pass->refcount > 0);
- pass->refcount--;
-}
-
-static void gpu_pass_free(GPUPass *pass)
-{
- BLI_assert(pass->refcount == 0);
- if (pass->shader) {
- GPU_shader_free(pass->shader);
- }
- MEM_SAFE_FREE(pass->fragmentcode);
- MEM_SAFE_FREE(pass->geometrycode);
- MEM_SAFE_FREE(pass->vertexcode);
- MEM_SAFE_FREE(pass->defines);
- MEM_freeN(pass);
-}
-
-void GPU_pass_cache_garbage_collect(void)
-{
- static int lasttime = 0;
- const int shadercollectrate = 60; /* hardcoded for now. */
- int ctime = (int)PIL_check_seconds_timer();
-
- if (ctime < shadercollectrate + lasttime) {
- return;
- }
-
- lasttime = ctime;
-
- BLI_spin_lock(&pass_cache_spin);
- GPUPass *next, **prev_pass = &pass_cache;
- for (GPUPass *pass = pass_cache; pass; pass = next) {
- next = pass->next;
- if (pass->refcount == 0) {
- /* Remove from list */
- *prev_pass = next;
- gpu_pass_free(pass);
- }
- else {
- prev_pass = &pass->next;
- }
- }
- BLI_spin_unlock(&pass_cache_spin);
-}
-
-void GPU_pass_cache_init(void)
-{
- BLI_spin_init(&pass_cache_spin);
-}
-
-void GPU_pass_cache_free(void)
-{
- BLI_spin_lock(&pass_cache_spin);
- while (pass_cache) {
- GPUPass *next = pass_cache->next;
- gpu_pass_free(pass_cache);
- pass_cache = next;
- }
- BLI_spin_unlock(&pass_cache_spin);
-
- BLI_spin_end(&pass_cache_spin);
-}
-
-/* Module */
-
-void gpu_codegen_init(void)
-{
-}
-
-void gpu_codegen_exit(void)
-{
- BKE_material_defaults_free_gpu();
- GPU_shader_free_builtin_shaders();
-}
diff --git a/source/blender/gpu/intern/gpu_codegen.cc b/source/blender/gpu/intern/gpu_codegen.cc
new file mode 100644
index 00000000000..8963fa45c96
--- /dev/null
+++ b/source/blender/gpu/intern/gpu_codegen.cc
@@ -0,0 +1,825 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2005 Blender Foundation. */
+
+/** \file
+ * \ingroup gpu
+ *
+ * Convert material node-trees to GLSL.
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_customdata_types.h"
+#include "DNA_image_types.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_dynstr.h"
+#include "BLI_ghash.h"
+#include "BLI_hash_mm2a.h"
+#include "BLI_link_utils.h"
+#include "BLI_threads.h"
+#include "BLI_utildefines.h"
+
+#include "PIL_time.h"
+
+#include "BKE_material.h"
+#include "BKE_world.h"
+
+#include "GPU_capabilities.h"
+#include "GPU_material.h"
+#include "GPU_shader.h"
+#include "GPU_uniform_buffer.h"
+#include "GPU_vertex_format.h"
+
+#include "BLI_sys_types.h" /* for intptr_t support */
+
+#include "gpu_codegen.h"
+#include "gpu_material_library.h"
+#include "gpu_node_graph.h"
+#include "gpu_shader_create_info.hh"
+#include "gpu_shader_dependency_private.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+#include <sstream>
+#include <string>
+
+using namespace blender::gpu::shader;
+
+struct GPUCodegenCreateInfo : ShaderCreateInfo {
+ struct NameBuffer {
+ char attr_names[16][GPU_MAX_SAFE_ATTR_NAME + 1];
+ char var_names[16][8];
+ };
+
+ /** Optional generated interface. */
+ StageInterfaceInfo *interface_generated = nullptr;
+ /** Optional name buffer containing names referenced by StringRefNull. */
+ NameBuffer *name_buffer = nullptr;
+
+ GPUCodegenCreateInfo(const char *name) : ShaderCreateInfo(name){};
+ ~GPUCodegenCreateInfo()
+ {
+ delete interface_generated;
+ MEM_delete(name_buffer);
+ };
+};
+
+struct GPUPass {
+ struct GPUPass *next;
+
+ GPUShader *shader;
+ GPUCodegenCreateInfo *create_info = nullptr;
+ /** Orphaned GPUPasses gets freed by the garbage collector. */
+ uint refcount;
+ /** Identity hash generated from all GLSL code. */
+ uint32_t hash;
+ /** Did we already tried to compile the attached GPUShader. */
+ bool compiled;
+};
+
+/* -------------------------------------------------------------------- */
+/** \name GPUPass Cache
+ *
+ * Internal shader cache: This prevent the shader recompilation / stall when
+ * using undo/redo AND also allows for GPUPass reuse if the Shader code is the
+ * same for 2 different Materials. Unused GPUPasses are free by Garbage collection.
+ */
+
+/* Only use one linklist that contains the GPUPasses grouped by hash. */
+static GPUPass *pass_cache = nullptr;
+static SpinLock pass_cache_spin;
+
+/* Search by hash only. Return first pass with the same hash.
+ * There is hash collision if (pass->next && pass->next->hash == hash) */
+static GPUPass *gpu_pass_cache_lookup(uint32_t hash)
+{
+ BLI_spin_lock(&pass_cache_spin);
+ /* Could be optimized with a Lookup table. */
+ for (GPUPass *pass = pass_cache; pass; pass = pass->next) {
+ if (pass->hash == hash) {
+ BLI_spin_unlock(&pass_cache_spin);
+ return pass;
+ }
+ }
+ BLI_spin_unlock(&pass_cache_spin);
+ return nullptr;
+}
+
+static void gpu_pass_cache_insert_after(GPUPass *node, GPUPass *pass)
+{
+ BLI_spin_lock(&pass_cache_spin);
+ if (node != nullptr) {
+ /* Add after the first pass having the same hash. */
+ pass->next = node->next;
+ node->next = pass;
+ }
+ else {
+ /* No other pass have same hash, just prepend to the list. */
+ BLI_LINKS_PREPEND(pass_cache, pass);
+ }
+ BLI_spin_unlock(&pass_cache_spin);
+}
+
+/* Check all possible passes with the same hash. */
+static GPUPass *gpu_pass_cache_resolve_collision(GPUPass *pass,
+ GPUShaderCreateInfo *info,
+ uint32_t hash)
+{
+ BLI_spin_lock(&pass_cache_spin);
+ for (; pass && (pass->hash == hash); pass = pass->next) {
+ if (*reinterpret_cast<ShaderCreateInfo *>(info) ==
+ *reinterpret_cast<ShaderCreateInfo *>(pass->create_info)) {
+ BLI_spin_unlock(&pass_cache_spin);
+ return pass;
+ }
+ }
+ BLI_spin_unlock(&pass_cache_spin);
+ return nullptr;
+}
+
+static bool gpu_pass_is_valid(GPUPass *pass)
+{
+ /* Shader is not null if compilation is successful. */
+ return (pass->compiled == false || pass->shader != nullptr);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Type > string conversion
+ * \{ */
+
+static std::ostream &operator<<(std::ostream &stream, const GPUInput *input)
+{
+ switch (input->source) {
+ case GPU_SOURCE_FUNCTION_CALL:
+ case GPU_SOURCE_OUTPUT:
+ return stream << "tmp" << input->id;
+ case GPU_SOURCE_CONSTANT:
+ return stream << "cons" << input->id;
+ case GPU_SOURCE_UNIFORM:
+ return stream << "node_tree.u" << input->id;
+ case GPU_SOURCE_ATTR:
+ return stream << "var_attrs.v" << input->attr->id;
+ case GPU_SOURCE_UNIFORM_ATTR:
+ return stream << "unf_attrs[resource_id].attr" << input->uniform_attr->id;
+ case GPU_SOURCE_STRUCT:
+ return stream << "strct" << input->id;
+ case GPU_SOURCE_TEX:
+ return stream << input->texture->sampler_name;
+ case GPU_SOURCE_TEX_TILED_MAPPING:
+ return stream << input->texture->tiled_mapping_name;
+ case GPU_SOURCE_VOLUME_GRID:
+ return stream << input->volume_grid->sampler_name;
+ case GPU_SOURCE_VOLUME_GRID_TRANSFORM:
+ return stream << input->volume_grid->transform_name;
+ default:
+ BLI_assert(0);
+ return stream;
+ }
+}
+
+static std::ostream &operator<<(std::ostream &stream, const GPUOutput *output)
+{
+ return stream << "tmp" << output->id;
+}
+
+/* Trick type to change overload and keep a somewhat nice syntax. */
+struct GPUConstant : public GPUInput {
+};
+
+/* Print data constructor (i.e: vec2(1.0f, 1.0f)). */
+static std::ostream &operator<<(std::ostream &stream, const GPUConstant *input)
+{
+ stream << input->type << "(";
+ for (int i = 0; i < input->type; i++) {
+ char formated_float[32];
+ /* Print with the maximum precision for single precision float using scientific notation.
+ * See https://stackoverflow.com/questions/16839658/#answer-21162120 */
+ SNPRINTF(formated_float, "%.9g", input->vec[i]);
+ stream << formated_float;
+ if (i < input->type - 1) {
+ stream << ", ";
+ }
+ }
+ stream << ")";
+ return stream;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name GLSL code generation
+ * \{ */
+
+class GPUCodegen {
+ public:
+ GPUMaterial &mat;
+ GPUNodeGraph &graph;
+ GPUCodegenOutput output = {};
+ GPUCodegenCreateInfo *create_info = nullptr;
+
+ private:
+ uint32_t hash_ = 0;
+ BLI_HashMurmur2A hm2a_;
+ ListBase ubo_inputs_ = {nullptr, nullptr};
+
+ public:
+ GPUCodegen(GPUMaterial *mat_, GPUNodeGraph *graph_) : mat(*mat_), graph(*graph_)
+ {
+ BLI_hash_mm2a_init(&hm2a_, GPU_material_uuid_get(&mat));
+ BLI_hash_mm2a_add_int(&hm2a_, GPU_material_flag(&mat));
+ create_info = new GPUCodegenCreateInfo("codegen");
+ output.create_info = reinterpret_cast<GPUShaderCreateInfo *>(
+ static_cast<ShaderCreateInfo *>(create_info));
+
+ if (GPU_material_flag_get(mat_, GPU_MATFLAG_OBJECT_INFO)) {
+ create_info->additional_info("draw_object_infos");
+ }
+ }
+
+ ~GPUCodegen()
+ {
+ MEM_SAFE_FREE(output.attr_load);
+ MEM_SAFE_FREE(output.surface);
+ MEM_SAFE_FREE(output.volume);
+ MEM_SAFE_FREE(output.thickness);
+ MEM_SAFE_FREE(output.displacement);
+ MEM_SAFE_FREE(output.material_functions);
+ delete create_info;
+ BLI_freelistN(&ubo_inputs_);
+ };
+
+ void generate_graphs();
+ void generate_uniform_buffer();
+ void generate_attribs();
+ void generate_resources();
+ void generate_library();
+
+ uint32_t hash_get() const
+ {
+ return hash_;
+ }
+
+ private:
+ void set_unique_ids();
+
+ void node_serialize(std::stringstream &eval_ss, const GPUNode *node);
+ char *graph_serialize(eGPUNodeTag tree_tag, GPUNodeLink *output_link);
+
+ static char *extract_c_str(std::stringstream &stream)
+ {
+ auto string = stream.str();
+ return BLI_strdup(string.c_str());
+ }
+};
+
+static char attr_prefix_get(CustomDataType type)
+{
+ switch (type) {
+ case CD_MTFACE:
+ return 'u';
+ case CD_TANGENT:
+ return 't';
+ case CD_MCOL:
+ case CD_MLOOPCOL:
+ return 'c';
+ case CD_PROP_COLOR:
+ return 'c';
+ case CD_AUTO_FROM_NAME:
+ return 'a';
+ case CD_HAIRLENGTH:
+ return 'l';
+ default:
+ BLI_assert_msg(0, "GPUVertAttr Prefix type not found : This should not happen!");
+ return '\0';
+ }
+}
+
+void GPUCodegen::generate_attribs()
+{
+ if (BLI_listbase_is_empty(&graph.attributes)) {
+ output.attr_load = nullptr;
+ return;
+ }
+
+ GPUCodegenCreateInfo &info = *create_info;
+
+ info.name_buffer = MEM_new<GPUCodegenCreateInfo::NameBuffer>("info.name_buffer");
+ info.interface_generated = new StageInterfaceInfo("codegen_iface", "var_attrs");
+ StageInterfaceInfo &iface = *info.interface_generated;
+ info.vertex_out(iface);
+
+ /* Input declaration, loading / assignment to interface and geometry shader passthrough. */
+ std::stringstream decl_ss, iface_ss, load_ss;
+
+ int slot = 15;
+ LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph.attributes) {
+
+ /* NOTE: Replicate changes to mesh_render_data_create() in draw_cache_impl_mesh.c */
+ if (attr->type == CD_ORCO) {
+ /* OPTI: orco is computed from local positions, but only if no modifier is present. */
+ STRNCPY(info.name_buffer->attr_names[slot], "orco");
+ }
+ else {
+ char *name = info.name_buffer->attr_names[slot];
+ name[0] = attr_prefix_get(static_cast<CustomDataType>(attr->type));
+ name[1] = '\0';
+ if (attr->name[0] != '\0') {
+ /* XXX FIXME: see notes in mesh_render_data_create() */
+ GPU_vertformat_safe_attr_name(attr->name, &name[1], GPU_MAX_SAFE_ATTR_NAME);
+ }
+ }
+ SNPRINTF(info.name_buffer->var_names[slot], "v%d", attr->id);
+
+ blender::StringRefNull attr_name = info.name_buffer->attr_names[slot];
+ blender::StringRefNull var_name = info.name_buffer->var_names[slot];
+
+ eGPUType input_type, iface_type;
+
+ load_ss << "var_attrs." << var_name;
+ switch (attr->type) {
+ case CD_ORCO:
+ /* Need vec4 to detect usage of default attribute. */
+ input_type = GPU_VEC4;
+ iface_type = GPU_VEC3;
+ load_ss << " = attr_load_orco(" << attr_name << ");\n";
+ break;
+ case CD_HAIRLENGTH:
+ iface_type = input_type = GPU_FLOAT;
+ load_ss << " = attr_load_" << input_type << "(" << attr_name << ");\n";
+ break;
+ case CD_TANGENT:
+ iface_type = input_type = GPU_VEC4;
+ load_ss << " = attr_load_tangent(" << attr_name << ");\n";
+ break;
+ case CD_MTFACE:
+ iface_type = input_type = GPU_VEC3;
+ load_ss << " = attr_load_uv(" << attr_name << ");\n";
+ break;
+ case CD_MCOL:
+ iface_type = input_type = GPU_VEC4;
+ load_ss << " = attr_load_color(" << attr_name << ");\n";
+ break;
+ default:
+ iface_type = input_type = GPU_VEC4;
+ load_ss << " = attr_load_" << input_type << "(" << attr_name << ");\n";
+ break;
+ }
+
+ info.vertex_in(slot--, to_type(input_type), attr_name);
+ iface.smooth(to_type(iface_type), var_name);
+ }
+
+ output.attr_load = extract_c_str(load_ss);
+}
+
+void GPUCodegen::generate_resources()
+{
+ GPUCodegenCreateInfo &info = *create_info;
+
+ std::stringstream ss;
+
+ /* Textures. */
+ LISTBASE_FOREACH (GPUMaterialTexture *, tex, &graph.textures) {
+ if (tex->colorband) {
+ info.sampler(0, ImageType::FLOAT_1D_ARRAY, tex->sampler_name, Frequency::BATCH);
+ }
+ else if (tex->tiled_mapping_name[0] != '\0') {
+ info.sampler(0, ImageType::FLOAT_2D_ARRAY, tex->sampler_name, Frequency::BATCH);
+ info.sampler(0, ImageType::FLOAT_1D_ARRAY, tex->tiled_mapping_name, Frequency::BATCH);
+ }
+ else {
+ info.sampler(0, ImageType::FLOAT_2D, tex->sampler_name, Frequency::BATCH);
+ }
+ }
+ /* Volume Grids. */
+ LISTBASE_FOREACH (GPUMaterialVolumeGrid *, grid, &graph.volume_grids) {
+ info.sampler(0, ImageType::FLOAT_3D, grid->sampler_name, Frequency::BATCH);
+ /* TODO(@fclem): Global uniform. To put in an UBO. */
+ info.push_constant(Type::MAT4, grid->transform_name);
+ }
+
+ if (!BLI_listbase_is_empty(&ubo_inputs_)) {
+ /* NOTE: generate_uniform_buffer() should have sorted the inputs before this. */
+ ss << "struct NodeTree {\n";
+ LISTBASE_FOREACH (LinkData *, link, &ubo_inputs_) {
+ GPUInput *input = (GPUInput *)(link->data);
+ ss << input->type << " u" << input->id << ";\n";
+ }
+ ss << "};\n\n";
+
+ info.uniform_buf(0, "NodeTree", GPU_UBO_BLOCK_NAME, Frequency::BATCH);
+ }
+
+ if (!BLI_listbase_is_empty(&graph.uniform_attrs.list)) {
+ ss << "struct UniformAttrs {\n";
+ LISTBASE_FOREACH (GPUUniformAttr *, attr, &graph.uniform_attrs.list) {
+ ss << "vec4 attr" << attr->id << ";\n";
+ }
+ ss << "};\n\n";
+
+ /* TODO(fclem): Use the macro for length. Currently not working for EEVEE. */
+ /* DRW_RESOURCE_CHUNK_LEN = 512 */
+ info.uniform_buf(0, "UniformAttrs", GPU_ATTRIBUTE_UBO_BLOCK_NAME "[512]", Frequency::BATCH);
+ }
+
+ info.typedef_source_generated = ss.str();
+}
+
+void GPUCodegen::generate_library()
+{
+ GPUCodegenCreateInfo &info = *create_info;
+
+ void *value;
+ GSetIterState pop_state = {};
+ while (BLI_gset_pop(graph.used_libraries, &pop_state, &value)) {
+ auto deps = gpu_shader_dependency_get_resolved_source((const char *)value);
+ info.dependencies_generated.extend_non_duplicates(deps);
+ }
+}
+
+void GPUCodegen::node_serialize(std::stringstream &eval_ss, const GPUNode *node)
+{
+ /* Declare constants. */
+ LISTBASE_FOREACH (GPUInput *, input, &node->inputs) {
+ switch (input->source) {
+ case GPU_SOURCE_FUNCTION_CALL:
+ eval_ss << input->type << " " << input << "; " << input->function_call << input << ");\n";
+ break;
+ case GPU_SOURCE_STRUCT:
+ eval_ss << input->type << " " << input << " = CLOSURE_DEFAULT;\n";
+ break;
+ case GPU_SOURCE_CONSTANT:
+ eval_ss << input->type << " " << input << " = " << (GPUConstant *)input << ";\n";
+ break;
+ default:
+ break;
+ }
+ }
+ /* Declare temporary variables for node output storage. */
+ LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) {
+ eval_ss << output->type << " " << output << ";\n";
+ }
+
+ /* Function call. */
+ eval_ss << node->name << "(";
+ /* Input arguments. */
+ LISTBASE_FOREACH (GPUInput *, input, &node->inputs) {
+ switch (input->source) {
+ case GPU_SOURCE_OUTPUT:
+ case GPU_SOURCE_ATTR: {
+ /* These inputs can have non matching types. Do conversion. */
+ eGPUType to = input->type;
+ eGPUType from = (input->source == GPU_SOURCE_ATTR) ? input->attr->gputype :
+ input->link->output->type;
+ if (from != to) {
+ /* Use defines declared inside codegen_lib (i.e: vec4_from_float). */
+ eval_ss << to << "_from_" << from << "(";
+ }
+
+ if (input->source == GPU_SOURCE_ATTR) {
+ eval_ss << input;
+ }
+ else {
+ eval_ss << input->link->output;
+ }
+
+ if (from != to) {
+ eval_ss << ")";
+ }
+ break;
+ }
+ default:
+ eval_ss << input;
+ break;
+ }
+ eval_ss << ", ";
+ }
+ /* Output arguments. */
+ LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) {
+ eval_ss << output;
+ if (output->next) {
+ eval_ss << ", ";
+ }
+ }
+ eval_ss << ");\n\n";
+}
+
+char *GPUCodegen::graph_serialize(eGPUNodeTag tree_tag, GPUNodeLink *output_link)
+{
+ if (output_link == nullptr) {
+ return nullptr;
+ }
+
+ std::stringstream eval_ss;
+ /* NOTE: The node order is already top to bottom (or left to right in node editor)
+ * because of the evaluation order inside ntreeExecGPUNodes(). */
+ LISTBASE_FOREACH (GPUNode *, node, &graph.nodes) {
+ if ((node->tag & tree_tag) == 0) {
+ continue;
+ }
+ node_serialize(eval_ss, node);
+ }
+ eval_ss << "return " << output_link->output << ";\n";
+
+ char *eval_c_str = extract_c_str(eval_ss);
+ BLI_hash_mm2a_add(&hm2a_, (uchar *)eval_c_str, eval_ss.str().size());
+ return eval_c_str;
+}
+
+void GPUCodegen::generate_uniform_buffer()
+{
+ /* Extract uniform inputs. */
+ LISTBASE_FOREACH (GPUNode *, node, &graph.nodes) {
+ LISTBASE_FOREACH (GPUInput *, input, &node->inputs) {
+ if (input->source == GPU_SOURCE_UNIFORM && !input->link) {
+ /* We handle the UBO uniforms separately. */
+ BLI_addtail(&ubo_inputs_, BLI_genericNodeN(input));
+ }
+ }
+ }
+ if (!BLI_listbase_is_empty(&ubo_inputs_)) {
+ /* This sorts the inputs based on size. */
+ GPU_material_uniform_buffer_create(&mat, &ubo_inputs_);
+ }
+}
+
+/* Sets id for unique names for all inputs, resources and temp variables. */
+void GPUCodegen::set_unique_ids()
+{
+ int id = 1;
+ LISTBASE_FOREACH (GPUNode *, node, &graph.nodes) {
+ LISTBASE_FOREACH (GPUInput *, input, &node->inputs) {
+ input->id = id++;
+ }
+ LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) {
+ output->id = id++;
+ }
+ }
+}
+
+void GPUCodegen::generate_graphs()
+{
+ set_unique_ids();
+
+ output.surface = graph_serialize(GPU_NODE_TAG_SURFACE | GPU_NODE_TAG_AOV, graph.outlink_surface);
+ output.volume = graph_serialize(GPU_NODE_TAG_VOLUME, graph.outlink_volume);
+ output.displacement = graph_serialize(GPU_NODE_TAG_DISPLACEMENT, graph.outlink_displacement);
+ output.thickness = graph_serialize(GPU_NODE_TAG_THICKNESS, graph.outlink_thickness);
+
+ if (!BLI_listbase_is_empty(&graph.material_functions)) {
+ std::stringstream eval_ss;
+ eval_ss << "\n/* Generated Functions */\n\n";
+ LISTBASE_FOREACH (GPUNodeGraphFunctionLink *, func_link, &graph.material_functions) {
+ char *fn = graph_serialize(GPU_NODE_TAG_FUNCTION, func_link->outlink);
+ eval_ss << "float " << func_link->name << "() {\n" << fn << "}\n\n";
+ MEM_SAFE_FREE(fn);
+ }
+ output.material_functions = extract_c_str(eval_ss);
+ }
+
+ LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph.attributes) {
+ BLI_hash_mm2a_add(&hm2a_, (uchar *)attr->name, strlen(attr->name));
+ }
+
+ hash_ = BLI_hash_mm2a_end(&hm2a_);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name GPUPass
+ * \{ */
+
+GPUPass *GPU_generate_pass(GPUMaterial *material,
+ GPUNodeGraph *graph,
+ GPUCodegenCallbackFn finalize_source_cb,
+ void *thunk)
+{
+ /* Prune the unused nodes and extract attributes before compiling so the
+ * generated VBOs are ready to accept the future shader. */
+ gpu_node_graph_prune_unused(graph);
+ gpu_node_graph_finalize_uniform_attrs(graph);
+
+ GPUCodegen codegen(material, graph);
+ codegen.generate_graphs();
+ codegen.generate_uniform_buffer();
+
+ /* Cache lookup: Reuse shaders already compiled. */
+ GPUPass *pass_hash = gpu_pass_cache_lookup(codegen.hash_get());
+
+ /* FIXME(fclem): This is broken. Since we only check for the hash and not the full source
+ * there is no way to have a collision currently. Some advocated to only use a bigger hash. */
+ if (pass_hash && (pass_hash->next == nullptr || pass_hash->next->hash != codegen.hash_get())) {
+ if (!gpu_pass_is_valid(pass_hash)) {
+ /* Shader has already been created but failed to compile. */
+ return nullptr;
+ }
+ /* No collision, just return the pass. */
+ pass_hash->refcount += 1;
+ return pass_hash;
+ }
+
+ /* Either the shader is not compiled or there is a hash collision...
+ * continue generating the shader strings. */
+ codegen.generate_attribs();
+ codegen.generate_resources();
+ codegen.generate_library();
+
+ /* Make engine add its own code and implement the generated functions. */
+ finalize_source_cb(thunk, material, &codegen.output);
+
+ GPUPass *pass = nullptr;
+ if (pass_hash) {
+ /* Cache lookup: Reuse shaders already compiled. */
+ pass = gpu_pass_cache_resolve_collision(
+ pass_hash, codegen.output.create_info, codegen.hash_get());
+ }
+
+ if (pass) {
+ /* Cache hit. Reuse the same GPUPass and GPUShader. */
+ if (!gpu_pass_is_valid(pass)) {
+ /* Shader has already been created but failed to compile. */
+ return nullptr;
+ }
+ pass->refcount += 1;
+ }
+ else {
+ /* We still create a pass even if shader compilation
+ * fails to avoid trying to compile again and again. */
+ pass = (GPUPass *)MEM_callocN(sizeof(GPUPass), "GPUPass");
+ pass->shader = nullptr;
+ pass->refcount = 1;
+ pass->create_info = codegen.create_info;
+ pass->hash = codegen.hash_get();
+ pass->compiled = false;
+
+ codegen.create_info = nullptr;
+
+ gpu_pass_cache_insert_after(pass_hash, pass);
+ }
+ return pass;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Compilation
+ * \{ */
+
+static int count_active_texture_sampler(GPUPass *pass, GPUShader *shader)
+{
+ int num_samplers = 0;
+
+ for (const ShaderCreateInfo::Resource &res : pass->create_info->pass_resources_) {
+ if (res.bind_type == ShaderCreateInfo::Resource::BindType::SAMPLER) {
+ if (GPU_shader_get_uniform(shader, res.sampler.name.c_str()) != -1) {
+ num_samplers += 1;
+ }
+ }
+ }
+
+ return num_samplers;
+}
+
+static bool gpu_pass_shader_validate(GPUPass *pass, GPUShader *shader)
+{
+ if (shader == nullptr) {
+ return false;
+ }
+
+ /* NOTE: The only drawback of this method is that it will count a sampler
+ * used in the fragment shader and only declared (but not used) in the vertex
+ * shader as used by both. But this corner case is not happening for now. */
+ int active_samplers_len = count_active_texture_sampler(pass, shader);
+
+ /* Validate against opengl limit. */
+ if ((active_samplers_len > GPU_max_textures_frag()) ||
+ (active_samplers_len > GPU_max_textures_vert())) {
+ return false;
+ }
+
+ if (pass->create_info->geometry_source_.is_empty() == false) {
+ if (active_samplers_len > GPU_max_textures_geom()) {
+ return false;
+ }
+ }
+
+ return (active_samplers_len * 3 <= GPU_max_textures());
+}
+
+bool GPU_pass_compile(GPUPass *pass, const char *shname)
+{
+ bool success = true;
+ if (!pass->compiled) {
+ GPUShaderCreateInfo *info = reinterpret_cast<GPUShaderCreateInfo *>(
+ static_cast<ShaderCreateInfo *>(pass->create_info));
+
+ pass->create_info->name_ = shname;
+
+ GPUShader *shader = GPU_shader_create_from_info(info);
+
+ /* NOTE: Some drivers / gpu allows more active samplers than the opengl limit.
+ * We need to make sure to count active samplers to avoid undefined behavior. */
+ if (!gpu_pass_shader_validate(pass, shader)) {
+ success = false;
+ if (shader != nullptr) {
+ fprintf(stderr, "GPUShader: error: too many samplers in shader.\n");
+ GPU_shader_free(shader);
+ shader = nullptr;
+ }
+ }
+ pass->shader = shader;
+ pass->compiled = true;
+ }
+ return success;
+}
+
+GPUShader *GPU_pass_shader_get(GPUPass *pass)
+{
+ return pass->shader;
+}
+
+void GPU_pass_release(GPUPass *pass)
+{
+ BLI_assert(pass->refcount > 0);
+ pass->refcount--;
+}
+
+static void gpu_pass_free(GPUPass *pass)
+{
+ BLI_assert(pass->refcount == 0);
+ if (pass->shader) {
+ GPU_shader_free(pass->shader);
+ }
+ delete pass->create_info;
+ MEM_freeN(pass);
+}
+
+void GPU_pass_cache_garbage_collect(void)
+{
+ static int lasttime = 0;
+ const int shadercollectrate = 60; /* hardcoded for now. */
+ int ctime = (int)PIL_check_seconds_timer();
+
+ if (ctime < shadercollectrate + lasttime) {
+ return;
+ }
+
+ lasttime = ctime;
+
+ BLI_spin_lock(&pass_cache_spin);
+ GPUPass *next, **prev_pass = &pass_cache;
+ for (GPUPass *pass = pass_cache; pass; pass = next) {
+ next = pass->next;
+ if (pass->refcount == 0) {
+ /* Remove from list */
+ *prev_pass = next;
+ gpu_pass_free(pass);
+ }
+ else {
+ prev_pass = &pass->next;
+ }
+ }
+ BLI_spin_unlock(&pass_cache_spin);
+}
+
+void GPU_pass_cache_init(void)
+{
+ BLI_spin_init(&pass_cache_spin);
+}
+
+void GPU_pass_cache_free(void)
+{
+ BLI_spin_lock(&pass_cache_spin);
+ while (pass_cache) {
+ GPUPass *next = pass_cache->next;
+ gpu_pass_free(pass_cache);
+ pass_cache = next;
+ }
+ BLI_spin_unlock(&pass_cache_spin);
+
+ BLI_spin_end(&pass_cache_spin);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Module
+ * \{ */
+
+void gpu_codegen_init(void)
+{
+}
+
+void gpu_codegen_exit(void)
+{
+ // BKE_world_defaults_free_gpu();
+ BKE_material_defaults_free_gpu();
+ GPU_shader_free_builtin_shaders();
+}
+
+/** \} */ \ No newline at end of file
diff --git a/source/blender/gpu/intern/gpu_codegen.h b/source/blender/gpu/intern/gpu_codegen.h
index 0e99476d3ea..95a672c0400 100644
--- a/source/blender/gpu/intern/gpu_codegen.h
+++ b/source/blender/gpu/intern/gpu_codegen.h
@@ -9,36 +9,24 @@
#pragma once
+#include "GPU_material.h"
+#include "GPU_shader.h"
+
#ifdef __cplusplus
extern "C" {
#endif
-struct GPUMaterial;
struct GPUNodeGraph;
-struct GPUShader;
-
-typedef struct GPUPass {
- struct GPUPass *next;
- struct GPUShader *shader;
- char *fragmentcode;
- char *geometrycode;
- char *vertexcode;
- char *defines;
- uint refcount; /* Orphaned GPUPasses gets freed by the garbage collector. */
- uint32_t hash; /* Identity hash generated from all GLSL code. */
- bool compiled; /* Did we already tried to compile the attached GPUShader. */
-} GPUPass;
+typedef struct GPUPass GPUPass;
/* Pass */
-GPUPass *GPU_generate_pass(struct GPUMaterial *material,
+GPUPass *GPU_generate_pass(GPUMaterial *material,
struct GPUNodeGraph *graph,
- const char *vert_code,
- const char *geom_code,
- const char *frag_lib,
- const char *defines);
-struct GPUShader *GPU_pass_shader_get(GPUPass *pass);
+ GPUCodegenCallbackFn finalize_source_cb,
+ void *thunk);
+GPUShader *GPU_pass_shader_get(GPUPass *pass);
bool GPU_pass_compile(GPUPass *pass, const char *shname);
void GPU_pass_release(GPUPass *pass);
diff --git a/source/blender/gpu/intern/gpu_immediate_util.c b/source/blender/gpu/intern/gpu_immediate_util.c
index cf8837ab26e..67035853594 100644
--- a/source/blender/gpu/intern/gpu_immediate_util.c
+++ b/source/blender/gpu/intern/gpu_immediate_util.c
@@ -142,12 +142,27 @@ static void imm_draw_circle(GPUPrimType prim_type,
float radius_y,
int nsegments)
{
- immBegin(prim_type, nsegments);
- for (int i = 0; i < nsegments; i++) {
- const float angle = (float)(2 * M_PI) * ((float)i / (float)nsegments);
- immVertex2f(shdr_pos, x + (radius_x * cosf(angle)), y + (radius_y * sinf(angle)));
+ if (prim_type == GPU_PRIM_LINE_LOOP) {
+ /* Note(Metal/AMD): For small primitives, line list more efficient than line strip.. */
+ immBegin(GPU_PRIM_LINES, nsegments * 2);
+
+ immVertex2f(shdr_pos, x + (radius_x * cosf(0.0f)), y + (radius_y * sinf(0.0f)));
+ for (int i = 1; i < nsegments; i++) {
+ const float angle = (float)(2 * M_PI) * ((float)i / (float)nsegments);
+ immVertex2f(shdr_pos, x + (radius_x * cosf(angle)), y + (radius_y * sinf(angle)));
+ immVertex2f(shdr_pos, x + (radius_x * cosf(angle)), y + (radius_y * sinf(angle)));
+ }
+ immVertex2f(shdr_pos, x + (radius_x * cosf(0.0f)), y + (radius_y * sinf(0.0f)));
+ immEnd();
+ }
+ else {
+ immBegin(prim_type, nsegments);
+ for (int i = 0; i < nsegments; i++) {
+ const float angle = (float)(2 * M_PI) * ((float)i / (float)nsegments);
+ immVertex2f(shdr_pos, x + (radius_x * cosf(angle)), y + (radius_y * sinf(angle)));
+ }
+ immEnd();
}
- immEnd();
}
void imm_draw_circle_wire_2d(uint shdr_pos, float x, float y, float radius, int nsegments)
@@ -194,12 +209,42 @@ static void imm_draw_circle_partial(GPUPrimType prim_type,
immEnd();
}
+static void imm_draw_circle_partial_3d(GPUPrimType prim_type,
+ uint pos,
+ float x,
+ float y,
+ float z,
+ float rad,
+ int nsegments,
+ float start,
+ float sweep)
+{
+ /* shift & reverse angle, increase 'nsegments' to match gluPartialDisk */
+ const float angle_start = -(DEG2RADF(start)) + (float)(M_PI / 2);
+ const float angle_end = -(DEG2RADF(sweep) - angle_start);
+ nsegments += 1;
+ immBegin(prim_type, nsegments);
+ for (int i = 0; i < nsegments; i++) {
+ const float angle = interpf(angle_start, angle_end, ((float)i / (float)(nsegments - 1)));
+ const float angle_sin = sinf(angle);
+ const float angle_cos = cosf(angle);
+ immVertex3f(pos, x + rad * angle_cos, y + rad * angle_sin, z);
+ }
+ immEnd();
+}
+
void imm_draw_circle_partial_wire_2d(
uint pos, float x, float y, float radius, int nsegments, float start, float sweep)
{
imm_draw_circle_partial(GPU_PRIM_LINE_STRIP, pos, x, y, radius, nsegments, start, sweep);
}
+void imm_draw_circle_partial_wire_3d(
+ uint pos, float x, float y, float z, float rad, int nsegments, float start, float sweep)
+{
+ imm_draw_circle_partial_3d(GPU_PRIM_LINE_STRIP, pos, x, y, z, rad, nsegments, start, sweep);
+}
+
static void imm_draw_disk_partial(GPUPrimType prim_type,
uint pos,
float x,
@@ -229,6 +274,36 @@ static void imm_draw_disk_partial(GPUPrimType prim_type,
immEnd();
}
+static void imm_draw_disk_partial_3d(GPUPrimType prim_type,
+ uint pos,
+ float x,
+ float y,
+ float z,
+ float rad_inner,
+ float rad_outer,
+ int nsegments,
+ float start,
+ float sweep)
+{
+ /* to avoid artifacts */
+ const float max_angle = 3 * 360;
+ CLAMP(sweep, -max_angle, max_angle);
+
+ /* shift & reverse angle, increase 'nsegments' to match gluPartialDisk */
+ const float angle_start = -(DEG2RADF(start)) + (float)M_PI_2;
+ const float angle_end = -(DEG2RADF(sweep) - angle_start);
+ nsegments += 1;
+ immBegin(prim_type, nsegments * 2);
+ for (int i = 0; i < nsegments; i++) {
+ const float angle = interpf(angle_start, angle_end, ((float)i / (float)(nsegments - 1)));
+ const float angle_sin = sinf(angle);
+ const float angle_cos = cosf(angle);
+ immVertex3f(pos, x + rad_inner * angle_cos, y + rad_inner * angle_sin, z);
+ immVertex3f(pos, x + rad_outer * angle_cos, y + rad_outer * angle_sin, z);
+ }
+ immEnd();
+}
+
void imm_draw_disk_partial_fill_2d(uint pos,
float x,
float y,
@@ -241,16 +316,44 @@ void imm_draw_disk_partial_fill_2d(uint pos,
imm_draw_disk_partial(
GPU_PRIM_TRI_STRIP, pos, x, y, rad_inner, rad_outer, nsegments, start, sweep);
}
+void imm_draw_disk_partial_fill_3d(uint pos,
+ float x,
+ float y,
+ float z,
+ float rad_inner,
+ float rad_outer,
+ int nsegments,
+ float start,
+ float sweep)
+{
+ imm_draw_disk_partial_3d(
+ GPU_PRIM_TRI_STRIP, pos, x, y, z, rad_inner, rad_outer, nsegments, start, sweep);
+}
static void imm_draw_circle_3D(
GPUPrimType prim_type, uint pos, float x, float y, float radius, int nsegments)
{
- immBegin(prim_type, nsegments);
- for (int i = 0; i < nsegments; i++) {
- float angle = (float)(2 * M_PI) * ((float)i / (float)nsegments);
- immVertex3f(pos, x + radius * cosf(angle), y + radius * sinf(angle), 0.0f);
+ if (prim_type == GPU_PRIM_LINE_LOOP) {
+ /* Note(Metal/AMD): For small primitives, line list more efficient than line strip. */
+ immBegin(GPU_PRIM_LINES, nsegments * 2);
+
+ immVertex3f(pos, x + radius * cosf(0.0f), y + radius * sinf(0.0f), 0.0f);
+ for (int i = 1; i < nsegments; i++) {
+ float angle = (float)(2 * M_PI) * ((float)i / (float)nsegments);
+ immVertex3f(pos, x + radius * cosf(angle), y + radius * sinf(angle), 0.0f);
+ immVertex3f(pos, x + radius * cosf(angle), y + radius * sinf(angle), 0.0f);
+ }
+ immVertex3f(pos, x + radius * cosf(0.0f), y + radius * sinf(0.0f), 0.0f);
+ immEnd();
+ }
+ else {
+ immBegin(prim_type, nsegments);
+ for (int i = 0; i < nsegments; i++) {
+ float angle = (float)(2 * M_PI) * ((float)i / (float)nsegments);
+ immVertex3f(pos, x + radius * cosf(angle), y + radius * sinf(angle), 0.0f);
+ }
+ immEnd();
}
- immEnd();
}
void imm_draw_circle_wire_3d(uint pos, float x, float y, float radius, int nsegments)
@@ -270,22 +373,38 @@ void imm_draw_circle_fill_3d(uint pos, float x, float y, float radius, int nsegm
void imm_draw_box_wire_2d(uint pos, float x1, float y1, float x2, float y2)
{
- immBegin(GPU_PRIM_LINE_LOOP, 4);
+ /* Note(Metal/AMD): For small primitives, line list more efficient than line-strip. */
+ immBegin(GPU_PRIM_LINES, 8);
immVertex2f(pos, x1, y1);
immVertex2f(pos, x1, y2);
+
+ immVertex2f(pos, x1, y2);
immVertex2f(pos, x2, y2);
+
+ immVertex2f(pos, x2, y2);
+ immVertex2f(pos, x2, y1);
+
immVertex2f(pos, x2, y1);
+ immVertex2f(pos, x1, y1);
immEnd();
}
void imm_draw_box_wire_3d(uint pos, float x1, float y1, float x2, float y2)
{
/* use this version when GPUVertFormat has a vec3 position */
- immBegin(GPU_PRIM_LINE_LOOP, 4);
+ /* Note(Metal/AMD): For small primitives, line list more efficient than line-strip. */
+ immBegin(GPU_PRIM_LINES, 8);
immVertex3f(pos, x1, y1, 0.0f);
immVertex3f(pos, x1, y2, 0.0f);
+
+ immVertex3f(pos, x1, y2, 0.0f);
+ immVertex3f(pos, x2, y2, 0.0f);
+
immVertex3f(pos, x2, y2, 0.0f);
immVertex3f(pos, x2, y1, 0.0f);
+
+ immVertex3f(pos, x2, y1, 0.0f);
+ immVertex3f(pos, x1, y1, 0.0f);
immEnd();
}
diff --git a/source/blender/gpu/intern/gpu_init_exit.c b/source/blender/gpu/intern/gpu_init_exit.c
index 7ccb0f89dc3..e97c9e9c829 100644
--- a/source/blender/gpu/intern/gpu_init_exit.c
+++ b/source/blender/gpu/intern/gpu_init_exit.c
@@ -39,7 +39,6 @@ void GPU_init(void)
gpu_shader_create_info_init();
gpu_codegen_init();
- gpu_material_library_init();
gpu_batch_init();
@@ -56,7 +55,6 @@ void GPU_exit(void)
gpu_batch_exit();
- gpu_material_library_exit();
gpu_codegen_exit();
gpu_shader_dependency_exit();
diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c
index 49732240125..711a3943a25 100644
--- a/source/blender/gpu/intern/gpu_material.c
+++ b/source/blender/gpu/intern/gpu_material.c
@@ -16,7 +16,6 @@
#include "DNA_scene_types.h"
#include "DNA_world_types.h"
-#include "BLI_ghash.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_string.h"
@@ -27,6 +26,7 @@
#include "BKE_material.h"
#include "BKE_node.h"
#include "BKE_scene.h"
+#include "BKE_world.h"
#include "NOD_shader.h"
@@ -49,56 +49,50 @@ typedef struct GPUColorBandBuilder {
} GPUColorBandBuilder;
struct GPUMaterial {
- Scene *scene; /* DEPRECATED was only useful for lights. */
- Material *ma;
-
+ /* Contains GPUShader and source code for deferred compilation.
+ * Can be shared between similar material (i.e: sharing same nodetree topology). */
+ GPUPass *pass;
+ /** UBOs for this material parameters. */
+ GPUUniformBuf *ubo;
+ /** Compilation status. Do not use if shader is not GPU_MAT_SUCCESS. */
eGPUMaterialStatus status;
-
- const void *engine_type; /* attached engine type */
- int options; /* to identify shader variations (shadow, probe, world background...) */
- bool is_volume_shader; /* is volumetric shader */
-
- /* Nodes */
+ /** Some flags about the nodetree & the needed resources. */
+ eGPUMaterialFlag flag;
+ /* Identify shader variations (shadow, probe, world background...).
+ * Should be unique even across render engines. */
+ uint64_t uuid;
+ /* Number of generated function. */
+ int generated_function_len;
+ /** Object type for attribute fetching. */
+ bool is_volume_shader;
+
+ /** DEPRECATED Currently only used for deferred compilation. */
+ Scene *scene;
+ /** Source material, might be null. */
+ Material *ma;
+ /** 1D Texture array containing all color bands. */
+ GPUTexture *coba_tex;
+ /** Builder for coba_tex. */
+ GPUColorBandBuilder *coba_builder;
+ /* Low level node graph(s). Also contains resources needed by the material. */
GPUNodeGraph graph;
- /* for binding the material */
- GPUPass *pass;
-
- /* XXX: Should be in Material. But it depends on the output node
- * used and since the output selection is different for GPUMaterial...
- */
- bool has_volume_output;
+ /** DEPRECATED: To remove. */
bool has_surface_output;
-
- /* Only used by Eevee to know which BSDF are used. */
- eGPUMatFlag flag;
-
- /* Used by 2.8 pipeline */
- GPUUniformBuf *ubo; /* UBOs for shader uniforms. */
-
- /* Eevee SSS */
+ bool has_volume_output;
+ /** DEPRECATED: To remove. */
GPUUniformBuf *sss_profile; /* UBO containing SSS profile. */
GPUTexture *sss_tex_profile; /* Texture containing SSS profile. */
- float sss_enabled;
+ bool sss_enabled;
float sss_radii[3];
int sss_samples;
bool sss_dirty;
- GPUTexture *coba_tex; /* 1D Texture array containing all color bands. */
- GPUColorBandBuilder *coba_builder;
-
- GSet *used_libraries;
-
#ifndef NDEBUG
char name[64];
#endif
};
-enum {
- GPU_USE_SURFACE_OUTPUT = (1 << 0),
- GPU_USE_VOLUME_OUTPUT = (1 << 1),
-};
-
/* Functions */
GPUTexture **gpu_material_ramp_texture_row_set(GPUMaterial *mat,
@@ -159,17 +153,15 @@ static void gpu_material_free_single(GPUMaterial *material)
if (material->ubo != NULL) {
GPU_uniformbuf_free(material->ubo);
}
- if (material->sss_tex_profile != NULL) {
- GPU_texture_free(material->sss_tex_profile);
+ if (material->coba_tex != NULL) {
+ GPU_texture_free(material->coba_tex);
}
if (material->sss_profile != NULL) {
GPU_uniformbuf_free(material->sss_profile);
}
- if (material->coba_tex != NULL) {
- GPU_texture_free(material->coba_tex);
+ if (material->sss_tex_profile != NULL) {
+ GPU_texture_free(material->sss_tex_profile);
}
-
- BLI_gset_free(material->used_libraries, NULL);
}
void GPU_material_free(ListBase *gpumaterial)
@@ -217,17 +209,40 @@ void GPU_material_uniform_buffer_create(GPUMaterial *material, ListBase *inputs)
material->ubo = GPU_uniformbuf_create_from_list(inputs, name);
}
+ListBase GPU_material_attributes(GPUMaterial *material)
+{
+ return material->graph.attributes;
+}
+
+ListBase GPU_material_textures(GPUMaterial *material)
+{
+ return material->graph.textures;
+}
+
+ListBase GPU_material_volume_grids(GPUMaterial *material)
+{
+ return material->graph.volume_grids;
+}
+
+GPUUniformAttrList *GPU_material_uniform_attributes(GPUMaterial *material)
+{
+ GPUUniformAttrList *attrs = &material->graph.uniform_attrs;
+ return attrs->count > 0 ? attrs : NULL;
+}
+
+#if 1 /* End of life code. */
/* Eevee Subsurface scattering. */
/* Based on Separable SSS. by Jorge Jimenez and Diego Gutierrez */
-#define SSS_SAMPLES 65
-#define SSS_EXPONENT 2.0f /* Importance sampling exponent */
+# define SSS_SAMPLES 65
+# define SSS_EXPONENT 2.0f /* Importance sampling exponent */
typedef struct GPUSssKernelData {
float kernel[SSS_SAMPLES][4];
float param[3], max_radius;
+ float avg_inv_radius;
int samples;
- int pad[3];
+ int pad[2];
} GPUSssKernelData;
BLI_STATIC_ASSERT_ALIGN(GPUSssKernelData, 16)
@@ -243,8 +258,8 @@ static void sss_calculate_offsets(GPUSssKernelData *kd, int count, float exponen
}
}
-#define BURLEY_TRUNCATE 16.0f
-#define BURLEY_TRUNCATE_CDF 0.9963790093708328f // cdf(BURLEY_TRUNCATE)
+# define BURLEY_TRUNCATE 16.0f
+# define BURLEY_TRUNCATE_CDF 0.9963790093708328f // cdf(BURLEY_TRUNCATE)
static float burley_profile(float r, float d)
{
float exp_r_3_d = expf(-r / (3.0f * d));
@@ -259,7 +274,7 @@ static float eval_profile(float r, float param)
}
/* Resolution for each sample of the precomputed kernel profile */
-#define INTEGRAL_RESOLUTION 32
+# define INTEGRAL_RESOLUTION 32
static float eval_integral(float x0, float x1, float param)
{
const float range = x1 - x0;
@@ -274,7 +289,7 @@ static float eval_integral(float x0, float x1, float param)
return integral;
}
-#undef INTEGRAL_RESOLUTION
+# undef INTEGRAL_RESOLUTION
static void compute_sss_kernel(GPUSssKernelData *kd, const float radii[3], int sample_len)
{
@@ -284,6 +299,8 @@ static void compute_sss_kernel(GPUSssKernelData *kd, const float radii[3], int s
rad[1] = MAX2(radii[1], 1e-15f);
rad[2] = MAX2(radii[2], 1e-15f);
+ kd->avg_inv_radius = 3.0f / (rad[0] + rad[1] + rad[2]);
+
/* Christensen-Burley fitting */
float l[3], d[3];
@@ -358,7 +375,7 @@ static void compute_sss_kernel(GPUSssKernelData *kd, const float radii[3], int s
kd->samples = sample_len;
}
-#define INTEGRAL_RESOLUTION 512
+# define INTEGRAL_RESOLUTION 512
static void compute_sss_translucence_kernel(const GPUSssKernelData *kd,
int resolution,
float **output)
@@ -417,10 +434,14 @@ static void compute_sss_translucence_kernel(const GPUSssKernelData *kd,
mul_v3_fl(texels[resolution - 3], 0.5f);
mul_v3_fl(texels[resolution - 4], 0.75f);
}
-#undef INTEGRAL_RESOLUTION
+# undef INTEGRAL_RESOLUTION
-void GPU_material_sss_profile_create(GPUMaterial *material, float radii[3])
+bool GPU_material_sss_profile_create(GPUMaterial *material, float radii[3])
{
+ /* Enable only once. */
+ if (material->sss_enabled) {
+ return false;
+ }
copy_v3_v3(material->sss_radii, radii);
material->sss_dirty = true;
material->sss_enabled = true;
@@ -429,6 +450,7 @@ void GPU_material_sss_profile_create(GPUMaterial *material, float radii[3])
if (material->sss_profile == NULL) {
material->sss_profile = GPU_uniformbuf_create(sizeof(GPUSssKernelData));
}
+ return true;
}
struct GPUUniformBuf *GPU_material_sss_profile_get(GPUMaterial *material,
@@ -475,34 +497,37 @@ struct GPUUniformBuf *GPU_material_create_sss_profile_ubo(void)
return GPU_uniformbuf_create(sizeof(GPUSssKernelData));
}
-#undef SSS_EXPONENT
-#undef SSS_SAMPLES
-
-ListBase GPU_material_attributes(GPUMaterial *material)
-{
- return material->graph.attributes;
-}
+# undef SSS_EXPONENT
+# undef SSS_SAMPLES
+#endif
-ListBase GPU_material_textures(GPUMaterial *material)
+void GPU_material_output_surface(GPUMaterial *material, GPUNodeLink *link)
{
- return material->graph.textures;
+ if (!material->graph.outlink_surface) {
+ material->graph.outlink_surface = link;
+ material->has_surface_output = true;
+ }
}
-ListBase GPU_material_volume_grids(GPUMaterial *material)
+void GPU_material_output_volume(GPUMaterial *material, GPUNodeLink *link)
{
- return material->graph.volume_grids;
+ if (!material->graph.outlink_volume) {
+ material->graph.outlink_volume = link;
+ material->has_volume_output = true;
+ }
}
-GPUUniformAttrList *GPU_material_uniform_attributes(GPUMaterial *material)
+void GPU_material_output_displacement(GPUMaterial *material, GPUNodeLink *link)
{
- GPUUniformAttrList *attrs = &material->graph.uniform_attrs;
- return attrs->count > 0 ? attrs : NULL;
+ if (!material->graph.outlink_displacement) {
+ material->graph.outlink_displacement = link;
+ }
}
-void GPU_material_output_link(GPUMaterial *material, GPUNodeLink *link)
+void GPU_material_output_thickness(GPUMaterial *material, GPUNodeLink *link)
{
- if (!material->graph.outlink) {
- material->graph.outlink = link;
+ if (!material->graph.outlink_thickness) {
+ material->graph.outlink_thickness = link;
}
}
@@ -514,23 +539,71 @@ void GPU_material_add_output_link_aov(GPUMaterial *material, GPUNodeLink *link,
BLI_addtail(&material->graph.outlink_aovs, aov_link);
}
+char *GPU_material_split_sub_function(GPUMaterial *material,
+ eGPUType return_type,
+ GPUNodeLink **link)
+{
+ /* Force cast to return type. */
+ switch (return_type) {
+ case GPU_FLOAT:
+ GPU_link(material, "set_value", *link, link);
+ break;
+ case GPU_VEC3:
+ GPU_link(material, "set_rgb", *link, link);
+ break;
+ case GPU_VEC4:
+ GPU_link(material, "set_rgba", *link, link);
+ break;
+ default:
+ BLI_assert(0);
+ break;
+ }
+
+ GPUNodeGraphFunctionLink *func_link = MEM_callocN(sizeof(GPUNodeGraphFunctionLink), __func__);
+ func_link->outlink = *link;
+ SNPRINTF(func_link->name, "ntree_fn%d", material->generated_function_len++);
+ BLI_addtail(&material->graph.material_functions, func_link);
+
+ /* Set value to break the link with the main graph. */
+ switch (return_type) {
+ case GPU_FLOAT:
+ GPU_link(material, "set_value_one", link);
+ break;
+ case GPU_VEC3:
+ GPU_link(material, "set_rgb_one", link);
+ break;
+ case GPU_VEC4:
+ GPU_link(material, "set_rgba_one", link);
+ break;
+ default:
+ BLI_assert(0);
+ break;
+ }
+ return func_link->name;
+}
+
GPUNodeGraph *gpu_material_node_graph(GPUMaterial *material)
{
return &material->graph;
}
-GSet *gpu_material_used_libraries(GPUMaterial *material)
+eGPUMaterialStatus GPU_material_status(GPUMaterial *mat)
{
- return material->used_libraries;
+ return mat->status;
}
-eGPUMaterialStatus GPU_material_status(GPUMaterial *mat)
+void GPU_material_status_set(GPUMaterial *mat, eGPUMaterialStatus status)
{
- return mat->status;
+ mat->status = status;
}
/* Code generation */
+bool GPU_material_is_volume_shader(GPUMaterial *mat)
+{
+ return mat->is_volume_shader;
+}
+
bool GPU_material_has_surface_output(GPUMaterial *mat)
{
return mat->has_surface_output;
@@ -541,100 +614,79 @@ bool GPU_material_has_volume_output(GPUMaterial *mat)
return mat->has_volume_output;
}
-bool GPU_material_is_volume_shader(GPUMaterial *mat)
+void GPU_material_flag_set(GPUMaterial *mat, eGPUMaterialFlag flag)
{
- return mat->is_volume_shader;
+ mat->flag |= flag;
}
-void GPU_material_flag_set(GPUMaterial *mat, eGPUMatFlag flag)
+bool GPU_material_flag_get(const GPUMaterial *mat, eGPUMaterialFlag flag)
{
- mat->flag |= flag;
+ return (mat->flag & flag) != 0;
}
-bool GPU_material_flag_get(GPUMaterial *mat, eGPUMatFlag flag)
+eGPUMaterialFlag GPU_material_flag(const GPUMaterial *mat)
{
- return (mat->flag & flag) != 0;
+ return mat->flag;
}
-GPUMaterial *GPU_material_from_nodetree_find(ListBase *gpumaterials,
- const void *engine_type,
- int options)
+/* Note: Consumes the flags. */
+bool GPU_material_recalc_flag_get(GPUMaterial *mat)
{
- LISTBASE_FOREACH (LinkData *, link, gpumaterials) {
- GPUMaterial *current_material = (GPUMaterial *)link->data;
- if (current_material->engine_type == engine_type && current_material->options == options) {
- return current_material;
- }
- }
+ bool updated = (mat->flag & GPU_MATFLAG_UPDATED) != 0;
+ mat->flag &= ~GPU_MATFLAG_UPDATED;
+ return updated;
+}
- return NULL;
+uint64_t GPU_material_uuid_get(GPUMaterial *mat)
+{
+ return mat->uuid;
}
GPUMaterial *GPU_material_from_nodetree(Scene *scene,
- struct Material *ma,
- struct bNodeTree *ntree,
+ Material *ma,
+ bNodeTree *ntree,
ListBase *gpumaterials,
- const void *engine_type,
- const int options,
- const bool is_volume_shader,
- const char *vert_code,
- const char *geom_code,
- const char *frag_lib,
- const char *defines,
const char *name,
- GPUMaterialEvalCallbackFn callback)
+ uint64_t shader_uuid,
+ bool is_volume_shader,
+ bool is_lookdev,
+ GPUCodegenCallbackFn callback,
+ void *thunk)
{
- LinkData *link;
- bool has_volume_output, has_surface_output;
-
- /* Caller must re-use materials. */
- BLI_assert(GPU_material_from_nodetree_find(gpumaterials, engine_type, options) == NULL);
-
- /* HACK: Eevee assume this to create #GHash keys. */
- BLI_assert(sizeof(GPUPass) > 16);
+ /* Search if this material is not already compiled. */
+ LISTBASE_FOREACH (LinkData *, link, gpumaterials) {
+ GPUMaterial *mat = (GPUMaterial *)link->data;
+ if (mat->uuid == shader_uuid) {
+ return mat;
+ }
+ }
- /* allocate material */
GPUMaterial *mat = MEM_callocN(sizeof(GPUMaterial), "GPUMaterial");
mat->ma = ma;
mat->scene = scene;
- mat->engine_type = engine_type;
- mat->options = options;
+ mat->uuid = shader_uuid;
+ mat->flag = GPU_MATFLAG_UPDATED;
mat->is_volume_shader = is_volume_shader;
+ mat->graph.used_libraries = BLI_gset_new(
+ BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "GPUNodeGraph.used_libraries");
#ifndef NDEBUG
BLI_snprintf(mat->name, sizeof(mat->name), "%s", name);
#else
UNUSED_VARS(name);
#endif
+ if (is_lookdev) {
+ mat->flag |= GPU_MATFLAG_LOOKDEV_HACK;
+ }
- mat->used_libraries = BLI_gset_new(
- BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "GPUMaterial.used_libraries");
-
- /* localize tree to create links for reroute and mute */
+ /* Localize tree to create links for reroute and mute. */
bNodeTree *localtree = ntreeLocalize(ntree);
- ntreeGPUMaterialNodes(localtree, mat, &has_surface_output, &has_volume_output);
+ ntreeGPUMaterialNodes(localtree, mat);
gpu_material_ramp_texture_build(mat);
- mat->has_surface_output = has_surface_output;
- mat->has_volume_output = has_volume_output;
-
- if (mat->graph.outlink) {
- if (callback) {
- callback(mat, options, &vert_code, &geom_code, &frag_lib, &defines);
- }
- /* HACK: this is only for eevee. We add the define here after the nodetree evaluation. */
- if (GPU_material_flag_get(mat, GPU_MATFLAG_SSS)) {
- defines = BLI_string_joinN(defines,
- "#ifndef USE_ALPHA_BLEND\n"
- "# define USE_SSS\n"
- "#endif\n");
- }
+ {
/* Create source code and search pass cache for an already compiled version. */
- mat->pass = GPU_generate_pass(mat, &mat->graph, vert_code, geom_code, frag_lib, defines);
-
- if (GPU_material_flag_get(mat, GPU_MATFLAG_SSS)) {
- MEM_freeN((char *)defines);
- }
+ mat->pass = GPU_generate_pass(mat, &mat->graph, callback, thunk);
if (mat->pass == NULL) {
/* We had a cache hit and the shader has already failed to compile. */
@@ -649,26 +701,20 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene,
gpu_node_graph_free_nodes(&mat->graph);
}
else {
- mat->status = GPU_MAT_QUEUED;
+ mat->status = GPU_MAT_CREATED;
}
}
}
- else {
- mat->status = GPU_MAT_FAILED;
- gpu_node_graph_free(&mat->graph);
- }
- /* Only free after GPU_pass_shader_get where GPUUniformBuf
- * read data from the local tree. */
+ /* Only free after GPU_pass_shader_get where GPUUniformBuf read data from the local tree. */
ntreeFreeLocalTree(localtree);
BLI_assert(!localtree->id.py_instance); /* Or call #BKE_libblock_free_data_py. */
MEM_freeN(localtree);
- /* note that even if building the shader fails in some way, we still keep
+ /* Note that even if building the shader fails in some way, we still keep
* it to avoid trying to compile again and again, and simply do not use
- * the actual shader on drawing */
-
- link = MEM_callocN(sizeof(LinkData), "GPUMaterialLink");
+ * the actual shader on drawing. */
+ LinkData *link = MEM_callocN(sizeof(LinkData), "GPUMaterialLink");
link->data = mat;
BLI_addtail(gpumaterials, link);
@@ -690,6 +736,8 @@ void GPU_material_compile(GPUMaterial *mat)
success = GPU_pass_compile(mat->pass, __func__);
#endif
+ mat->flag |= GPU_MATFLAG_UPDATED;
+
if (success) {
GPUShader *sh = GPU_pass_shader_get(mat->pass);
if (sh != NULL) {
@@ -715,5 +763,6 @@ void GPU_materials_free(Main *bmain)
GPU_material_free(&wo->gpumaterial);
}
+ // BKE_world_defaults_free_gpu();
BKE_material_defaults_free_gpu();
}
diff --git a/source/blender/gpu/intern/gpu_material_library.c b/source/blender/gpu/intern/gpu_material_library.c
deleted file mode 100644
index ca5c3d4bb45..00000000000
--- a/source/blender/gpu/intern/gpu_material_library.c
+++ /dev/null
@@ -1,904 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later
- * Copyright 2005 Blender Foundation. All rights reserved. */
-
-/** \file
- * \ingroup gpu
- *
- * GPU material library parsing and code generation.
- */
-
-#include <stdio.h>
-#include <string.h>
-
-#include "MEM_guardedalloc.h"
-
-#include "BLI_dynstr.h"
-#include "BLI_ghash.h"
-#include "BLI_string.h"
-#include "BLI_utildefines.h"
-
-#include "gpu_material_library.h"
-
-/* List of all gpu_shader_material_*.glsl files used by GLSL materials. These
- * will be parsed to make all functions in them available to use for GPU_link().
- *
- * If a file uses functions from another file, it must be added to the list of
- * dependencies, and be placed after that file in the list. */
-
-extern char datatoc_gpu_shader_material_add_shader_glsl[];
-extern char datatoc_gpu_shader_material_ambient_occlusion_glsl[];
-extern char datatoc_gpu_shader_material_anisotropic_glsl[];
-extern char datatoc_gpu_shader_material_attribute_glsl[];
-extern char datatoc_gpu_shader_material_background_glsl[];
-extern char datatoc_gpu_shader_material_bevel_glsl[];
-extern char datatoc_gpu_shader_material_wavelength_glsl[];
-extern char datatoc_gpu_shader_material_blackbody_glsl[];
-extern char datatoc_gpu_shader_material_bright_contrast_glsl[];
-extern char datatoc_gpu_shader_material_bump_glsl[];
-extern char datatoc_gpu_shader_material_camera_glsl[];
-extern char datatoc_gpu_shader_material_clamp_glsl[];
-extern char datatoc_gpu_shader_material_color_ramp_glsl[];
-extern char datatoc_gpu_shader_material_color_util_glsl[];
-extern char datatoc_gpu_shader_material_combine_hsv_glsl[];
-extern char datatoc_gpu_shader_material_combine_rgb_glsl[];
-extern char datatoc_gpu_shader_material_combine_xyz_glsl[];
-extern char datatoc_gpu_shader_material_diffuse_glsl[];
-extern char datatoc_gpu_shader_material_displacement_glsl[];
-extern char datatoc_gpu_shader_material_eevee_specular_glsl[];
-extern char datatoc_gpu_shader_material_emission_glsl[];
-extern char datatoc_gpu_shader_material_float_curve_glsl[];
-extern char datatoc_gpu_shader_material_fractal_noise_glsl[];
-extern char datatoc_gpu_shader_material_fresnel_glsl[];
-extern char datatoc_gpu_shader_material_gamma_glsl[];
-extern char datatoc_gpu_shader_material_geometry_glsl[];
-extern char datatoc_gpu_shader_material_glass_glsl[];
-extern char datatoc_gpu_shader_material_glossy_glsl[];
-extern char datatoc_gpu_shader_material_hair_info_glsl[];
-extern char datatoc_gpu_shader_material_hash_glsl[];
-extern char datatoc_gpu_shader_material_holdout_glsl[];
-extern char datatoc_gpu_shader_material_hue_sat_val_glsl[];
-extern char datatoc_gpu_shader_material_invert_glsl[];
-extern char datatoc_gpu_shader_material_layer_weight_glsl[];
-extern char datatoc_gpu_shader_material_light_falloff_glsl[];
-extern char datatoc_gpu_shader_material_light_path_glsl[];
-extern char datatoc_gpu_shader_material_mapping_glsl[];
-extern char datatoc_gpu_shader_material_map_range_glsl[];
-extern char datatoc_gpu_shader_material_math_glsl[];
-extern char datatoc_gpu_shader_material_math_util_glsl[];
-extern char datatoc_gpu_shader_material_mix_rgb_glsl[];
-extern char datatoc_gpu_shader_material_mix_shader_glsl[];
-extern char datatoc_gpu_shader_material_noise_glsl[];
-extern char datatoc_gpu_shader_material_normal_glsl[];
-extern char datatoc_gpu_shader_material_normal_map_glsl[];
-extern char datatoc_gpu_shader_material_object_info_glsl[];
-extern char datatoc_gpu_shader_material_output_aov_glsl[];
-extern char datatoc_gpu_shader_material_output_material_glsl[];
-extern char datatoc_gpu_shader_material_output_world_glsl[];
-extern char datatoc_gpu_shader_material_particle_info_glsl[];
-extern char datatoc_gpu_shader_material_point_info_glsl[];
-extern char datatoc_gpu_shader_material_principled_glsl[];
-extern char datatoc_gpu_shader_material_refraction_glsl[];
-extern char datatoc_gpu_shader_material_rgb_curves_glsl[];
-extern char datatoc_gpu_shader_material_rgb_to_bw_glsl[];
-extern char datatoc_gpu_shader_material_separate_hsv_glsl[];
-extern char datatoc_gpu_shader_material_separate_rgb_glsl[];
-extern char datatoc_gpu_shader_material_separate_xyz_glsl[];
-extern char datatoc_gpu_shader_material_set_glsl[];
-extern char datatoc_gpu_shader_material_shader_to_rgba_glsl[];
-extern char datatoc_gpu_shader_material_squeeze_glsl[];
-extern char datatoc_gpu_shader_material_subsurface_scattering_glsl[];
-extern char datatoc_gpu_shader_material_tangent_glsl[];
-extern char datatoc_gpu_shader_material_tex_brick_glsl[];
-extern char datatoc_gpu_shader_material_tex_checker_glsl[];
-extern char datatoc_gpu_shader_material_tex_environment_glsl[];
-extern char datatoc_gpu_shader_material_tex_gradient_glsl[];
-extern char datatoc_gpu_shader_material_tex_image_glsl[];
-extern char datatoc_gpu_shader_material_tex_magic_glsl[];
-extern char datatoc_gpu_shader_material_tex_musgrave_glsl[];
-extern char datatoc_gpu_shader_material_tex_noise_glsl[];
-extern char datatoc_gpu_shader_material_tex_sky_glsl[];
-extern char datatoc_gpu_shader_material_texture_coordinates_glsl[];
-extern char datatoc_gpu_shader_material_tex_voronoi_glsl[];
-extern char datatoc_gpu_shader_material_tex_wave_glsl[];
-extern char datatoc_gpu_shader_material_tex_white_noise_glsl[];
-extern char datatoc_gpu_shader_material_toon_glsl[];
-extern char datatoc_gpu_shader_material_translucent_glsl[];
-extern char datatoc_gpu_shader_material_transparent_glsl[];
-extern char datatoc_gpu_shader_material_uv_map_glsl[];
-extern char datatoc_gpu_shader_material_vector_curves_glsl[];
-extern char datatoc_gpu_shader_material_vector_displacement_glsl[];
-extern char datatoc_gpu_shader_material_vector_math_glsl[];
-extern char datatoc_gpu_shader_material_vector_rotate_glsl[];
-extern char datatoc_gpu_shader_material_velvet_glsl[];
-extern char datatoc_gpu_shader_material_vertex_color_glsl[];
-extern char datatoc_gpu_shader_material_volume_absorption_glsl[];
-extern char datatoc_gpu_shader_material_volume_info_glsl[];
-extern char datatoc_gpu_shader_material_volume_principled_glsl[];
-extern char datatoc_gpu_shader_material_volume_scatter_glsl[];
-extern char datatoc_gpu_shader_material_wireframe_glsl[];
-extern char datatoc_gpu_shader_material_world_normals_glsl[];
-
-static GPUMaterialLibrary gpu_shader_material_math_util_library = {
- .code = datatoc_gpu_shader_material_math_util_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_color_util_library = {
- .code = datatoc_gpu_shader_material_color_util_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_hash_library = {
- .code = datatoc_gpu_shader_material_hash_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_noise_library = {
- .code = datatoc_gpu_shader_material_noise_glsl,
- .dependencies = {&gpu_shader_material_hash_library, NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_fractal_noise_library = {
- .code = datatoc_gpu_shader_material_fractal_noise_glsl,
- .dependencies = {&gpu_shader_material_noise_library, NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_add_shader_library = {
- .code = datatoc_gpu_shader_material_add_shader_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_ambient_occlusion_library = {
- .code = datatoc_gpu_shader_material_ambient_occlusion_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_glossy_library = {
- .code = datatoc_gpu_shader_material_glossy_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_anisotropic_library = {
- .code = datatoc_gpu_shader_material_anisotropic_glsl,
- .dependencies = {&gpu_shader_material_glossy_library, NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_attribute_library = {
- .code = datatoc_gpu_shader_material_attribute_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_background_library = {
- .code = datatoc_gpu_shader_material_background_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_bevel_library = {
- .code = datatoc_gpu_shader_material_bevel_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_wavelength_library = {
- .code = datatoc_gpu_shader_material_wavelength_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_blackbody_library = {
- .code = datatoc_gpu_shader_material_blackbody_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_bright_contrast_library = {
- .code = datatoc_gpu_shader_material_bright_contrast_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_bump_library = {
- .code = datatoc_gpu_shader_material_bump_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_camera_library = {
- .code = datatoc_gpu_shader_material_camera_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_clamp_library = {
- .code = datatoc_gpu_shader_material_clamp_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_color_ramp_library = {
- .code = datatoc_gpu_shader_material_color_ramp_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_combine_hsv_library = {
- .code = datatoc_gpu_shader_material_combine_hsv_glsl,
- .dependencies = {&gpu_shader_material_color_util_library, NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_combine_rgb_library = {
- .code = datatoc_gpu_shader_material_combine_rgb_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_combine_xyz_library = {
- .code = datatoc_gpu_shader_material_combine_xyz_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_diffuse_library = {
- .code = datatoc_gpu_shader_material_diffuse_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_displacement_library = {
- .code = datatoc_gpu_shader_material_displacement_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_eevee_specular_library = {
- .code = datatoc_gpu_shader_material_eevee_specular_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_emission_library = {
- .code = datatoc_gpu_shader_material_emission_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_float_curve_library = {
- .code = datatoc_gpu_shader_material_float_curve_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_fresnel_library = {
- .code = datatoc_gpu_shader_material_fresnel_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_gamma_library = {
- .code = datatoc_gpu_shader_material_gamma_glsl,
- .dependencies = {&gpu_shader_material_math_util_library, NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_tangent_library = {
- .code = datatoc_gpu_shader_material_tangent_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_geometry_library = {
- .code = datatoc_gpu_shader_material_geometry_glsl,
- .dependencies = {&gpu_shader_material_tangent_library, NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_glass_library = {
- .code = datatoc_gpu_shader_material_glass_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_hair_info_library = {
- .code = datatoc_gpu_shader_material_hair_info_glsl,
- .dependencies = {&gpu_shader_material_hash_library, NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_holdout_library = {
- .code = datatoc_gpu_shader_material_holdout_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_hue_sat_val_library = {
- .code = datatoc_gpu_shader_material_hue_sat_val_glsl,
- .dependencies = {&gpu_shader_material_color_util_library, NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_invert_library = {
- .code = datatoc_gpu_shader_material_invert_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_layer_weight_library = {
- .code = datatoc_gpu_shader_material_layer_weight_glsl,
- .dependencies = {&gpu_shader_material_fresnel_library, NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_light_falloff_library = {
- .code = datatoc_gpu_shader_material_light_falloff_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_light_path_library = {
- .code = datatoc_gpu_shader_material_light_path_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_mapping_library = {
- .code = datatoc_gpu_shader_material_mapping_glsl,
- .dependencies = {&gpu_shader_material_math_util_library, NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_map_range_library = {
- .code = datatoc_gpu_shader_material_map_range_glsl,
- .dependencies = {&gpu_shader_material_math_util_library, NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_math_library = {
- .code = datatoc_gpu_shader_material_math_glsl,
- .dependencies = {&gpu_shader_material_math_util_library, NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_mix_rgb_library = {
- .code = datatoc_gpu_shader_material_mix_rgb_glsl,
- .dependencies = {&gpu_shader_material_color_util_library, NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_mix_shader_library = {
- .code = datatoc_gpu_shader_material_mix_shader_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_normal_library = {
- .code = datatoc_gpu_shader_material_normal_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_normal_map_library = {
- .code = datatoc_gpu_shader_material_normal_map_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_object_info_library = {
- .code = datatoc_gpu_shader_material_object_info_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_output_aov_library = {
- .code = datatoc_gpu_shader_material_output_aov_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_output_material_library = {
- .code = datatoc_gpu_shader_material_output_material_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_output_world_library = {
- .code = datatoc_gpu_shader_material_output_world_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_particle_info_library = {
- .code = datatoc_gpu_shader_material_particle_info_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_point_info_library = {
- .code = datatoc_gpu_shader_material_point_info_glsl,
- .dependencies = {&gpu_shader_material_hash_library, NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_principled_library = {
- .code = datatoc_gpu_shader_material_principled_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_refraction_library = {
- .code = datatoc_gpu_shader_material_refraction_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_rgb_curves_library = {
- .code = datatoc_gpu_shader_material_rgb_curves_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_rgb_to_bw_library = {
- .code = datatoc_gpu_shader_material_rgb_to_bw_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_separate_hsv_library = {
- .code = datatoc_gpu_shader_material_separate_hsv_glsl,
- .dependencies = {&gpu_shader_material_color_util_library, NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_separate_rgb_library = {
- .code = datatoc_gpu_shader_material_separate_rgb_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_separate_xyz_library = {
- .code = datatoc_gpu_shader_material_separate_xyz_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_set_library = {
- .code = datatoc_gpu_shader_material_set_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_shader_to_rgba_library = {
- .code = datatoc_gpu_shader_material_shader_to_rgba_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_squeeze_library = {
- .code = datatoc_gpu_shader_material_squeeze_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_subsurface_scattering_library = {
- .code = datatoc_gpu_shader_material_subsurface_scattering_glsl,
- .dependencies = {&gpu_shader_material_diffuse_library, NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_tex_brick_library = {
- .code = datatoc_gpu_shader_material_tex_brick_glsl,
- .dependencies = {&gpu_shader_material_math_util_library,
- &gpu_shader_material_hash_library,
- NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_tex_checker_library = {
- .code = datatoc_gpu_shader_material_tex_checker_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_tex_environment_library = {
- .code = datatoc_gpu_shader_material_tex_environment_glsl,
- .dependencies = {&gpu_shader_material_math_util_library, NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_tex_gradient_library = {
- .code = datatoc_gpu_shader_material_tex_gradient_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_tex_image_library = {
- .code = datatoc_gpu_shader_material_tex_image_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_tex_magic_library = {
- .code = datatoc_gpu_shader_material_tex_magic_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_tex_musgrave_library = {
- .code = datatoc_gpu_shader_material_tex_musgrave_glsl,
- .dependencies = {&gpu_shader_material_noise_library, NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_tex_noise_library = {
- .code = datatoc_gpu_shader_material_tex_noise_glsl,
- .dependencies = {&gpu_shader_material_fractal_noise_library, NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_tex_sky_library = {
- .code = datatoc_gpu_shader_material_tex_sky_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_texture_coordinates_library = {
- .code = datatoc_gpu_shader_material_texture_coordinates_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_tex_voronoi_library = {
- .code = datatoc_gpu_shader_material_tex_voronoi_glsl,
- .dependencies = {&gpu_shader_material_math_util_library,
- &gpu_shader_material_hash_library,
- NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_tex_wave_library = {
- .code = datatoc_gpu_shader_material_tex_wave_glsl,
- .dependencies = {&gpu_shader_material_fractal_noise_library, NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_tex_white_noise_library = {
- .code = datatoc_gpu_shader_material_tex_white_noise_glsl,
- .dependencies = {&gpu_shader_material_hash_library, NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_toon_library = {
- .code = datatoc_gpu_shader_material_toon_glsl,
- .dependencies = {&gpu_shader_material_diffuse_library, NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_translucent_library = {
- .code = datatoc_gpu_shader_material_translucent_glsl,
- .dependencies = {&gpu_shader_material_diffuse_library, NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_transparent_library = {
- .code = datatoc_gpu_shader_material_transparent_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_uv_map_library = {
- .code = datatoc_gpu_shader_material_uv_map_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_vector_curves_library = {
- .code = datatoc_gpu_shader_material_vector_curves_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_vector_displacement_library = {
- .code = datatoc_gpu_shader_material_vector_displacement_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_vector_math_library = {
- .code = datatoc_gpu_shader_material_vector_math_glsl,
- .dependencies = {&gpu_shader_material_math_util_library, NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_vector_rotate_library = {
- .code = datatoc_gpu_shader_material_vector_rotate_glsl,
- .dependencies = {&gpu_shader_material_math_util_library, NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_velvet_library = {
- .code = datatoc_gpu_shader_material_velvet_glsl,
- .dependencies = {&gpu_shader_material_diffuse_library, NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_vertex_color_library = {
- .code = datatoc_gpu_shader_material_vertex_color_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_volume_absorption_library = {
- .code = datatoc_gpu_shader_material_volume_absorption_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_volume_info_library = {
- .code = datatoc_gpu_shader_material_volume_info_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_volume_principled_library = {
- .code = datatoc_gpu_shader_material_volume_principled_glsl,
- .dependencies = {&gpu_shader_material_blackbody_library, NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_volume_scatter_library = {
- .code = datatoc_gpu_shader_material_volume_scatter_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_wireframe_library = {
- .code = datatoc_gpu_shader_material_wireframe_glsl,
- .dependencies = {NULL},
-};
-
-static GPUMaterialLibrary gpu_shader_material_world_normals_library = {
- .code = datatoc_gpu_shader_material_world_normals_glsl,
- .dependencies = {&gpu_shader_material_texture_coordinates_library, NULL},
-};
-
-static GPUMaterialLibrary *gpu_material_libraries[] = {
- &gpu_shader_material_math_util_library,
- &gpu_shader_material_color_util_library,
- &gpu_shader_material_hash_library,
- &gpu_shader_material_noise_library,
- &gpu_shader_material_float_curve_library,
- &gpu_shader_material_fractal_noise_library,
- &gpu_shader_material_add_shader_library,
- &gpu_shader_material_ambient_occlusion_library,
- &gpu_shader_material_glossy_library,
- &gpu_shader_material_anisotropic_library,
- &gpu_shader_material_attribute_library,
- &gpu_shader_material_background_library,
- &gpu_shader_material_bevel_library,
- &gpu_shader_material_wavelength_library,
- &gpu_shader_material_blackbody_library,
- &gpu_shader_material_bright_contrast_library,
- &gpu_shader_material_bump_library,
- &gpu_shader_material_camera_library,
- &gpu_shader_material_clamp_library,
- &gpu_shader_material_color_ramp_library,
- &gpu_shader_material_combine_hsv_library,
- &gpu_shader_material_combine_rgb_library,
- &gpu_shader_material_combine_xyz_library,
- &gpu_shader_material_diffuse_library,
- &gpu_shader_material_displacement_library,
- &gpu_shader_material_eevee_specular_library,
- &gpu_shader_material_emission_library,
- &gpu_shader_material_fresnel_library,
- &gpu_shader_material_gamma_library,
- &gpu_shader_material_tangent_library,
- &gpu_shader_material_geometry_library,
- &gpu_shader_material_glass_library,
- &gpu_shader_material_hair_info_library,
- &gpu_shader_material_holdout_library,
- &gpu_shader_material_hue_sat_val_library,
- &gpu_shader_material_invert_library,
- &gpu_shader_material_layer_weight_library,
- &gpu_shader_material_light_falloff_library,
- &gpu_shader_material_light_path_library,
- &gpu_shader_material_mapping_library,
- &gpu_shader_material_map_range_library,
- &gpu_shader_material_math_library,
- &gpu_shader_material_mix_rgb_library,
- &gpu_shader_material_mix_shader_library,
- &gpu_shader_material_normal_library,
- &gpu_shader_material_normal_map_library,
- &gpu_shader_material_object_info_library,
- &gpu_shader_material_output_aov_library,
- &gpu_shader_material_output_material_library,
- &gpu_shader_material_output_world_library,
- &gpu_shader_material_particle_info_library,
- &gpu_shader_material_point_info_library,
- &gpu_shader_material_principled_library,
- &gpu_shader_material_refraction_library,
- &gpu_shader_material_rgb_curves_library,
- &gpu_shader_material_rgb_to_bw_library,
- &gpu_shader_material_separate_hsv_library,
- &gpu_shader_material_separate_rgb_library,
- &gpu_shader_material_separate_xyz_library,
- &gpu_shader_material_set_library,
- &gpu_shader_material_shader_to_rgba_library,
- &gpu_shader_material_squeeze_library,
- &gpu_shader_material_subsurface_scattering_library,
- &gpu_shader_material_tex_brick_library,
- &gpu_shader_material_tex_checker_library,
- &gpu_shader_material_tex_environment_library,
- &gpu_shader_material_tex_gradient_library,
- &gpu_shader_material_tex_image_library,
- &gpu_shader_material_tex_magic_library,
- &gpu_shader_material_tex_musgrave_library,
- &gpu_shader_material_tex_noise_library,
- &gpu_shader_material_tex_sky_library,
- &gpu_shader_material_texture_coordinates_library,
- &gpu_shader_material_tex_voronoi_library,
- &gpu_shader_material_tex_wave_library,
- &gpu_shader_material_tex_white_noise_library,
- &gpu_shader_material_toon_library,
- &gpu_shader_material_translucent_library,
- &gpu_shader_material_transparent_library,
- &gpu_shader_material_uv_map_library,
- &gpu_shader_material_vector_curves_library,
- &gpu_shader_material_vector_displacement_library,
- &gpu_shader_material_vector_math_library,
- &gpu_shader_material_vector_rotate_library,
- &gpu_shader_material_velvet_library,
- &gpu_shader_material_vertex_color_library,
- &gpu_shader_material_volume_absorption_library,
- &gpu_shader_material_volume_info_library,
- &gpu_shader_material_volume_principled_library,
- &gpu_shader_material_volume_scatter_library,
- &gpu_shader_material_wireframe_library,
- &gpu_shader_material_world_normals_library,
- NULL};
-
-/* GLSL code parsing for finding function definitions.
- * These are stored in a hash for lookup when creating a material. */
-
-static GHash *FUNCTION_HASH = NULL;
-
-const char *gpu_str_skip_token(const char *str, char *token, int max)
-{
- int len = 0;
-
- /* skip a variable/function name */
- while (*str) {
- if (ELEM(*str, ' ', '(', ')', ',', ';', '\t', '\n', '\r')) {
- break;
- }
-
- if (token && len < max - 1) {
- *token = *str;
- token++;
- len++;
- }
- str++;
- }
-
- if (token) {
- *token = '\0';
- }
-
- /* skip the next special characters:
- * note the missing ')' */
- while (*str) {
- if (ELEM(*str, ' ', '(', ',', ';', '\t', '\n', '\r')) {
- str++;
- }
- else {
- break;
- }
- }
-
- return str;
-}
-
-/* Indices match the eGPUType enum */
-static const char *GPU_DATATYPE_STR[17] = {
- "",
- "float",
- "vec2",
- "vec3",
- "vec4",
- NULL,
- NULL,
- NULL,
- NULL,
- "mat3",
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- "mat4",
-};
-
-const char *gpu_data_type_to_string(const eGPUType type)
-{
- return GPU_DATATYPE_STR[type];
-}
-
-static void gpu_parse_material_library(GHash *hash, GPUMaterialLibrary *library)
-{
- GPUFunction *function;
- eGPUType type;
- GPUFunctionQual qual;
- int i;
- const char *code = library->code;
-
- while ((code = strstr(code, "void "))) {
- function = MEM_callocN(sizeof(GPUFunction), "GPUFunction");
- function->library = library;
-
- code = gpu_str_skip_token(code, NULL, 0);
- code = gpu_str_skip_token(code, function->name, MAX_FUNCTION_NAME);
-
- /* get parameters */
- while (*code && *code != ')') {
- if (BLI_str_startswith(code, "const ")) {
- code = gpu_str_skip_token(code, NULL, 0);
- }
-
- /* test if it's an input or output */
- qual = FUNCTION_QUAL_IN;
- if (BLI_str_startswith(code, "out ")) {
- qual = FUNCTION_QUAL_OUT;
- }
- if (BLI_str_startswith(code, "inout ")) {
- qual = FUNCTION_QUAL_INOUT;
- }
- if ((qual != FUNCTION_QUAL_IN) || BLI_str_startswith(code, "in ")) {
- code = gpu_str_skip_token(code, NULL, 0);
- }
-
- /* test for type */
- type = GPU_NONE;
- for (i = 1; i < ARRAY_SIZE(GPU_DATATYPE_STR); i++) {
- if (GPU_DATATYPE_STR[i] && BLI_str_startswith(code, GPU_DATATYPE_STR[i])) {
- type = i;
- break;
- }
- }
-
- if (!type && BLI_str_startswith(code, "samplerCube")) {
- type = GPU_TEXCUBE;
- }
- if (!type && BLI_str_startswith(code, "sampler2DShadow")) {
- type = GPU_SHADOW2D;
- }
- if (!type && BLI_str_startswith(code, "sampler1DArray")) {
- type = GPU_TEX1D_ARRAY;
- }
- if (!type && BLI_str_startswith(code, "sampler2DArray")) {
- type = GPU_TEX2D_ARRAY;
- }
- if (!type && BLI_str_startswith(code, "sampler2D")) {
- type = GPU_TEX2D;
- }
- if (!type && BLI_str_startswith(code, "sampler3D")) {
- type = GPU_TEX3D;
- }
-
- if (!type && BLI_str_startswith(code, "Closure")) {
- type = GPU_CLOSURE;
- }
-
- if (type) {
- /* add parameter */
- code = gpu_str_skip_token(code, NULL, 0);
- code = gpu_str_skip_token(code, NULL, 0);
- function->paramqual[function->totparam] = qual;
- function->paramtype[function->totparam] = type;
- function->totparam++;
- }
- else {
- fprintf(stderr, "GPU invalid function parameter in %s.\n", function->name);
- break;
- }
- }
-
- if (function->name[0] == '\0' || function->totparam == 0) {
- fprintf(stderr, "GPU functions parse error.\n");
- MEM_freeN(function);
- break;
- }
-
- BLI_ghash_insert(hash, function->name, function);
- }
-}
-
-/* Module */
-
-void gpu_material_library_init(void)
-{
- /* Only parse GLSL shader files once. */
- if (FUNCTION_HASH) {
- return;
- }
-
- FUNCTION_HASH = BLI_ghash_str_new("GPU_lookup_function gh");
- for (int i = 0; gpu_material_libraries[i]; i++) {
- gpu_parse_material_library(FUNCTION_HASH, gpu_material_libraries[i]);
- }
-}
-
-void gpu_material_library_exit(void)
-{
- if (FUNCTION_HASH) {
- BLI_ghash_free(FUNCTION_HASH, NULL, MEM_freeN);
- FUNCTION_HASH = NULL;
- }
-}
-
-/* Code Generation */
-
-static void gpu_material_use_library_with_dependencies(GSet *used_libraries,
- GPUMaterialLibrary *library)
-{
- if (BLI_gset_add(used_libraries, library->code)) {
- for (int i = 0; library->dependencies[i]; i++) {
- gpu_material_use_library_with_dependencies(used_libraries, library->dependencies[i]);
- }
- }
-}
-
-GPUFunction *gpu_material_library_use_function(GSet *used_libraries, const char *name)
-{
- GPUFunction *function = BLI_ghash_lookup(FUNCTION_HASH, (const void *)name);
- if (function) {
- gpu_material_use_library_with_dependencies(used_libraries, function->library);
- }
- return function;
-}
-
-char *gpu_material_library_generate_code(GSet *used_libraries, const char *frag_lib)
-{
- DynStr *ds = BLI_dynstr_new();
-
- if (frag_lib) {
- BLI_dynstr_append(ds, frag_lib);
- }
-
- /* Always include those because they may be needed by the execution function. */
- gpu_material_use_library_with_dependencies(used_libraries,
- &gpu_shader_material_world_normals_library);
-
- /* Add library code in order, for dependencies. */
- for (int i = 0; gpu_material_libraries[i]; i++) {
- GPUMaterialLibrary *library = gpu_material_libraries[i];
- if (BLI_gset_haskey(used_libraries, library->code)) {
- BLI_dynstr_append(ds, library->code);
- }
- }
-
- char *result = BLI_dynstr_get_cstring(ds);
- BLI_dynstr_free(ds);
-
- return result;
-}
diff --git a/source/blender/gpu/intern/gpu_material_library.h b/source/blender/gpu/intern/gpu_material_library.h
index 96e1a265c72..ffc2228a49b 100644
--- a/source/blender/gpu/intern/gpu_material_library.h
+++ b/source/blender/gpu/intern/gpu_material_library.h
@@ -10,16 +10,15 @@
#include "GPU_material.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#define MAX_FUNCTION_NAME 64
#define MAX_PARAMETER 36
struct GSet;
-typedef struct GPUMaterialLibrary {
- char *code;
- struct GPUMaterialLibrary *dependencies[8];
-} GPUMaterialLibrary;
-
typedef enum {
FUNCTION_QUAL_IN,
FUNCTION_QUAL_OUT,
@@ -31,20 +30,12 @@ typedef struct GPUFunction {
eGPUType paramtype[MAX_PARAMETER];
GPUFunctionQual paramqual[MAX_PARAMETER];
int totparam;
- GPUMaterialLibrary *library;
+ /* TOOD(@fclem): Clean that void pointer. */
+ void *source; /* GPUSource */
} GPUFunction;
-/* Module */
-
-void gpu_material_library_init(void);
-void gpu_material_library_exit(void);
-
-/* Code Generation */
-
GPUFunction *gpu_material_library_use_function(struct GSet *used_libraries, const char *name);
-char *gpu_material_library_generate_code(struct GSet *used_libraries, const char *frag_lib);
-
-/* Code Parsing */
-const char *gpu_str_skip_token(const char *str, char *token, int max);
-const char *gpu_data_type_to_string(eGPUType type);
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/gpu/intern/gpu_node_graph.c b/source/blender/gpu/intern/gpu_node_graph.c
index 57e415a8183..d9143a12d5b 100644
--- a/source/blender/gpu/intern/gpu_node_graph.c
+++ b/source/blender/gpu/intern/gpu_node_graph.c
@@ -87,10 +87,6 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const eGPUType
input->type = type;
switch (link->link_type) {
- case GPU_NODE_LINK_BUILTIN:
- input->source = GPU_SOURCE_BUILTIN;
- input->builtin = link->builtin;
- break;
case GPU_NODE_LINK_OUTPUT:
input->source = GPU_SOURCE_OUTPUT;
input->link = link;
@@ -132,6 +128,11 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const eGPUType
case GPU_NODE_LINK_UNIFORM:
input->source = GPU_SOURCE_UNIFORM;
break;
+ case GPU_NODE_LINK_DIFFERENTIATE_FLOAT_FN:
+ input->source = GPU_SOURCE_FUNCTION_CALL;
+ /* NOTE(@fclem): End of function call is the return variable set during codegen. */
+ SNPRINTF(input->function_call, "dF_branch(%s(), ", link->function_name);
+ break;
default:
break;
}
@@ -478,6 +479,11 @@ GPUNodeLink *GPU_attribute(GPUMaterial *mat, const CustomDataType type, const ch
GPUNodeGraph *graph = gpu_material_node_graph(mat);
GPUMaterialAttribute *attr = gpu_node_graph_add_attribute(graph, type, name);
+ if (type == CD_ORCO) {
+ /* OPTI: orco might be computed from local positions and needs object infos. */
+ GPU_material_flag_set(mat, GPU_MATFLAG_OBJECT_INFO);
+ }
+
/* Dummy fallback if out of slots. */
if (attr == NULL) {
static const float zero_data[GPU_MAX_CONSTANT_DATA] = {0.0f};
@@ -523,6 +529,14 @@ GPUNodeLink *GPU_uniform(const float *num)
return link;
}
+GPUNodeLink *GPU_differentiate_float_function(const char *function_name)
+{
+ GPUNodeLink *link = gpu_node_link_create();
+ link->link_type = GPU_NODE_LINK_DIFFERENTIATE_FLOAT_FN;
+ link->function_name = function_name;
+ return link;
+}
+
GPUNodeLink *GPU_image(GPUMaterial *mat,
Image *ima,
ImageUser *iuser,
@@ -588,41 +602,35 @@ GPUNodeLink *GPU_volume_grid(GPUMaterial *mat,
transform_link->volume_grid = link->volume_grid;
transform_link->volume_grid->users++;
+ GPUNodeLink *cos_link = GPU_attribute(mat, CD_ORCO, "");
+
/* Two special cases, where we adjust the output values of smoke grids to
* bring the into standard range without having to modify the grid values. */
if (STREQ(name, "color")) {
- GPU_link(mat, "node_attribute_volume_color", link, transform_link, &link);
+ GPU_link(mat, "node_attribute_volume_color", link, transform_link, cos_link, &link);
}
else if (STREQ(name, "temperature")) {
- GPU_link(mat, "node_attribute_volume_temperature", link, transform_link, &link);
+ GPU_link(mat, "node_attribute_volume_temperature", link, transform_link, cos_link, &link);
}
else {
- GPU_link(mat, "node_attribute_volume", link, transform_link, &link);
+ GPU_link(mat, "node_attribute_volume", link, transform_link, cos_link, &link);
}
return link;
}
-GPUNodeLink *GPU_builtin(eGPUBuiltin builtin)
-{
- GPUNodeLink *link = gpu_node_link_create();
- link->link_type = GPU_NODE_LINK_BUILTIN;
- link->builtin = builtin;
- return link;
-}
-
/* Creating Nodes */
bool GPU_link(GPUMaterial *mat, const char *name, ...)
{
- GSet *used_libraries = gpu_material_used_libraries(mat);
+ GPUNodeGraph *graph = gpu_material_node_graph(mat);
GPUNode *node;
GPUFunction *function;
GPUNodeLink *link, **linkptr;
va_list params;
int i;
- function = gpu_material_library_use_function(used_libraries, name);
+ function = gpu_material_library_use_function(graph->used_libraries, name);
if (!function) {
fprintf(stderr, "GPU failed to find function %s\n", name);
return false;
@@ -643,27 +651,25 @@ bool GPU_link(GPUMaterial *mat, const char *name, ...)
}
va_end(params);
- GPUNodeGraph *graph = gpu_material_node_graph(mat);
BLI_addtail(&graph->nodes, node);
return true;
}
-bool GPU_stack_link(GPUMaterial *material,
- bNode *bnode,
- const char *name,
- GPUNodeStack *in,
- GPUNodeStack *out,
- ...)
+static bool gpu_stack_link_v(GPUMaterial *material,
+ bNode *bnode,
+ const char *name,
+ GPUNodeStack *in,
+ GPUNodeStack *out,
+ va_list params)
{
- GSet *used_libraries = gpu_material_used_libraries(material);
+ GPUNodeGraph *graph = gpu_material_node_graph(material);
GPUNode *node;
GPUFunction *function;
GPUNodeLink *link, **linkptr;
- va_list params;
int i, totin, totout;
- function = gpu_material_library_use_function(used_libraries, name);
+ function = gpu_material_library_use_function(graph->used_libraries, name);
if (!function) {
fprintf(stderr, "GPU failed to find function %s\n", name);
return false;
@@ -691,7 +697,6 @@ bool GPU_stack_link(GPUMaterial *material,
}
}
- va_start(params, out);
for (i = 0; i < function->totparam; i++) {
if (function->paramqual[i] != FUNCTION_QUAL_IN) {
if (totout == 0) {
@@ -717,14 +722,27 @@ bool GPU_stack_link(GPUMaterial *material,
}
}
}
- va_end(params);
- GPUNodeGraph *graph = gpu_material_node_graph(material);
BLI_addtail(&graph->nodes, node);
return true;
}
+bool GPU_stack_link(GPUMaterial *material,
+ bNode *bnode,
+ const char *name,
+ GPUNodeStack *in,
+ GPUNodeStack *out,
+ ...)
+{
+ va_list params;
+ va_start(params, out);
+ bool valid = gpu_stack_link_v(material, bnode, name, in, out, params);
+ va_end(params);
+
+ return valid;
+}
+
GPUNodeLink *GPU_uniformbuf_link_out(GPUMaterial *mat,
bNode *node,
GPUNodeStack *stack,
@@ -786,12 +804,16 @@ void gpu_node_graph_free_nodes(GPUNodeGraph *graph)
gpu_node_free(node);
}
- graph->outlink = NULL;
+ graph->outlink_surface = NULL;
+ graph->outlink_volume = NULL;
+ graph->outlink_displacement = NULL;
+ graph->outlink_thickness = NULL;
}
void gpu_node_graph_free(GPUNodeGraph *graph)
{
BLI_freelistN(&graph->outlink_aovs);
+ BLI_freelistN(&graph->material_functions);
gpu_node_graph_free_nodes(graph);
LISTBASE_FOREACH (GPUMaterialVolumeGrid *, grid, &graph->volume_grids) {
@@ -801,28 +823,32 @@ void gpu_node_graph_free(GPUNodeGraph *graph)
BLI_freelistN(&graph->textures);
BLI_freelistN(&graph->attributes);
GPU_uniform_attr_list_free(&graph->uniform_attrs);
+
+ if (graph->used_libraries) {
+ BLI_gset_free(graph->used_libraries, NULL);
+ graph->used_libraries = NULL;
+ }
}
/* Prune Unused Nodes */
-static void gpu_nodes_tag(GPUNodeLink *link)
+static void gpu_nodes_tag(GPUNodeLink *link, eGPUNodeTag tag)
{
GPUNode *node;
- GPUInput *input;
- if (!link->output) {
+ if (!link || !link->output) {
return;
}
node = link->output->node;
- if (node->tag) {
+ if (node->tag & tag) {
return;
}
- node->tag = true;
- for (input = node->inputs.first; input; input = input->next) {
+ node->tag |= tag;
+ LISTBASE_FOREACH (GPUInput *, input, &node->inputs) {
if (input->link) {
- gpu_nodes_tag(input->link);
+ gpu_nodes_tag(input->link, tag);
}
}
}
@@ -830,18 +856,25 @@ static void gpu_nodes_tag(GPUNodeLink *link)
void gpu_node_graph_prune_unused(GPUNodeGraph *graph)
{
LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) {
- node->tag = false;
+ node->tag = GPU_NODE_TAG_NONE;
}
- gpu_nodes_tag(graph->outlink);
+ gpu_nodes_tag(graph->outlink_surface, GPU_NODE_TAG_SURFACE);
+ gpu_nodes_tag(graph->outlink_volume, GPU_NODE_TAG_VOLUME);
+ gpu_nodes_tag(graph->outlink_displacement, GPU_NODE_TAG_DISPLACEMENT);
+ gpu_nodes_tag(graph->outlink_thickness, GPU_NODE_TAG_THICKNESS);
+
LISTBASE_FOREACH (GPUNodeGraphOutputLink *, aovlink, &graph->outlink_aovs) {
- gpu_nodes_tag(aovlink->outlink);
+ gpu_nodes_tag(aovlink->outlink, GPU_NODE_TAG_AOV);
+ }
+ LISTBASE_FOREACH (GPUNodeGraphFunctionLink *, funclink, &graph->material_functions) {
+ gpu_nodes_tag(funclink->outlink, GPU_NODE_TAG_FUNCTION);
}
for (GPUNode *node = graph->nodes.first, *next = NULL; node; node = next) {
next = node->next;
- if (!node->tag) {
+ if (node->tag == GPU_NODE_TAG_NONE) {
BLI_remlink(&graph->nodes, node);
gpu_node_free(node);
}
diff --git a/source/blender/gpu/intern/gpu_node_graph.h b/source/blender/gpu/intern/gpu_node_graph.h
index 5856001d548..024119e1c24 100644
--- a/source/blender/gpu/intern/gpu_node_graph.h
+++ b/source/blender/gpu/intern/gpu_node_graph.h
@@ -12,9 +12,15 @@
#include "DNA_customdata_types.h"
#include "DNA_listBase.h"
+#include "BLI_ghash.h"
+
#include "GPU_material.h"
#include "GPU_shader.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct GPUNode;
struct GPUOutput;
struct ListBase;
@@ -25,19 +31,18 @@ typedef enum eGPUDataSource {
GPU_SOURCE_UNIFORM,
GPU_SOURCE_ATTR,
GPU_SOURCE_UNIFORM_ATTR,
- GPU_SOURCE_BUILTIN,
GPU_SOURCE_STRUCT,
GPU_SOURCE_TEX,
GPU_SOURCE_TEX_TILED_MAPPING,
GPU_SOURCE_VOLUME_GRID,
GPU_SOURCE_VOLUME_GRID_TRANSFORM,
+ GPU_SOURCE_FUNCTION_CALL,
} eGPUDataSource;
typedef enum {
GPU_NODE_LINK_NONE = 0,
GPU_NODE_LINK_ATTR,
GPU_NODE_LINK_UNIFORM_ATTR,
- GPU_NODE_LINK_BUILTIN,
GPU_NODE_LINK_COLORBAND,
GPU_NODE_LINK_CONSTANT,
GPU_NODE_LINK_IMAGE,
@@ -47,15 +52,28 @@ typedef enum {
GPU_NODE_LINK_VOLUME_GRID_TRANSFORM,
GPU_NODE_LINK_OUTPUT,
GPU_NODE_LINK_UNIFORM,
+ GPU_NODE_LINK_DIFFERENTIATE_FLOAT_FN,
} GPUNodeLinkType;
+typedef enum {
+ GPU_NODE_TAG_NONE = 0,
+ GPU_NODE_TAG_SURFACE = (1 << 0),
+ GPU_NODE_TAG_VOLUME = (1 << 1),
+ GPU_NODE_TAG_DISPLACEMENT = (1 << 2),
+ GPU_NODE_TAG_THICKNESS = (1 << 3),
+ GPU_NODE_TAG_AOV = (1 << 4),
+ GPU_NODE_TAG_FUNCTION = (1 << 5),
+} eGPUNodeTag;
+
+ENUM_OPERATORS(eGPUNodeTag, GPU_NODE_TAG_FUNCTION)
+
struct GPUNode {
struct GPUNode *next, *prev;
const char *name;
/* Internal flag to mark nodes during pruning */
- bool tag;
+ eGPUNodeTag tag;
ListBase inputs;
ListBase outputs;
@@ -70,8 +88,6 @@ struct GPUNodeLink {
union {
/* GPU_NODE_LINK_CONSTANT | GPU_NODE_LINK_UNIFORM */
const float *data;
- /* GPU_NODE_LINK_BUILTIN */
- eGPUBuiltin builtin;
/* GPU_NODE_LINK_COLORBAND */
struct GPUTexture **colorband;
/* GPU_NODE_LINK_VOLUME_GRID */
@@ -84,6 +100,8 @@ struct GPUNodeLink {
struct GPUUniformAttr *uniform_attr;
/* GPU_NODE_LINK_IMAGE_BLENDER */
struct GPUMaterialTexture *texture;
+ /* GPU_NODE_LINK_DIFFERENTIATE_FLOAT_FN */
+ const char *function_name;
};
};
@@ -110,8 +128,6 @@ typedef struct GPUInput {
union {
/* GPU_SOURCE_CONSTANT | GPU_SOURCE_UNIFORM */
float vec[16]; /* vector data */
- /* GPU_SOURCE_BUILTIN */
- eGPUBuiltin builtin; /* builtin uniform */
/* GPU_SOURCE_TEX | GPU_SOURCE_TEX_TILED_MAPPING */
struct GPUMaterialTexture *texture;
/* GPU_SOURCE_ATTR */
@@ -120,6 +136,8 @@ typedef struct GPUInput {
struct GPUUniformAttr *uniform_attr;
/* GPU_SOURCE_VOLUME_GRID | GPU_SOURCE_VOLUME_GRID_TRANSFORM */
struct GPUMaterialVolumeGrid *volume_grid;
+ /* GPU_SOURCE_FUNCTION_CALL */
+ char function_call[64];
};
} GPUInput;
@@ -129,14 +147,25 @@ typedef struct GPUNodeGraphOutputLink {
GPUNodeLink *outlink;
} GPUNodeGraphOutputLink;
+typedef struct GPUNodeGraphFunctionLink {
+ struct GPUNodeGraphFunctionLink *next, *prev;
+ char name[16];
+ GPUNodeLink *outlink;
+} GPUNodeGraphFunctionLink;
+
typedef struct GPUNodeGraph {
/* Nodes */
ListBase nodes;
- /* Main Output. */
- GPUNodeLink *outlink;
+ /* Main Outputs. */
+ GPUNodeLink *outlink_surface;
+ GPUNodeLink *outlink_volume;
+ GPUNodeLink *outlink_displacement;
+ GPUNodeLink *outlink_thickness;
/* List of GPUNodeGraphOutputLink */
ListBase outlink_aovs;
+ /* List of GPUNodeGraphFunctionLink */
+ ListBase material_functions;
/* Requested attributes and textures. */
ListBase attributes;
@@ -145,6 +174,9 @@ typedef struct GPUNodeGraph {
/* The list of uniform attributes. */
GPUUniformAttrList uniform_attrs;
+
+ /** Set of all the GLSL lib code blocks . */
+ GSet *used_libraries;
} GPUNodeGraph;
/* Node Graph */
@@ -171,4 +203,6 @@ struct GPUTexture **gpu_material_ramp_texture_row_set(struct GPUMaterial *mat,
float *pixels,
float *row);
-struct GSet *gpu_material_used_libraries(struct GPUMaterial *material);
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc
index b434cfbbb0e..fe9aacb95f9 100644
--- a/source/blender/gpu/intern/gpu_shader.cc
+++ b/source/blender/gpu/intern/gpu_shader.cc
@@ -249,6 +249,19 @@ const GPUShaderCreateInfo *GPU_shader_create_info_get(const char *info_name)
return gpu_shader_create_info_get(info_name);
}
+bool GPU_shader_create_info_check_error(const GPUShaderCreateInfo *_info, char r_error[128])
+{
+ using namespace blender::gpu::shader;
+ const ShaderCreateInfo &info = *reinterpret_cast<const ShaderCreateInfo *>(_info);
+ std::string error = info.check_error();
+ if (error.length() == 0) {
+ return true;
+ }
+
+ BLI_strncpy(r_error, error.c_str(), 128);
+ return false;
+}
+
GPUShader *GPU_shader_create_from_info_name(const char *info_name)
{
using namespace blender::gpu::shader;
@@ -270,28 +283,10 @@ GPUShader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info)
GPU_debug_group_begin(GPU_DEBUG_SHADER_COMPILATION_GROUP);
- /* At least a vertex shader and a fragment shader are required, or only a compute shader. */
- if (info.compute_source_.is_empty()) {
- if (info.vertex_source_.is_empty()) {
- printf("Missing vertex shader in %s.\n", info.name_.c_str());
- }
- if (info.fragment_source_.is_empty()) {
- printf("Missing fragment shader in %s.\n", info.name_.c_str());
- }
- BLI_assert(!info.vertex_source_.is_empty() && !info.fragment_source_.is_empty());
- }
- else {
- if (!info.vertex_source_.is_empty()) {
- printf("Compute shader has vertex_source_ shader attached in %s.\n", info.name_.c_str());
- }
- if (!info.geometry_source_.is_empty()) {
- printf("Compute shader has geometry_source_ shader attached in %s.\n", info.name_.c_str());
- }
- if (!info.fragment_source_.is_empty()) {
- printf("Compute shader has fragment_source_ shader attached in %s.\n", info.name_.c_str());
- }
- BLI_assert(info.vertex_source_.is_empty() && info.geometry_source_.is_empty() &&
- info.fragment_source_.is_empty());
+ const std::string error = info.check_error();
+ if (!error.empty()) {
+ printf("%s\n", error.c_str());
+ BLI_assert(false);
}
Shader *shader = GPUBackend::get()->shader_alloc(info.name_.c_str());
@@ -299,7 +294,9 @@ GPUShader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info)
std::string defines = shader->defines_declare(info);
std::string resources = shader->resources_declare(info);
- defines += "#define USE_GPU_SHADER_CREATE_INFO\n";
+ if (info.legacy_resource_location_ == false) {
+ defines += "#define USE_GPU_SHADER_CREATE_INFO\n";
+ }
Vector<const char *> typedefs;
if (!info.typedef_sources_.is_empty() || !info.typedef_source_generated.empty()) {
@@ -367,6 +364,7 @@ GPUShader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info)
sources.append(resources.c_str());
sources.append(layout.c_str());
sources.append(interface.c_str());
+ sources.append(info.geometry_source_generated.c_str());
sources.extend(code);
shader->geometry_shader_from_glsl(sources);
diff --git a/source/blender/gpu/intern/gpu_shader_builder_stubs.cc b/source/blender/gpu/intern/gpu_shader_builder_stubs.cc
index 95f9b031fe8..515f65adb73 100644
--- a/source/blender/gpu/intern/gpu_shader_builder_stubs.cc
+++ b/source/blender/gpu/intern/gpu_shader_builder_stubs.cc
@@ -202,10 +202,7 @@ int BKE_subdiv_ccg_grid_to_face_index(const SubdivCCG *UNUSED(subdiv_ccg),
/* -------------------------------------------------------------------- */
/** \name Stubs of BKE_node.h
* \{ */
-void ntreeGPUMaterialNodes(struct bNodeTree *UNUSED(localtree),
- struct GPUMaterial *UNUSED(mat),
- bool *UNUSED(has_surface_output),
- bool *UNUSED(has_volume_output))
+void ntreeGPUMaterialNodes(struct bNodeTree *UNUSED(localtree), struct GPUMaterial *UNUSED(mat))
{
BLI_assert_unreachable();
}
diff --git a/source/blender/gpu/intern/gpu_shader_create_info.cc b/source/blender/gpu/intern/gpu_shader_create_info.cc
index 0dd82d4ea44..6e82201b424 100644
--- a/source/blender/gpu/intern/gpu_shader_create_info.cc
+++ b/source/blender/gpu/intern/gpu_shader_create_info.cc
@@ -136,6 +136,34 @@ void ShaderCreateInfo::finalize()
}
}
+std::string ShaderCreateInfo::check_error() const
+{
+ std::string error;
+
+ /* At least a vertex shader and a fragment shader are required, or only a compute shader. */
+ if (this->compute_source_.is_empty()) {
+ if (this->vertex_source_.is_empty()) {
+ error += "Missing vertex shader in " + this->name_ + ".\n";
+ }
+ if (this->fragment_source_.is_empty()) {
+ error += "Missing fragment shader in " + this->name_ + ".\n";
+ }
+ }
+ else {
+ if (!this->vertex_source_.is_empty()) {
+ error += "Compute shader has vertex_source_ shader attached in" + this->name_ + ".\n";
+ }
+ if (!this->geometry_source_.is_empty()) {
+ error += "Compute shader has geometry_source_ shader attached in" + this->name_ + ".\n";
+ }
+ if (!this->fragment_source_.is_empty()) {
+ error += "Compute shader has fragment_source_ shader attached in" + this->name_ + ".\n";
+ }
+ }
+
+ return error;
+}
+
void ShaderCreateInfo::validate(const ShaderCreateInfo &other_info)
{
if (!auto_resource_location_) {
diff --git a/source/blender/gpu/intern/gpu_shader_create_info.hh b/source/blender/gpu/intern/gpu_shader_create_info.hh
index 51008993353..3ab96d0d84a 100644
--- a/source/blender/gpu/intern/gpu_shader_create_info.hh
+++ b/source/blender/gpu/intern/gpu_shader_create_info.hh
@@ -284,6 +284,8 @@ struct ShaderCreateInfo {
bool auto_resource_location_ = false;
/** If true, force depth and stencil tests to always happen before fragment shader invocation. */
bool early_fragment_test_ = false;
+ /** If true, force the use of the GL shader introspection for resource location. */
+ bool legacy_resource_location_ = false;
/** Allow optimization when fragment shader writes to `gl_FragDepth`. */
DepthWrite depth_write_ = DepthWrite::ANY;
/**
@@ -296,6 +298,7 @@ struct ShaderCreateInfo {
/** Manually set generated code. */
std::string vertex_source_generated = "";
std::string fragment_source_generated = "";
+ std::string geometry_source_generated = "";
std::string typedef_source_generated = "";
/** Manually set generated dependencies. */
Vector<const char *, 0> dependencies_generated;
@@ -721,6 +724,12 @@ struct ShaderCreateInfo {
return *(Self *)this;
}
+ Self &legacy_resource_location(bool value)
+ {
+ legacy_resource_location_ = value;
+ return *(Self *)this;
+ }
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -787,6 +796,8 @@ struct ShaderCreateInfo {
/* WARNING: Recursive. */
void finalize();
+ std::string check_error() const;
+
/** Error detection that some backend compilers do not complain about. */
void validate(const ShaderCreateInfo &other_info);
diff --git a/source/blender/gpu/intern/gpu_shader_dependency.cc b/source/blender/gpu/intern/gpu_shader_dependency.cc
index 2c6845334d3..460b6d32967 100644
--- a/source/blender/gpu/intern/gpu_shader_dependency.cc
+++ b/source/blender/gpu/intern/gpu_shader_dependency.cc
@@ -10,11 +10,14 @@
#include <iomanip>
#include <iostream>
+#include <sstream>
+#include "BLI_ghash.h"
#include "BLI_map.hh"
#include "BLI_set.hh"
#include "BLI_string_ref.hh"
+#include "gpu_material_library.h"
#include "gpu_shader_create_info.hh"
#include "gpu_shader_dependency_private.h"
@@ -31,6 +34,7 @@ extern "C" {
namespace blender::gpu {
using GPUSourceDictionnary = Map<StringRef, struct GPUSource *>;
+using GPUFunctionDictionnary = Map<StringRef, struct GPUFunction *>;
struct GPUSource {
StringRefNull fullpath;
@@ -41,7 +45,10 @@ struct GPUSource {
shader::BuiltinBits builtins = (shader::BuiltinBits)0;
std::string processed_source;
- GPUSource(const char *path, const char *file, const char *datatoc)
+ GPUSource(const char *path,
+ const char *file,
+ const char *datatoc,
+ GPUFunctionDictionnary *g_functions)
: fullpath(path), filename(file), source(datatoc)
{
/* Scan for builtins. */
@@ -92,16 +99,20 @@ struct GPUSource {
if (filename.endswith(".h") || filename.endswith(".hh")) {
enum_preprocess();
}
+
+ if (is_from_material_library()) {
+ material_functions_parse(g_functions);
+ }
};
- bool is_in_comment(const StringRef &input, int64_t offset)
+ static bool is_in_comment(const StringRef &input, int64_t offset)
{
return (input.rfind("/*", offset) > input.rfind("*/", offset)) ||
(input.rfind("//", offset) > input.rfind("\n", offset));
}
template<bool check_whole_word = true, bool reversed = false, typename T>
- int64_t find_str(const StringRef &input, const T keyword, int64_t offset = 0)
+ static int64_t find_str(const StringRef &input, const T keyword, int64_t offset = 0)
{
while (true) {
if constexpr (reversed) {
@@ -114,7 +125,7 @@ struct GPUSource {
if constexpr (check_whole_word) {
/* Fix false positive if something has "enum" as suffix. */
char previous_char = input[offset - 1];
- if (!(ELEM(previous_char, '\n', '\t', ' ', ':'))) {
+ if (!(ELEM(previous_char, '\n', '\t', ' ', ':', '(', ','))) {
offset += (reversed) ? -1 : 1;
continue;
}
@@ -129,9 +140,13 @@ struct GPUSource {
}
}
+#define find_keyword find_str<true, false>
+#define rfind_keyword find_str<true, true>
+#define find_token find_str<false, false>
+#define rfind_token find_str<false, true>
+
void print_error(const StringRef &input, int64_t offset, const StringRef message)
{
- std::cout << " error: " << message << "\n";
StringRef sub = input.substr(0, offset);
int64_t line_number = std::count(sub.begin(), sub.end(), '\n') + 1;
int64_t line_end = input.find("\n", offset);
@@ -152,6 +167,12 @@ struct GPUSource {
std::cout << "^\n";
}
+#define CHECK(test_value, str, ofs, msg) \
+ if ((test_value) == -1) { \
+ print_error(str, ofs, msg); \
+ continue; \
+ }
+
/**
* Transform C,C++ enum declaration into GLSL compatible defines and constants:
*
@@ -193,16 +214,6 @@ struct GPUSource {
int64_t last_pos = 0;
const bool is_cpp = filename.endswith(".hh");
-#define find_keyword find_str<true, false>
-#define find_token find_str<false, false>
-#define rfind_token find_str<false, true>
-#define CHECK(test_value, str, ofs, msg) \
- if ((test_value) == -1) { \
- print_error(str, ofs, msg); \
- cursor++; \
- continue; \
- }
-
while (true) {
cursor = find_keyword(input, "enum ", cursor + 1);
if (cursor == -1) {
@@ -268,10 +279,6 @@ struct GPUSource {
return;
}
-#undef find_keyword
-#undef find_token
-#undef rfind_token
-
if (last_pos != 0) {
output += input.substr(last_pos);
}
@@ -279,37 +286,237 @@ struct GPUSource {
source = processed_source.c_str();
};
+ void material_functions_parse(GPUFunctionDictionnary *g_functions)
+ {
+ const StringRefNull input = source;
+
+ const char whitespace_chars[] = " \r\n\t";
+
+ auto function_parse = [&](const StringRef input,
+ int64_t &cursor,
+ StringRef &out_return_type,
+ StringRef &out_name,
+ StringRef &out_args) -> bool {
+ cursor = find_keyword(input, "void ", cursor + 1);
+ if (cursor == -1) {
+ return false;
+ }
+ int64_t arg_start = find_token(input, '(', cursor);
+ if (arg_start == -1) {
+ return false;
+ }
+ int64_t arg_end = find_token(input, ')', arg_start);
+ if (arg_end == -1) {
+ return false;
+ }
+ int64_t body_start = find_token(input, '{', arg_end);
+ int64_t next_semicolon = find_token(input, ';', arg_end);
+ if (body_start != -1 && next_semicolon != -1 && body_start > next_semicolon) {
+ /* Assert no prototypes but could also just skip them. */
+ BLI_assert_msg(false, "No prototypes allowed in node GLSL libraries.");
+ }
+ int64_t name_start = input.find_first_not_of(whitespace_chars, input.find(' ', cursor));
+ if (name_start == -1) {
+ return false;
+ }
+ int64_t name_end = input.find_last_not_of(whitespace_chars, arg_start);
+ if (name_end == -1) {
+ return false;
+ }
+ /* Only support void type for now. */
+ out_return_type = "void";
+ out_name = input.substr(name_start, name_end - name_start);
+ out_args = input.substr(arg_start + 1, arg_end - (arg_start + 1));
+ return true;
+ };
+
+ auto keyword_parse = [&](const StringRef str, int64_t &cursor) -> StringRef {
+ int64_t keyword_start = str.find_first_not_of(whitespace_chars, cursor);
+ if (keyword_start == -1) {
+ /* No keyword found. */
+ return str.substr(0, 0);
+ }
+ int64_t keyword_end = str.find_first_of(whitespace_chars, keyword_start);
+ if (keyword_end == -1) {
+ /* Last keyword. */
+ keyword_end = str.size();
+ }
+ cursor = keyword_end + 1;
+ return str.substr(keyword_start, keyword_end - keyword_start);
+ };
+
+ auto arg_parse = [&](const StringRef str,
+ int64_t &cursor,
+ StringRef &out_qualifier,
+ StringRef &out_type,
+ StringRef &out_name) -> bool {
+ int64_t arg_start = cursor + 1;
+ if (arg_start >= str.size()) {
+ return false;
+ }
+ cursor = find_token(str, ',', arg_start);
+ if (cursor == -1) {
+ /* Last argument. */
+ cursor = str.size();
+ }
+ const StringRef arg = str.substr(arg_start, cursor - arg_start);
+
+ int64_t keyword_cursor = 0;
+ out_qualifier = keyword_parse(arg, keyword_cursor);
+ out_type = keyword_parse(arg, keyword_cursor);
+ out_name = keyword_parse(arg, keyword_cursor);
+ if (out_name.is_empty()) {
+ /* No qualifier case. */
+ out_name = out_type;
+ out_type = out_qualifier;
+ out_qualifier = arg.substr(0, 0);
+ }
+ return true;
+ };
+
+ int64_t cursor = -1;
+ StringRef func_return_type, func_name, func_args;
+ while (function_parse(input, cursor, func_return_type, func_name, func_args)) {
+ GPUFunction *func = MEM_new<GPUFunction>(__func__);
+ func_name.copy(func->name, sizeof(func->name));
+ func->source = reinterpret_cast<void *>(this);
+
+ bool insert = g_functions->add(func->name, func);
+
+ /* NOTE: We allow overloading non void function, but only if the function comes from the
+ * same file. Otherwise the dependency system breaks. */
+ if (!insert) {
+ GPUSource *other_source = reinterpret_cast<GPUSource *>(
+ g_functions->lookup(func_name)->source);
+ if (other_source != this) {
+ print_error(input,
+ source.find(func_name),
+ "Function redefinition or overload in two different files ...");
+ print_error(
+ input, other_source->source.find(func_name), "... previous definition was here");
+ }
+ else {
+ /* Non-void function overload. */
+ MEM_delete(func);
+ }
+ continue;
+ }
+
+ if (func_return_type != "void") {
+ continue;
+ }
+
+ func->totparam = 0;
+ int64_t args_cursor = -1;
+ StringRef arg_qualifier, arg_type, arg_name;
+ while (arg_parse(func_args, args_cursor, arg_qualifier, arg_type, arg_name)) {
+
+ if (func->totparam >= ARRAY_SIZE(func->paramtype)) {
+ print_error(input, source.find(func_name), "Too much parameter in function");
+ break;
+ }
+
+ auto parse_qualifier = [](StringRef qualifier) -> GPUFunctionQual {
+ if (qualifier == "out") {
+ return FUNCTION_QUAL_OUT;
+ }
+ if (qualifier == "inout") {
+ return FUNCTION_QUAL_INOUT;
+ }
+ return FUNCTION_QUAL_IN;
+ };
+
+ auto parse_type = [](StringRef type) -> eGPUType {
+ if (type == "float") {
+ return GPU_FLOAT;
+ }
+ if (type == "vec2") {
+ return GPU_VEC2;
+ }
+ if (type == "vec3") {
+ return GPU_VEC3;
+ }
+ if (type == "vec4") {
+ return GPU_VEC4;
+ }
+ if (type == "mat3") {
+ return GPU_MAT3;
+ }
+ if (type == "mat4") {
+ return GPU_MAT4;
+ }
+ if (type == "sampler1DArray") {
+ return GPU_TEX1D_ARRAY;
+ }
+ if (type == "sampler2DArray") {
+ return GPU_TEX2D_ARRAY;
+ }
+ if (type == "sampler2D") {
+ return GPU_TEX2D;
+ }
+ if (type == "sampler3D") {
+ return GPU_TEX3D;
+ }
+ if (type == "Closure") {
+ return GPU_CLOSURE;
+ }
+ return GPU_NONE;
+ };
+
+ func->paramqual[func->totparam] = parse_qualifier(arg_qualifier);
+ func->paramtype[func->totparam] = parse_type(arg_type);
+
+ if (func->paramtype[func->totparam] == GPU_NONE) {
+ std::string err = "Unknown parameter type \"" + arg_type + "\"";
+ int64_t err_ofs = source.find(func_name);
+ err_ofs = find_keyword(source, arg_name, err_ofs);
+ err_ofs = rfind_keyword(source, arg_type, err_ofs);
+ print_error(input, err_ofs, err);
+ }
+
+ func->totparam++;
+ }
+ }
+ }
+
+#undef find_keyword
+#undef rfind_keyword
+#undef find_token
+#undef rfind_token
+
/* Return 1 one error. */
- int init_dependencies(const GPUSourceDictionnary &dict)
+ int init_dependencies(const GPUSourceDictionnary &dict,
+ const GPUFunctionDictionnary &g_functions)
{
- if (dependencies_init) {
+ if (this->dependencies_init) {
return 0;
}
- dependencies_init = true;
- int64_t pos = 0;
+ this->dependencies_init = true;
+ int64_t pos = -1;
+
while (true) {
- pos = source.find("pragma BLENDER_REQUIRE(", pos);
- if (pos == -1) {
- return 0;
- }
- int64_t start = source.find('(', pos) + 1;
- int64_t end = source.find(')', pos);
- if (end == -1) {
- /* TODO Use clog. */
- std::cout << "Error: " << filename << " : Malformed BLENDER_REQUIRE: Missing \")\"."
- << std::endl;
- return 1;
- }
- StringRef dependency_name = source.substr(start, end - start);
- GPUSource *dependency_source = dict.lookup_default(dependency_name, nullptr);
- if (dependency_source == nullptr) {
- /* TODO Use clog. */
- std::cout << "Error: " << filename << " : Dependency not found \"" << dependency_name
- << "\"." << std::endl;
- return 1;
+ GPUSource *dependency_source = nullptr;
+
+ {
+ pos = source.find("pragma BLENDER_REQUIRE(", pos + 1);
+ if (pos == -1) {
+ return 0;
+ }
+ int64_t start = source.find('(', pos) + 1;
+ int64_t end = source.find(')', pos);
+ if (end == -1) {
+ print_error(source, start, "Malformed BLENDER_REQUIRE: Missing \")\" token");
+ return 1;
+ }
+ StringRef dependency_name = source.substr(start, end - start);
+ dependency_source = dict.lookup_default(dependency_name, nullptr);
+ if (dependency_source == nullptr) {
+ print_error(source, start, "Dependency not found");
+ return 1;
+ }
}
/* Recursive. */
- int result = dependency_source->init_dependencies(dict);
+ int result = dependency_source->init_dependencies(dict, g_functions);
if (result != 0) {
return 1;
}
@@ -318,8 +525,8 @@ struct GPUSource {
dependencies.append_non_duplicates(dep);
}
dependencies.append_non_duplicates(dependency_source);
- pos++;
- };
+ }
+ return 0;
}
/* Returns the final string with all includes done. */
@@ -339,6 +546,11 @@ struct GPUSource {
}
return out_builtins;
}
+
+ bool is_from_material_library() const
+ {
+ return filename.startswith("gpu_shader_material_") && filename.endswith(".glsl");
+ }
};
} // namespace blender::gpu
@@ -346,13 +558,15 @@ struct GPUSource {
using namespace blender::gpu;
static GPUSourceDictionnary *g_sources = nullptr;
+static GPUFunctionDictionnary *g_functions = nullptr;
void gpu_shader_dependency_init()
{
g_sources = new GPUSourceDictionnary();
+ g_functions = new GPUFunctionDictionnary();
#define SHADER_SOURCE(datatoc, filename, filepath) \
- g_sources->add_new(filename, new GPUSource(filepath, filename, datatoc));
+ g_sources->add_new(filename, new GPUSource(filepath, filename, datatoc, g_functions));
#include "glsl_draw_source_list.h"
#include "glsl_gpu_source_list.h"
#ifdef WITH_OCIO
@@ -362,7 +576,7 @@ void gpu_shader_dependency_init()
int errors = 0;
for (auto *value : g_sources->values()) {
- errors += value->init_dependencies(*g_sources);
+ errors += value->init_dependencies(*g_sources, *g_functions);
}
BLI_assert_msg(errors == 0, "Dependency errors detected: Aborting");
UNUSED_VARS_NDEBUG(errors);
@@ -373,7 +587,20 @@ void gpu_shader_dependency_exit()
for (auto *value : g_sources->values()) {
delete value;
}
+ for (auto *value : g_functions->values()) {
+ MEM_delete(value);
+ }
delete g_sources;
+ delete g_functions;
+}
+
+GPUFunction *gpu_material_library_use_function(GSet *used_libraries, const char *name)
+{
+ GPUFunction *function = g_functions->lookup_default(name, nullptr);
+ BLI_assert_msg(function != nullptr, "Requested function not in the function library");
+ GPUSource *source = reinterpret_cast<GPUSource *>(function->source);
+ BLI_gset_add(used_libraries, const_cast<char *>(source->filename.c_str()));
+ return function;
}
namespace blender::gpu::shader {
diff --git a/source/blender/gpu/opengl/gl_shader.cc b/source/blender/gpu/opengl/gl_shader.cc
index 5938444ce49..9768318f3b6 100644
--- a/source/blender/gpu/opengl/gl_shader.cc
+++ b/source/blender/gpu/opengl/gl_shader.cc
@@ -572,10 +572,13 @@ std::string GLShader::fragment_interface_declare(const ShaderCreateInfo &info) c
}
if (bool(info.builtins_ & BuiltinBits::BARYCENTRIC_COORD)) {
if (!GLContext::native_barycentric_support) {
+ ss << "flat in vec4 gpu_pos[3];\n";
ss << "smooth in vec3 gpu_BaryCoord;\n";
ss << "noperspective in vec3 gpu_BaryCoordNoPersp;\n";
+ ss << "#define gpu_position_at_vertex(v) gpu_pos[v]\n";
}
else if (GLEW_AMD_shader_explicit_vertex_parameter) {
+ std::cout << "native" << std::endl;
/* NOTE(fclem): This won't work with geometry shader. Hopefully, we don't need geometry
* shader workaround if this extension/feature is detected. */
ss << "\n/* Stable Barycentric Coordinates. */\n";
@@ -591,6 +594,12 @@ std::string GLShader::fragment_interface_declare(const ShaderCreateInfo &info) c
ss << " if (interpolateAtVertexAMD(gpu_pos, 2) == gpu_pos_flat) { return bary.yzx; }\n";
ss << " return bary.xyz;\n";
ss << "}\n";
+ ss << "\n";
+ ss << "vec4 gpu_position_at_vertex(int v) {\n";
+ ss << " if (interpolateAtVertexAMD(gpu_pos, 0) == gpu_pos_flat) { v = (v + 2) % 3; }\n";
+ ss << " if (interpolateAtVertexAMD(gpu_pos, 2) == gpu_pos_flat) { v = (v + 1) % 3; }\n";
+ ss << " return interpolateAtVertexAMD(gpu_pos, v);\n";
+ ss << "}\n";
pre_main += " gpu_BaryCoord = stable_bary_(gl_BaryCoordSmoothAMD);\n";
pre_main += " gpu_BaryCoordNoPersp = stable_bary_(gl_BaryCoordNoPerspAMD);\n";
@@ -730,6 +739,7 @@ std::string GLShader::workaround_geometry_shader_source_create(
ss << "in int gpu_Layer[];\n";
}
if (do_barycentric_workaround) {
+ ss << "flat out vec4 gpu_pos[3];\n";
ss << "smooth out vec3 gpu_BaryCoord;\n";
ss << "noperspective out vec3 gpu_BaryCoordNoPersp;\n";
}
@@ -740,6 +750,11 @@ std::string GLShader::workaround_geometry_shader_source_create(
if (do_layer_workaround) {
ss << " gl_Layer = gpu_Layer[0];\n";
}
+ if (do_barycentric_workaround) {
+ ss << " gpu_pos[0] = gl_in[0].gl_Position;\n";
+ ss << " gpu_pos[1] = gl_in[1].gl_Position;\n";
+ ss << " gpu_pos[2] = gl_in[2].gl_Position;\n";
+ }
for (auto i : IndexRange(3)) {
for (StageInterfaceInfo *iface : info_modified.vertex_out_interfaces_) {
for (auto &inout : iface->inouts) {
@@ -977,7 +992,7 @@ bool GLShader::finalize(const shader::ShaderCreateInfo *info)
return false;
}
- if (info != nullptr) {
+ if (info != nullptr && info->legacy_resource_location_ == false) {
interface = new GLShaderInterface(shader_program_, *info);
}
else {
diff --git a/source/blender/gpu/opengl/gl_texture.cc b/source/blender/gpu/opengl/gl_texture.cc
index b78e30e8b4c..14f84273925 100644
--- a/source/blender/gpu/opengl/gl_texture.cc
+++ b/source/blender/gpu/opengl/gl_texture.cc
@@ -72,7 +72,7 @@ bool GLTexture::init_internal()
GLenum internal_format = to_gl_internal_format(format_);
const bool is_cubemap = bool(type_ == GPU_TEXTURE_CUBE);
const bool is_layered = bool(type_ & GPU_TEXTURE_ARRAY);
- const bool is_compressed = bool(format_flag_ & GPU_TEXTURE_ARRAY);
+ const bool is_compressed = bool(format_flag_ & GPU_FORMAT_COMPRESSED);
const int dimensions = (is_cubemap) ? 2 : this->dimensions_count();
GLenum gl_format = to_gl_data_format(format_);
GLenum gl_type = to_gl(to_data_format(format_));
diff --git a/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl b/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl
index 193a4190cbf..5c97eada77d 100644
--- a/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl
@@ -95,3 +95,176 @@ vec4 tangent_get(vec4 attr, mat3 normalmat)
}
#endif
+
+/* Assumes GPU_VEC4 is color data. So converting to luminance like cycles. */
+#define float_from_vec4(v) dot(v.rgb, vec3(0.2126, 0.7152, 0.0722))
+#define float_from_vec3(v) avg(v.rgb)
+#define float_from_vec2(v) v.r
+
+#define vec2_from_vec4(v) vec2(avg(v.rgb), v.a)
+#define vec2_from_vec3(v) vec2(avg(v.rgb), 1.0)
+#define vec2_from_float(v) vec2(v)
+
+#define vec3_from_vec4(v) v.rgb
+#define vec3_from_vec2(v) v.rrr
+#define vec3_from_float(v) vec3(v)
+
+#define vec4_from_vec3(v) vec4(v, 1.0)
+#define vec4_from_vec2(v) v.rrrg
+#define vec4_from_float(v) vec4(vec3(v), 1.0)
+
+/* TODO: Move to shader_shared. */
+#define RAY_TYPE_CAMERA 0
+#define RAY_TYPE_SHADOW 1
+#define RAY_TYPE_DIFFUSE 2
+#define RAY_TYPE_GLOSSY 3
+
+#ifdef GPU_FRAGMENT_SHADER
+# define FrontFacing gl_FrontFacing
+#else
+# define FrontFacing true
+#endif
+
+struct ClosureDiffuse {
+ float weight;
+ vec3 color;
+ vec3 N;
+ vec3 sss_radius;
+ uint sss_id;
+};
+
+struct ClosureTranslucent {
+ float weight;
+ vec3 color;
+ vec3 N;
+};
+
+struct ClosureReflection {
+ float weight;
+ vec3 color;
+ vec3 N;
+ float roughness;
+};
+
+struct ClosureRefraction {
+ float weight;
+ vec3 color;
+ vec3 N;
+ float roughness;
+ float ior;
+};
+
+struct ClosureHair {
+ float weight;
+ vec3 color;
+ float offset;
+ vec2 roughness;
+ vec3 T;
+};
+
+struct ClosureVolumeScatter {
+ float weight;
+ vec3 scattering;
+ float anisotropy;
+};
+
+struct ClosureVolumeAbsorption {
+ float weight;
+ vec3 absorption;
+};
+
+struct ClosureEmission {
+ float weight;
+ vec3 emission;
+};
+
+struct ClosureTransparency {
+ float weight;
+ vec3 transmittance;
+ float holdout;
+};
+
+struct GlobalData {
+ /** World position. */
+ vec3 P;
+ /** Surface Normal. */
+ vec3 N;
+ /** Geometric Normal. */
+ vec3 Ng;
+ /** Surface default Tangent. */
+ vec3 T;
+ /** Barycentric coordinates. */
+ vec2 barycentric_coords;
+ vec3 barycentric_dists;
+ /** Ray properties (approximation). */
+ int ray_type;
+ float ray_depth;
+ float ray_length;
+ /** Hair time along hair length. 0 at base 1 at tip. */
+ float hair_time;
+ /** Hair time along width of the hair. */
+ float hair_time_width;
+ /** Hair thickness in world space. */
+ float hair_thickness;
+ /** Index of the strand for per strand effects. */
+ int hair_strand_id;
+ /** Is hair. */
+ bool is_strand;
+};
+
+GlobalData g_data;
+
+#ifndef GPU_FRAGMENT_SHADER
+/* Stubs. */
+vec3 dF_impl(vec3 v)
+{
+ return vec3(0.0);
+}
+
+void dF_branch(float fn, out vec2 result)
+{
+ result = vec2(0.0);
+}
+
+#elif 0 /* TODO(@fclem): User Option? */
+/* Fast derivatives */
+vec3 dF_impl(vec3 v)
+{
+ return vec3(0.0);
+}
+
+void dF_branch(float fn, out vec2 result)
+{
+ result.x = DFDX_SIGN * dFdx(fn);
+ result.y = DFDY_SIGN * dFdy(fn);
+}
+
+#else
+/* Precise derivatives */
+int g_derivative_flag = 0;
+
+vec3 dF_impl(vec3 v)
+{
+ if (g_derivative_flag > 0) {
+ return DFDX_SIGN * dFdx(v);
+ }
+ else if (g_derivative_flag < 0) {
+ return DFDY_SIGN * dFdy(v);
+ }
+ return vec3(0.0);
+}
+
+# define dF_branch(fn, result) \
+ if (true) { \
+ g_derivative_flag = 1; \
+ result.x = (fn); \
+ g_derivative_flag = -1; \
+ result.y = (fn); \
+ g_derivative_flag = 0; \
+ result -= vec2((fn)); \
+ }
+
+#endif
+
+/* TODO(fclem): Remove. */
+#define CODEGEN_LIB
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_ambient_occlusion.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_ambient_occlusion.glsl
index 12921a31b23..4ba1f6f7368 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_ambient_occlusion.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_ambient_occlusion.glsl
@@ -1,4 +1,4 @@
-#ifndef VOLUMETRICS
+
void node_ambient_occlusion(vec4 color,
float dist,
vec3 normal,
@@ -7,20 +7,6 @@ void node_ambient_occlusion(vec4 color,
out vec4 result_color,
out float result_ao)
{
- vec3 bent_normal;
- vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy);
- OcclusionData data = occlusion_search(viewPosition, maxzBuffer, dist, inverted, sample_count);
-
- vec3 V = cameraVec(worldPosition);
- vec3 N = normalize(normal);
- vec3 Ng = safe_normalize(cross(dFdx(worldPosition), dFdy(worldPosition)));
-
- float unused_error;
- vec3 unused;
- occlusion_eval(data, V, N, Ng, inverted, result_ao, unused_error, unused);
+ result_ao = ambient_occlusion_eval(normal, dist, inverted, sample_count);
result_color = result_ao * color;
}
-#else
-/* Stub ambient occlusion because it is not compatible with volumetrics. */
-# define node_ambient_occlusion(a, b, c, d, e, f) (e = vec4(0); f = 0.0)
-#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_anisotropic.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_anisotropic.glsl
index ec49cc86761..77de9e096a6 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_anisotropic.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_anisotropic.glsl
@@ -1,17 +1,27 @@
-#ifndef VOLUMETRICS
+
void node_bsdf_anisotropic(vec4 color,
float roughness,
float anisotropy,
float rotation,
vec3 N,
vec3 T,
- const float use_multiscatter,
- const float ssr_id,
+ float weight,
+ const float do_multiscatter,
out Closure result)
{
- node_bsdf_glossy(color, roughness, N, use_multiscatter, ssr_id, result);
+ N = safe_normalize(N);
+ vec3 V = cameraVec(g_data.P);
+ float NV = dot(N, V);
+
+ vec2 split_sum = brdf_lut(NV, roughness);
+
+ ClosureReflection reflection_data;
+ reflection_data.weight = weight;
+ reflection_data.color = (do_multiscatter != 0.0) ?
+ F_brdf_multi_scatter(color.rgb, color.rgb, split_sum) :
+ F_brdf_single_scatter(color.rgb, color.rgb, split_sum);
+ reflection_data.N = N;
+ reflection_data.roughness = roughness;
+
+ result = closure_eval(reflection_data);
}
-#else
-/* Stub anisotropic because it is not compatible with volumetrics. */
-# define node_bsdf_anisotropic(a, b, c, d, e, f, g, h, result) (result = CLOSURE_DEFAULT)
-#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_background.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_background.glsl
index 69ef4dcb7c7..2460bd63b38 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_background.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_background.glsl
@@ -1,11 +1,9 @@
-void node_background(vec4 color, float strength, out Closure result)
+
+void node_background(vec4 color, float strength, float weight, out Closure result)
{
-#ifndef VOLUMETRICS
- color *= strength;
- result = CLOSURE_DEFAULT;
- result.radiance = color.rgb;
- result.transmittance = vec3(0.0);
-#else
- result = CLOSURE_DEFAULT;
-#endif
+ ClosureEmission emission_data;
+ emission_data.weight = weight;
+ emission_data.emission = color.rgb * strength;
+
+ result = closure_eval(emission_data);
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_bump.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_bump.glsl
index 9f73f654217..e0931128485 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_bump.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_bump.glsl
@@ -1,28 +1,19 @@
-void dfdx_v3(vec3 v, out vec3 dy)
-{
- dy = v + DFDX_SIGN * dFdx(v);
-}
-void dfdy_v3(vec3 v, out vec3 dy)
+void differentiate_texco(vec3 v, out vec3 df)
{
- dy = v + DFDY_SIGN * dFdy(v);
+ /* Implementation defined. */
+ df = v + dF_impl(v);
}
-void node_bump(float strength,
- float dist,
- float height,
- float height_dx,
- float height_dy,
- vec3 N,
- vec3 surf_pos,
- float invert,
- out vec3 result)
+void node_bump(
+ float strength, float dist, float height, vec3 N, vec2 dHd, float invert, out vec3 result)
{
- N = mat3(ViewMatrix) * normalize(N);
- dist *= gl_FrontFacing ? invert : -invert;
+ N = normalize(N);
+ dist *= FrontFacing ? invert : -invert;
- vec3 dPdx = dFdx(surf_pos);
- vec3 dPdy = dFdy(surf_pos);
+#ifdef GPU_FRAGMENT_SHADER
+ vec3 dPdx = dFdx(g_data.P);
+ vec3 dPdy = dFdy(g_data.P);
/* Get surface tangents from normal. */
vec3 Rx = cross(dPdy, N);
@@ -31,14 +22,13 @@ void node_bump(float strength,
/* Compute surface gradient and determinant. */
float det = dot(dPdx, Rx);
- float dHdx = height_dx - height;
- float dHdy = height_dy - height;
- vec3 surfgrad = dHdx * Rx + dHdy * Ry;
+ vec3 surfgrad = dHd.x * Rx + dHd.y * Ry;
strength = max(strength, 0.0);
result = normalize(abs(det) * N - dist * sign(det) * surfgrad);
result = normalize(mix(N, result, strength));
-
- result = mat3(ViewMatrixInverse) * result;
+#else
+ result = N;
+#endif
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_camera.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_camera.glsl
index 03e61e9f472..ff84a0a334c 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_camera.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_camera.glsl
@@ -1,6 +1,6 @@
-void camera(vec3 co, out vec3 outview, out float outdepth, out float outdist)
+void camera(out vec3 outview, out float outdepth, out float outdist)
{
- outdepth = abs(co.z);
- outdist = length(co);
- outview = normalize(co);
+ outdepth = abs(transform_point(ViewMatrix, g_data.P).z);
+ outdist = distance(g_data.P, cameraPos);
+ outview = normalize(g_data.P - cameraPos);
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_combine_hsv.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_combine_hsv.glsl
index 2ce061da3cb..e8f444080b9 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_combine_hsv.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_combine_hsv.glsl
@@ -1,3 +1,5 @@
+#pragma BLENDER_REQUIRE(gpu_shader_material_color_util.glsl)
+
void combine_hsv(float h, float s, float v, out vec4 col)
{
hsv_to_rgb(vec4(h, s, v, 1.0), col);
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl
index ab6024b073d..bbdd86bd664 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl
@@ -1,28 +1,11 @@
-#ifndef VOLUMETRICS
-CLOSURE_EVAL_FUNCTION_DECLARE_1(node_bsdf_diffuse, Diffuse)
-
-void node_bsdf_diffuse(vec4 color, float roughness, vec3 N, out Closure result)
+void node_bsdf_diffuse(vec4 color, float roughness, vec3 N, float weight, out Closure result)
{
- CLOSURE_VARS_DECLARE_1(Diffuse);
-
- in_Diffuse_0.N = N; /* Normalized during eval. */
- in_Diffuse_0.albedo = color.rgb;
-
- CLOSURE_EVAL_FUNCTION_1(node_bsdf_diffuse, Diffuse);
-
- result = CLOSURE_DEFAULT;
+ ClosureDiffuse diffuse_data;
+ diffuse_data.weight = weight;
+ diffuse_data.color = color.rgb;
+ diffuse_data.N = N;
+ diffuse_data.sss_id = 0u;
- out_Diffuse_0.radiance = render_pass_diffuse_mask(vec3(1.0), out_Diffuse_0.radiance);
- out_Diffuse_0.radiance *= color.rgb;
-
- result.radiance = out_Diffuse_0.radiance;
-
- /* TODO(@fclem): Try to not use this. */
- closure_load_ssr_data(vec3(0.0), 0.0, in_Diffuse_0.N, -1.0, result);
+ result = closure_eval(diffuse_data);
}
-
-#else
-/* Stub diffuse because it is not compatible with volumetrics. */
-# define node_bsdf_diffuse(a, b, c, d) (d = CLOSURE_DEFAULT)
-#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_displacement.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_displacement.glsl
index 0838b5c8b71..cdcdbe50917 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_displacement.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_displacement.glsl
@@ -1,9 +1,9 @@
-void node_displacement_object(
- float height, float midlevel, float scale, vec3 N, mat4 obmat, out vec3 result)
+void node_displacement_object(float height, float midlevel, float scale, vec3 N, out vec3 result)
{
- N = (vec4(N, 0.0) * obmat).xyz;
+ N = transform_direction(ModelMatrix, N);
result = (height - midlevel) * scale * normalize(N);
- result = (obmat * vec4(result, 0.0)).xyz;
+ /* Apply object scale and orientation. */
+ result = transform_direction(ModelMatrix, result);
}
void node_displacement_world(float height, float midlevel, float scale, vec3 N, out vec3 result)
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl
index 0941482df45..c81880184e3 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_eevee_specular.glsl
@@ -1,80 +1,69 @@
-#ifndef VOLUMETRICS
-
-CLOSURE_EVAL_FUNCTION_DECLARE_3(node_eevee_specular, Diffuse, Glossy, Glossy)
void node_eevee_specular(vec4 diffuse,
vec4 specular,
float roughness,
vec4 emissive,
float transp,
- vec3 normal,
+ vec3 N,
float clearcoat,
float clearcoat_roughness,
- vec3 clearcoat_normal,
+ vec3 CN,
float occlusion,
- float ssr_id,
+ float weight,
+ const float use_clearcoat,
out Closure result)
{
- CLOSURE_VARS_DECLARE_3(Diffuse, Glossy, Glossy);
-
- in_common.occlusion = occlusion;
-
- in_Diffuse_0.N = normal; /* Normalized during eval. */
- in_Diffuse_0.albedo = diffuse.rgb;
+ N = safe_normalize(N);
+ CN = safe_normalize(CN);
+ vec3 V = cameraVec(g_data.P);
- in_Glossy_1.N = normal; /* Normalized during eval. */
- in_Glossy_1.roughness = roughness;
+ ClosureEmission emission_data;
+ emission_data.weight = weight;
+ emission_data.emission = emissive.rgb;
- in_Glossy_2.N = clearcoat_normal; /* Normalized during eval. */
- in_Glossy_2.roughness = clearcoat_roughness;
+ ClosureTransparency transparency_data;
+ transparency_data.weight = weight;
+ transparency_data.transmittance = vec3(transp);
+ transparency_data.holdout = 0.0;
- CLOSURE_EVAL_FUNCTION_3(node_eevee_specular, Diffuse, Glossy, Glossy);
+ float alpha = (1.0 - transp) * weight;
- result = CLOSURE_DEFAULT;
+ ClosureDiffuse diffuse_data;
+ diffuse_data.weight = alpha;
+ diffuse_data.color = diffuse.rgb;
+ diffuse_data.N = N;
+ diffuse_data.sss_id = 0u;
- vec3 V = cameraVec(worldPosition);
-
- {
- /* Diffuse. */
- out_Diffuse_0.radiance = render_pass_diffuse_mask(vec3(1), out_Diffuse_0.radiance);
- out_Diffuse_0.radiance *= in_Diffuse_0.albedo;
- result.radiance += out_Diffuse_0.radiance;
- }
- {
- /* Glossy. */
- float NV = dot(in_Glossy_1.N, V);
- vec2 split_sum = brdf_lut(NV, in_Glossy_1.roughness);
+ ClosureReflection reflection_data;
+ reflection_data.weight = alpha;
+ if (true) {
+ float NV = dot(N, V);
+ vec2 split_sum = brdf_lut(NV, roughness);
vec3 brdf = F_brdf_single_scatter(specular.rgb, vec3(1.0), split_sum);
- out_Glossy_1.radiance = closure_mask_ssr_radiance(out_Glossy_1.radiance, ssr_id);
- out_Glossy_1.radiance *= brdf;
- out_Glossy_1.radiance = render_pass_glossy_mask(specular.rgb, out_Glossy_1.radiance);
- closure_load_ssr_data(
- out_Glossy_1.radiance, in_Glossy_1.roughness, in_Glossy_1.N, ssr_id, result);
+ reflection_data.color = specular.rgb * brdf;
+ reflection_data.N = N;
+ reflection_data.roughness = roughness;
}
- {
- /* Clearcoat. */
- float NV = dot(in_Glossy_2.N, V);
- vec2 split_sum = brdf_lut(NV, in_Glossy_2.roughness);
+
+ ClosureReflection clearcoat_data;
+ clearcoat_data.weight = alpha * clearcoat * 0.25;
+ if (true) {
+ float NV = dot(CN, V);
+ vec2 split_sum = brdf_lut(NV, clearcoat_roughness);
vec3 brdf = F_brdf_single_scatter(vec3(0.04), vec3(1.0), split_sum);
- out_Glossy_2.radiance *= brdf * clearcoat * 0.25;
- out_Glossy_2.radiance = render_pass_glossy_mask(vec3(1), out_Glossy_2.radiance);
- result.radiance += out_Glossy_2.radiance;
- }
- {
- /* Emission. */
- vec3 out_emission_radiance = render_pass_emission_mask(emissive.rgb);
- result.radiance += out_emission_radiance;
+ clearcoat_data.color = brdf;
+ clearcoat_data.N = CN;
+ clearcoat_data.roughness = clearcoat_roughness;
}
- float alpha = 1.0 - transp;
- result.transmittance = vec3(transp);
- result.radiance *= alpha;
- result.ssr_data.rgb *= alpha;
+ if (use_clearcoat != 0.0f) {
+ result = closure_eval(diffuse_data, reflection_data, clearcoat_data);
+ }
+ else {
+ result = closure_eval(diffuse_data, reflection_data);
+ }
+ result = closure_add(result, closure_eval(emission_data));
+ result = closure_add(result, closure_eval(transparency_data));
}
-
-#else
-/* Stub specular because it is not compatible with volumetrics. */
-# define node_eevee_specular(a, b, c, d, e, f, g, h, i, j, k, result) (result = CLOSURE_DEFAULT)
-#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_emission.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_emission.glsl
index f2de7c2da39..32484491abd 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_emission.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_emission.glsl
@@ -1,10 +1,9 @@
-void node_emission(vec4 color, float strength, vec3 vN, out Closure result)
+
+void node_emission(vec4 color, float strength, float weight, out Closure result)
{
- result = CLOSURE_DEFAULT;
-#ifndef VOLUMETRICS
- result.radiance = render_pass_emission_mask(color.rgb) * strength;
- result.ssr_normal = normal_encode(vN, viewCameraVec(viewPosition));
-#else
- result.emission = color.rgb * strength;
-#endif
+ ClosureEmission emission_data;
+ emission_data.weight = weight;
+ emission_data.emission = color.rgb * strength;
+
+ result = closure_eval(emission_data);
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl
index 95f2be4bd44..7f502f74c0c 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl
@@ -1,3 +1,6 @@
+#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_material_noise.glsl)
+
/* The fractal_noise functions are all exactly the same except for the input type. */
float fractal_noise(float p, float octaves, float roughness)
{
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_fresnel.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_fresnel.glsl
index 7a4d28f2dd6..9fb98d598ab 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_fresnel.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_fresnel.glsl
@@ -26,12 +26,11 @@ float fresnel_dielectric(vec3 Incoming, vec3 Normal, float eta)
return fresnel_dielectric_cos(dot(Incoming, Normal), eta);
}
-void node_fresnel(float ior, vec3 N, vec3 I, out float result)
+void node_fresnel(float ior, vec3 N, out float result)
{
N = normalize(N);
- /* handle perspective/orthographic */
- vec3 I_view = (ProjectionMatrix[3][3] == 0.0) ? normalize(I) : vec3(0.0, 0.0, -1.0);
+ vec3 V = cameraVec(g_data.P);
float eta = max(ior, 0.00001);
- result = fresnel_dielectric(I_view, N, (gl_FrontFacing) ? eta : 1.0 / eta);
+ result = fresnel_dielectric(V, N, (FrontFacing) ? eta : 1.0 / eta);
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_gamma.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_gamma.glsl
index 5733992f8dd..29fb09ceebd 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_gamma.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_gamma.glsl
@@ -1,3 +1,5 @@
+#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl)
+
void node_gamma(vec4 col, float gamma, out vec4 outcol)
{
outcol = col;
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl
index a14ff5021bf..5e86a4577ee 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl
@@ -1,9 +1,6 @@
-void node_geometry(vec3 I,
- vec3 N,
- vec3 orco,
- mat4 objmat,
- mat4 toworld,
- vec2 barycentric,
+#pragma BLENDER_REQUIRE(gpu_shader_material_tangent.glsl)
+
+void node_geometry(vec3 orco,
out vec3 position,
out vec3 normal,
out vec3 tangent,
@@ -15,39 +12,21 @@ void node_geometry(vec3 I,
out float random_per_island)
{
/* handle perspective/orthographic */
- vec3 I_view = (ProjectionMatrix[3][3] == 0.0) ? normalize(I) : vec3(0.0, 0.0, -1.0);
- incoming = -(toworld * vec4(I_view, 0.0)).xyz;
-
-#if defined(WORLD_BACKGROUND) || defined(PROBE_CAPTURE)
- position = -incoming;
- true_normal = normal = incoming;
- tangent = parametric = vec3(0.0);
- vec3(0.0);
- backfacing = 0.0;
- pointiness = 0.0;
-#else
-
- position = worldPosition;
-# ifndef VOLUMETRICS
- normal = normalize(N);
- vec3 B = dFdx(worldPosition);
- vec3 T = dFdy(worldPosition);
- true_normal = normalize(cross(B, T));
-# else
- normal = (toworld * vec4(N, 0.0)).xyz;
- true_normal = normal;
-# endif
+ incoming = coordinate_incoming(g_data.P);
+ position = g_data.P;
+ normal = g_data.N;
+ true_normal = g_data.Ng;
-# ifdef HAIR_SHADER
- tangent = -hairTangent;
-# else
- tangent_orco_z(orco, orco);
- node_tangent(N, orco, objmat, tangent);
-# endif
+ if (g_data.is_strand) {
+ tangent = g_data.T;
+ }
+ else {
+ tangent_orco_z(orco, orco);
+ node_tangent(orco, tangent);
+ }
- parametric = vec3(barycentric, 0.0);
- backfacing = (gl_FrontFacing) ? 0.0 : 1.0;
+ parametric = vec3(g_data.barycentric_coords, 0.0);
+ backfacing = (FrontFacing) ? 0.0 : 1.0;
pointiness = 0.5;
random_per_island = 0.0;
-#endif
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_glass.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_glass.glsl
index aa0a8873596..36c71c27837 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_glass.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_glass.glsl
@@ -1,57 +1,33 @@
-#ifndef VOLUMETRICS
-
-CLOSURE_EVAL_FUNCTION_DECLARE_2(node_bsdf_glass, Glossy, Refraction)
void node_bsdf_glass(vec4 color,
float roughness,
float ior,
vec3 N,
- const float do_multiscatter,
- const float ssr_id,
+ float weight,
+ float do_multiscatter,
out Closure result)
{
- CLOSURE_VARS_DECLARE_2(Glossy, Refraction);
-
- in_Glossy_0.N = N; /* Normalized during eval. */
- in_Glossy_0.roughness = roughness;
-
- in_Refraction_1.N = N; /* Normalized during eval. */
- in_Refraction_1.roughness = roughness;
- in_Refraction_1.ior = ior;
+ N = safe_normalize(N);
+ vec3 V = cameraVec(g_data.P);
+ float NV = dot(N, V);
- CLOSURE_EVAL_FUNCTION_2(node_bsdf_glass, Glossy, Refraction);
+ vec2 split_sum = btdf_lut(NV, roughness, ior);
- result = CLOSURE_DEFAULT;
+ float fresnel = (do_multiscatter != 0.0) ? split_sum.y : F_eta(ior, NV);
+ float btdf = (do_multiscatter != 0.0) ? 1.0 : split_sum.x;
- float NV = dot(in_Refraction_1.N, cameraVec(worldPosition));
+ ClosureReflection reflection_data;
+ reflection_data.weight = fresnel * weight;
+ reflection_data.color = color.rgb;
+ reflection_data.N = N;
+ reflection_data.roughness = roughness;
- float fresnel = (do_multiscatter != 0.0) ?
- btdf_lut(NV, in_Refraction_1.roughness, in_Refraction_1.ior).y :
- F_eta(in_Refraction_1.ior, NV);
+ ClosureRefraction refraction_data;
+ refraction_data.weight = (1.0 - fresnel) * weight;
+ refraction_data.color = color.rgb * btdf;
+ refraction_data.N = N;
+ refraction_data.roughness = roughness;
+ refraction_data.ior = ior;
- vec2 split_sum = brdf_lut(NV, in_Glossy_0.roughness);
- vec3 brdf = (do_multiscatter != 0.0) ? F_brdf_multi_scatter(vec3(1.0), vec3(1.0), split_sum) :
- F_brdf_single_scatter(vec3(1.0), vec3(1.0), split_sum);
-
- out_Glossy_0.radiance = closure_mask_ssr_radiance(out_Glossy_0.radiance, ssr_id);
- out_Glossy_0.radiance *= brdf;
- out_Glossy_0.radiance = render_pass_glossy_mask(vec3(1.0), out_Glossy_0.radiance);
- out_Glossy_0.radiance *= color.rgb * fresnel;
- closure_load_ssr_data(
- out_Glossy_0.radiance, in_Glossy_0.roughness, in_Glossy_0.N, ssr_id, result);
-
- float btdf = (do_multiscatter != 0.0) ?
- 1.0 :
- btdf_lut(NV, in_Refraction_1.roughness, in_Refraction_1.ior).x;
- out_Refraction_1.radiance *= btdf;
- out_Refraction_1.radiance = render_pass_glossy_mask(vec3(1.0), out_Refraction_1.radiance);
- out_Refraction_1.radiance *= color.rgb * (1.0 - fresnel);
- /* Simulate 2nd absorption event. */
- out_Refraction_1.radiance *= (refractionDepth > 0.0) ? color.rgb : vec3(1.0);
- result.radiance += out_Refraction_1.radiance;
+ result = closure_eval(reflection_data, refraction_data);
}
-
-#else
-/* Stub glass because it is not compatible with volumetrics. */
-# define node_bsdf_glass(a, b, c, d, e, f, result) (result = CLOSURE_DEFAULT)
-#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_glossy.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_glossy.glsl
index fa83bfb6c7a..2e48ddd1c5e 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_glossy.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_glossy.glsl
@@ -1,33 +1,20 @@
-#ifndef VOLUMETRICS
-
-CLOSURE_EVAL_FUNCTION_DECLARE_1(node_bsdf_glossy, Glossy)
void node_bsdf_glossy(
- vec4 color, float roughness, vec3 N, float use_multiscatter, float ssr_id, out Closure result)
+ vec4 color, float roughness, vec3 N, float weight, float do_multiscatter, out Closure result)
{
- bool do_ssr = (ssrToggle && int(ssr_id) == outputSsrId);
-
- CLOSURE_VARS_DECLARE_1(Glossy);
+ N = safe_normalize(N);
+ vec3 V = cameraVec(g_data.P);
+ float NV = dot(N, V);
- in_Glossy_0.N = N; /* Normalized during eval. */
- in_Glossy_0.roughness = roughness;
+ vec2 split_sum = brdf_lut(NV, roughness);
- CLOSURE_EVAL_FUNCTION_1(node_bsdf_glossy, Glossy);
+ ClosureReflection reflection_data;
+ reflection_data.weight = weight;
+ reflection_data.color = (do_multiscatter != 0.0) ?
+ F_brdf_multi_scatter(color.rgb, color.rgb, split_sum) :
+ F_brdf_single_scatter(color.rgb, color.rgb, split_sum);
+ reflection_data.N = N;
+ reflection_data.roughness = roughness;
- result = CLOSURE_DEFAULT;
-
- vec2 split_sum = brdf_lut(dot(in_Glossy_0.N, cameraVec(worldPosition)), in_Glossy_0.roughness);
- vec3 brdf = (use_multiscatter != 0.0) ? F_brdf_multi_scatter(vec3(1.0), vec3(1.0), split_sum) :
- F_brdf_single_scatter(vec3(1.0), vec3(1.0), split_sum);
- out_Glossy_0.radiance = closure_mask_ssr_radiance(out_Glossy_0.radiance, ssr_id);
- out_Glossy_0.radiance *= brdf;
- out_Glossy_0.radiance = render_pass_glossy_mask(vec3(1.0), out_Glossy_0.radiance);
- out_Glossy_0.radiance *= color.rgb;
- closure_load_ssr_data(
- out_Glossy_0.radiance, in_Glossy_0.roughness, in_Glossy_0.N, ssr_id, result);
+ result = closure_eval(reflection_data);
}
-
-#else
-/* Stub glossy because it is not compatible with volumetrics. */
-# define node_bsdf_glossy(a, b, c, d, e, result) (result = CLOSURE_DEFAULT)
-#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hair.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hair.glsl
new file mode 100644
index 00000000000..7bf8795495a
--- /dev/null
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_hair.glsl
@@ -0,0 +1,46 @@
+
+void node_bsdf_hair(vec4 color,
+ float offset,
+ float roughness_u,
+ float roughness_v,
+ vec3 T,
+ float weight,
+ out Closure result)
+{
+ ClosureHair hair_data;
+ hair_data.weight = weight;
+ hair_data.color = color.rgb;
+ hair_data.offset = offset;
+ hair_data.roughness = vec2(roughness_u, roughness_v);
+ hair_data.T = T;
+
+ result = closure_eval(hair_data);
+}
+
+void node_bsdf_hair_principled(vec4 color,
+ float melanin,
+ float melanin_redness,
+ vec4 tint,
+ vec3 absorption_coefficient,
+ float roughness,
+ float radial_roughness,
+ float coat,
+ float ior,
+ float offset,
+ float random_color,
+ float random_roughness,
+ float random,
+ float weight,
+ out Closure result)
+{
+ /* Placeholder closure.
+ * Some computation will have to happen here just like the Principled BSDF. */
+ ClosureHair hair_data;
+ hair_data.weight = weight;
+ hair_data.color = color.rgb;
+ hair_data.offset = offset;
+ hair_data.roughness = vec2(0.0);
+ hair_data.T = g_data.T;
+
+ result = closure_eval(hair_data);
+}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl
index 59f0377869b..2885bf4e082 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl
@@ -1,25 +1,19 @@
+#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl)
+
void node_hair_info(float hair_length,
out float is_strand,
out float intercept,
- out float length,
+ out float out_length,
out float thickness,
out vec3 tangent,
out float random)
{
- length = hair_length;
-#ifdef HAIR_SHADER
- is_strand = 1.0;
- intercept = hairTime;
- thickness = hairThickness;
- tangent = normalize(worldNormal);
- random = wang_hash_noise(
- uint(hairStrandID)); /* TODO: could be precomputed per strand instead. */
-#else
- is_strand = 0.0;
- intercept = 0.0;
- thickness = 0.0;
- tangent = vec3(1.0);
- random = 0.0;
-#endif
+ is_strand = float(g_data.is_strand);
+ intercept = g_data.hair_time;
+ thickness = g_data.hair_thickness;
+ out_length = hair_length;
+ tangent = g_data.T;
+ /* TODO: could be precomputed per strand instead. */
+ random = wang_hash_noise(uint(g_data.hair_strand_id));
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl
index cb798047791..32d61f06a65 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_hash.glsl
@@ -209,10 +209,12 @@ vec3 hash_vec4_to_vec3(vec4 k)
float integer_noise(int n)
{
- int nn;
- n = (n + 1013) & 0x7fffffff;
- n = (n >> 13) ^ n;
- nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff;
+ /* Integer bit-shifts for these calculations can cause precision problems on macOS.
+ * Using uint resolves these issues. */
+ uint nn;
+ nn = (uint(n) + 1013u) & 0x7fffffffu;
+ nn = (nn >> 13u) ^ nn;
+ nn = (uint(nn * (nn * nn * 60493u + 19990303u)) + 1376312589u) & 0x7fffffffu;
return 0.5 * (float(nn) / 1073741824.0);
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_holdout.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_holdout.glsl
index 50ce2bf2ab8..d022c1ced59 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_holdout.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_holdout.glsl
@@ -1,8 +1,10 @@
-void node_holdout(out Closure result)
+
+void node_holdout(float weight, out Closure result)
{
- result = CLOSURE_DEFAULT;
-#ifndef VOLUMETRICS
- result.holdout = 1.0;
- result.flag = CLOSURE_HOLDOUT_FLAG;
-#endif
+ ClosureTransparency transparency_data;
+ transparency_data.weight = weight;
+ transparency_data.transmittance = vec3(0.0);
+ transparency_data.holdout = 1.0;
+
+ result = closure_eval(transparency_data);
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hue_sat_val.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hue_sat_val.glsl
index 64ac73ecdf3..30b808508e9 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_hue_sat_val.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_hue_sat_val.glsl
@@ -1,3 +1,5 @@
+#pragma BLENDER_REQUIRE(gpu_shader_material_color_util.glsl)
+
void hue_sat(float hue, float sat, float value, float fac, vec4 col, out vec4 outcol)
{
vec4 hsv;
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_layer_weight.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_layer_weight.glsl
index 588d295bcc4..2b61343a200 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_layer_weight.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_layer_weight.glsl
@@ -1,15 +1,17 @@
-void node_layer_weight(float blend, vec3 N, vec3 I, out float fresnel, out float facing)
+#pragma BLENDER_REQUIRE(gpu_shader_material_fresnel.glsl)
+
+void node_layer_weight(float blend, vec3 N, out float fresnel, out float facing)
{
N = normalize(N);
/* fresnel */
float eta = max(1.0 - blend, 0.00001);
- vec3 I_view = (ProjectionMatrix[3][3] == 0.0) ? normalize(I) : vec3(0.0, 0.0, -1.0);
+ vec3 V = cameraVec(g_data.P);
- fresnel = fresnel_dielectric(I_view, N, (gl_FrontFacing) ? 1.0 / eta : eta);
+ fresnel = fresnel_dielectric(V, N, (FrontFacing) ? 1.0 / eta : eta);
/* facing */
- facing = abs(dot(I_view, N));
+ facing = abs(dot(V, N));
if (blend != 0.5) {
blend = clamp(blend, 0.0, 0.99999);
blend = (blend < 0.5) ? 2.0 * blend : 0.5 / (1.0 - blend);
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_light_path.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_light_path.glsl
index 50c87e3f105..628a3d5e0e5 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_light_path.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_light_path.glsl
@@ -13,19 +13,19 @@ void node_light_path(out float is_camera_ray,
out float transmission_depth)
{
/* Supported. */
- is_camera_ray = (rayType == EEVEE_RAY_CAMERA) ? 1.0 : 0.0;
- is_shadow_ray = (rayType == EEVEE_RAY_SHADOW) ? 1.0 : 0.0;
- is_diffuse_ray = (rayType == EEVEE_RAY_DIFFUSE) ? 1.0 : 0.0;
- is_glossy_ray = (rayType == EEVEE_RAY_GLOSSY) ? 1.0 : 0.0;
+ is_camera_ray = float(g_data.ray_type == RAY_TYPE_CAMERA);
+ is_shadow_ray = float(g_data.ray_type == RAY_TYPE_SHADOW);
+ is_diffuse_ray = float(g_data.ray_type == RAY_TYPE_DIFFUSE);
+ is_glossy_ray = float(g_data.ray_type == RAY_TYPE_GLOSSY);
/* Kind of supported. */
is_singular_ray = is_glossy_ray;
is_reflection_ray = is_glossy_ray;
is_transmission_ray = is_glossy_ray;
- ray_depth = rayDepth;
- diffuse_depth = (is_diffuse_ray == 1.0) ? rayDepth : 0.0;
- glossy_depth = (is_glossy_ray == 1.0) ? rayDepth : 0.0;
+ ray_depth = g_data.ray_depth;
+ diffuse_depth = (is_diffuse_ray == 1.0) ? g_data.ray_depth : 0.0;
+ glossy_depth = (is_glossy_ray == 1.0) ? g_data.ray_depth : 0.0;
transmission_depth = (is_transmission_ray == 1.0) ? glossy_depth : 0.0;
+ ray_length = g_data.ray_length;
/* Not supported. */
- ray_length = 1.0;
transparent_depth = 0.0;
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl
index 1def3abec26..119ee3c0eaa 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_map_range.glsl
@@ -1,3 +1,5 @@
+#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl)
+
float smootherstep(float edge0, float edge1, float x)
{
x = clamp(safe_divide((x - edge0), (edge1 - edge0)), 0.0, 1.0);
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_mapping.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_mapping.glsl
index 07f152439fe..312c57231c5 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_mapping.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_mapping.glsl
@@ -1,3 +1,5 @@
+#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl)
+
void mapping_mat4(
vec3 vec, vec4 m0, vec4 m1, vec4 m2, vec4 m3, vec3 minvec, vec3 maxvec, out vec3 outvec)
{
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_math.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_math.glsl
index f200d666e28..0948ce2c9fa 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_math.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_math.glsl
@@ -1,3 +1,5 @@
+#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl)
+
void math_add(float a, float b, float c, out float result)
{
result = a + b;
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl
index 2a98d9fadd0..91a8996939a 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_math_util.glsl
@@ -140,17 +140,74 @@ mat3 euler_to_mat3(vec3 euler)
return mat;
}
-void direction_transform_m4v3(vec3 vin, mat4 mat, out vec3 vout)
+void normal_transform_object_to_world(vec3 vin, out vec3 vout)
{
- vout = (mat * vec4(vin, 0.0)).xyz;
+ vout = normal_object_to_world(vin);
}
-void normal_transform_transposed_m4v3(vec3 vin, mat4 mat, out vec3 vout)
+void normal_transform_world_to_object(vec3 vin, out vec3 vout)
{
- vout = transpose(mat3(mat)) * vin;
+ vout = normal_world_to_object(vin);
}
-void point_transform_m4v3(vec3 vin, mat4 mat, out vec3 vout)
+void direction_transform_object_to_world(vec3 vin, out vec3 vout)
{
- vout = (mat * vec4(vin, 1.0)).xyz;
+ vout = transform_direction(ModelMatrix, vin);
+}
+
+void direction_transform_object_to_view(vec3 vin, out vec3 vout)
+{
+ vout = transform_direction(ModelMatrix, vin);
+ vout = transform_direction(ViewMatrix, vout);
+}
+
+void direction_transform_view_to_world(vec3 vin, out vec3 vout)
+{
+ vout = transform_direction(ViewMatrixInverse, vin);
+}
+
+void direction_transform_view_to_object(vec3 vin, out vec3 vout)
+{
+ vout = transform_direction(ViewMatrixInverse, vin);
+ vout = transform_direction(ModelMatrixInverse, vout);
+}
+
+void direction_transform_world_to_view(vec3 vin, out vec3 vout)
+{
+ vout = transform_direction(ViewMatrix, vin);
+}
+
+void direction_transform_world_to_object(vec3 vin, out vec3 vout)
+{
+ vout = transform_direction(ModelMatrixInverse, vin);
+}
+
+void point_transform_object_to_world(vec3 vin, out vec3 vout)
+{
+ vout = point_object_to_world(vin);
+}
+
+void point_transform_object_to_view(vec3 vin, out vec3 vout)
+{
+ vout = point_object_to_view(vin);
+}
+
+void point_transform_view_to_world(vec3 vin, out vec3 vout)
+{
+ vout = point_view_to_world(vin);
+}
+
+void point_transform_view_to_object(vec3 vin, out vec3 vout)
+{
+ vout = point_view_to_object(vin);
+}
+
+void point_transform_world_to_view(vec3 vin, out vec3 vout)
+{
+ vout = point_world_to_view(vin);
+}
+
+void point_transform_world_to_object(vec3 vin, out vec3 vout)
+{
+ vout = point_world_to_object(vin);
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_mix_rgb.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_mix_rgb.glsl
index e089aec1d92..157a6a27c15 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_mix_rgb.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_mix_rgb.glsl
@@ -1,3 +1,5 @@
+#pragma BLENDER_REQUIRE(gpu_shader_material_color_util.glsl)
+
void mix_blend(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
fac = clamp(fac, 0.0, 1.0);
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl
index cc65b1eb57c..c84f34a834c 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl
@@ -1,3 +1,5 @@
+#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl)
+
/* clang-format off */
#define FLOORFRAC(x, x_int, x_fract) { float x_floor = floor(x); x_int = int(x_floor); x_fract = x - x_floor; }
/* clang-format on */
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_normal_map.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_normal_map.glsl
index 2b4a0204d97..e219e2b9bbe 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_normal_map.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_normal_map.glsl
@@ -1,13 +1,13 @@
-void node_normal_map(vec4 info, vec4 tangent, vec3 normal, vec3 texnormal, out vec3 outnormal)
+void node_normal_map(vec4 tangent, vec3 texnormal, out vec3 outnormal)
{
if (all(equal(tangent, vec4(0.0, 0.0, 0.0, 1.0)))) {
- outnormal = normal;
+ outnormal = g_data.N;
return;
}
- tangent *= (gl_FrontFacing ? 1.0 : -1.0);
- vec3 B = tangent.w * cross(normal, tangent.xyz) * sign(info.w);
+ tangent *= (FrontFacing ? 1.0 : -1.0);
+ vec3 B = tangent.w * cross(g_data.N, tangent.xyz) * sign(ObjectInfo.w);
- outnormal = texnormal.x * tangent.xyz + texnormal.y * B + texnormal.z * normal;
+ outnormal = texnormal.x * tangent.xyz + texnormal.y * B + texnormal.z * g_data.N;
outnormal = normalize(outnormal);
}
@@ -21,7 +21,7 @@ void color_to_blender_normal_new_shading(vec3 color, out vec3 normal)
normal = vec3(2.0, -2.0, -2.0) * color - vec3(1.0);
}
-void node_normal_map_mix(float strength, vec3 newnormal, vec3 oldnormal, out vec3 outnormal)
+void node_normal_map_mix(float strength, vec3 newnormal, out vec3 outnormal)
{
- outnormal = normalize(mix(oldnormal, newnormal, max(strength, 0.0)));
+ outnormal = normalize(mix(g_data.N, newnormal, max(strength, 0.0)));
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_object_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_object_info.glsl
index 607cf119b36..2dd2993ceb0 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_object_info.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_object_info.glsl
@@ -1,7 +1,4 @@
-void node_object_info(mat4 obmat,
- vec4 obcolor,
- vec4 info,
- float mat_index,
+void node_object_info(float mat_index,
out vec3 location,
out vec4 color,
out float alpha,
@@ -9,10 +6,11 @@ void node_object_info(mat4 obmat,
out float material_index,
out float random)
{
- location = obmat[3].xyz;
- color = obcolor;
- alpha = obcolor.w;
- object_index = info.x;
+ location = ModelMatrix[3].xyz;
+ color = ObjectColor;
+ alpha = ObjectColor.a;
+ object_index = ObjectInfo.x;
+ /* TODO(fclem): Put that inside the Material UBO. */
material_index = mat_index;
- random = info.z;
+ random = ObjectInfo.z;
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_output_aov.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_output_aov.glsl
index 648994739bf..b166c3e4e9f 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_output_aov.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_output_aov.glsl
@@ -1,13 +1,5 @@
-void node_output_aov(vec4 color, float value, out Closure result)
+void node_output_aov(vec4 color, float value, float hash, out Closure dummy)
{
- result = CLOSURE_DEFAULT;
-#ifndef VOLUMETRICS
- if (render_pass_aov_is_color()) {
- result.radiance = color.rgb;
- }
- else {
- result.radiance = vec3(value);
- }
-#endif
+ output_aov(color, value, floatBitsToUint(hash));
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_output_material.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_output_material.glsl
index 14271f9d107..2c24f50264c 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_output_material.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_output_material.glsl
@@ -1,20 +1,20 @@
-void node_output_material(Closure surface,
- Closure volume,
- vec3 displacement,
- float alpha_threshold,
- float shadow_threshold,
- out Closure result)
+
+void node_output_material_surface(Closure surface, out Closure out_surface)
{
-#ifdef VOLUMETRICS
- result = volume;
-#else
- result = surface;
-# if defined(USE_ALPHA_HASH)
- /* Alpha clip emulation. */
- if ((rayType != EEVEE_RAY_SHADOW) ? (alpha_threshold >= 0.0) : (shadow_threshold >= 0.0)) {
- float alpha = saturate(1.0 - avg(result.transmittance));
- result.transmittance = vec3(step(alpha, max(alpha_threshold, shadow_threshold)));
- }
-# endif
-#endif
+ out_surface = surface;
+}
+
+void node_output_material_volume(Closure volume, out Closure out_volume)
+{
+ out_volume = volume;
+}
+
+void node_output_material_displacement(vec3 displacement, out vec3 out_displacement)
+{
+ out_displacement = displacement;
+}
+
+void node_output_material_thickness(float thickness, out float out_thickness)
+{
+ out_thickness = thickness;
}
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 5eb853a4c1a..37c34a4f0d7 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
@@ -1,14 +1,10 @@
-uniform float backgroundAlpha;
-void node_output_world(Closure surface, Closure volume, out Closure result)
+void node_output_world_surface(Closure surface, out Closure out_surface)
{
-#ifndef VOLUMETRICS
- float alpha = renderPassEnvironment ? 1.0 : backgroundAlpha;
- result = CLOSURE_DEFAULT;
- result.radiance = surface.radiance * alpha;
- result.transmittance = vec3(0.0);
- result.holdout = (1.0 - alpha);
-#else
- result = volume;
-#endif /* VOLUMETRICS */
+ out_surface = surface;
+}
+
+void node_output_world_volume(Closure volume, out Closure out_volume)
+{
+ out_volume = volume;
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_particle_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_particle_info.glsl
index bdd60c20a81..5602345ea4a 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_particle_info.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_particle_info.glsl
@@ -1,8 +1,4 @@
-void particle_info(vec4 sprops,
- vec4 loc,
- vec3 vel,
- vec3 avel,
- out float index,
+void particle_info(out float index,
out float random,
out float age,
out float life_time,
@@ -11,13 +7,14 @@ void particle_info(vec4 sprops,
out vec3 velocity,
out vec3 angular_velocity)
{
- index = sprops.x;
- random = loc.w;
- age = sprops.y;
- life_time = sprops.z;
- size = sprops.w;
+ /* Unsupported for now. */
+ index = 0.0;
+ random = 0.0;
+ age = 0.0;
+ life_time = 0.0;
+ size = 0.0;
- location = loc.xyz;
- velocity = vel;
- angular_velocity = avel;
+ location = vec3(0.0);
+ velocity = vec3(0.0);
+ angular_velocity = vec3(0.0);
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_point_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_point_info.glsl
index d717ac97b28..1b1fed9502e 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_point_info.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_point_info.glsl
@@ -1,3 +1,5 @@
+#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl)
+
void node_point_info(out vec3 position, out float radius, out float random)
{
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl
index c97fc090fe2..033dc05c57d 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl
@@ -1,4 +1,4 @@
-#ifndef VOLUMETRICS
+
vec3 tint_from_color(vec3 color)
{
float lum = dot(color, vec3(0.3, 0.6, 0.1)); /* luminance approx. */
@@ -13,8 +13,6 @@ float principled_sheen(float NV)
return sheen;
}
-CLOSURE_EVAL_FUNCTION_DECLARE_4(node_bsdf_principled, Diffuse, Glossy, Glossy, Refraction)
-
void node_bsdf_principled(vec4 base_color,
float subsurface,
vec3 subsurface_radius,
@@ -40,169 +38,137 @@ void node_bsdf_principled(vec4 base_color,
vec3 N,
vec3 CN,
vec3 T,
+ float weight,
const float do_diffuse,
const float do_clearcoat,
const float do_refraction,
const float do_multiscatter,
- float ssr_id,
- float sss_id,
- vec3 sss_scale,
+ float do_sss,
out Closure result)
{
/* Match cycles. */
- metallic = saturate(metallic);
- transmission = saturate(transmission);
+ metallic = clamp(metallic, 0.0, 1.0);
+ transmission = clamp(transmission, 0.0, 1.0) * (1.0 - metallic);
float diffuse_weight = (1.0 - transmission) * (1.0 - metallic);
- transmission *= (1.0 - metallic);
float specular_weight = (1.0 - transmission);
- clearcoat = max(clearcoat, 0.0);
+ float clearcoat_weight = max(clearcoat, 0.0) * 0.25;
transmission_roughness = 1.0 - (1.0 - roughness) * (1.0 - transmission_roughness);
specular = max(0.0, specular);
- CLOSURE_VARS_DECLARE_4(Diffuse, Glossy, Glossy, Refraction);
-
- in_Diffuse_0.N = N; /* Normalized during eval. */
- in_Diffuse_0.albedo = mix(base_color.rgb, subsurface_color.rgb, subsurface);
-
- in_Glossy_1.N = N; /* Normalized during eval. */
- in_Glossy_1.roughness = roughness;
-
- in_Glossy_2.N = CN; /* Normalized during eval. */
- in_Glossy_2.roughness = clearcoat_roughness;
-
- in_Refraction_3.N = N; /* Normalized during eval. */
- in_Refraction_3.roughness = do_multiscatter != 0.0 ? roughness : transmission_roughness;
- in_Refraction_3.ior = ior;
+ N = safe_normalize(N);
+ CN = safe_normalize(CN);
+ vec3 V = cameraVec(g_data.P);
+ float NV = dot(N, V);
- CLOSURE_EVAL_FUNCTION_4(node_bsdf_principled, Diffuse, Glossy, Glossy, Refraction);
+ float fresnel = (do_multiscatter != 0.0) ? btdf_lut(NV, roughness, ior).y : F_eta(ior, NV);
+ float glass_reflection_weight = fresnel * transmission;
+ float glass_transmission_weight = (1.0 - fresnel) * transmission;
- result = CLOSURE_DEFAULT;
+ vec3 base_color_tint = tint_from_color(base_color.rgb);
- /* This will tag the whole eval for optimisation. */
- if (do_diffuse == 0.0) {
- out_Diffuse_0.radiance = vec3(0);
- }
- if (do_clearcoat == 0.0) {
- out_Glossy_2.radiance = vec3(0);
- }
- if (do_refraction == 0.0) {
- out_Refraction_3.radiance = vec3(0);
+ vec2 split_sum = brdf_lut(NV, roughness);
+
+ ClosureTransparency transparency_data;
+ transparency_data.weight = weight;
+ transparency_data.transmittance = vec3(1.0 - alpha);
+ transparency_data.holdout = 0.0;
+
+ weight *= alpha;
+
+ ClosureEmission emission_data;
+ emission_data.weight = weight;
+ emission_data.emission = emission.rgb * emission_strength;
+
+ /* Diffuse. */
+ ClosureDiffuse diffuse_data;
+ diffuse_data.weight = diffuse_weight * weight;
+ diffuse_data.color = mix(base_color.rgb, subsurface_color.rgb, subsurface);
+ /* Sheen Coarse approximation: We reuse the diffuse radiance and just scale it. */
+ vec3 sheen_color = mix(vec3(1.0), base_color_tint, sheen_tint);
+ diffuse_data.color += sheen * sheen_color * principled_sheen(NV);
+ diffuse_data.N = N;
+ diffuse_data.sss_radius = subsurface_radius * subsurface;
+ diffuse_data.sss_id = uint(do_sss);
+
+ /* NOTE(@fclem): We need to blend the reflection color but also need to avoid applying the
+ * weights so we compule the ratio. */
+ float reflection_weight = specular_weight + glass_reflection_weight;
+ float reflection_weight_inv = safe_rcp(reflection_weight);
+ specular_weight *= reflection_weight_inv;
+ glass_reflection_weight *= reflection_weight_inv;
+
+ /* Reflection. */
+ ClosureReflection reflection_data;
+ reflection_data.weight = reflection_weight * weight;
+ reflection_data.N = N;
+ reflection_data.roughness = roughness;
+ if (true) {
+ vec3 dielectric_f0_color = mix(vec3(1.0), base_color_tint, specular_tint);
+ vec3 metallic_f0_color = base_color.rgb;
+ vec3 f0 = mix((0.08 * specular) * dielectric_f0_color, metallic_f0_color, metallic);
+ /* Cycles does this blending using the microfacet fresnel factor. However, our fresnel
+ * is already baked inside the split sum LUT. We approximate by changing the f90 color
+ * directly in a non linear fashion. */
+ vec3 f90 = mix(f0, vec3(1.0), fast_sqrt(specular));
+
+ vec3 reflection_brdf = (do_multiscatter != 0.0) ? F_brdf_multi_scatter(f0, f90, split_sum) :
+ F_brdf_single_scatter(f0, f90, split_sum);
+ reflection_data.color = reflection_brdf * specular_weight;
}
+ if (true) {
+ /* Poor approximation since we baked the LUT using a fixed IOR. */
+ vec3 f0 = mix(vec3(1.0), base_color.rgb, specular_tint);
+ vec3 f90 = vec3(1.0);
- vec3 V = cameraVec(worldPosition);
+ vec3 glass_brdf = (do_multiscatter != 0.0) ? F_brdf_multi_scatter(f0, f90, split_sum) :
+ F_brdf_single_scatter(f0, f90, split_sum);
- /* Glossy_1 will always be evaluated. */
- float NV = dot(in_Glossy_1.N, V);
-
- vec3 base_color_tint = tint_from_color(base_color.rgb);
+ /* Avoid 3 glossy evaluation. Use the same closure for glass reflection. */
+ reflection_data.color += glass_brdf * glass_reflection_weight;
+ }
- float fresnel = (do_multiscatter != 0.0) ?
- btdf_lut(NV, in_Glossy_1.roughness, in_Refraction_3.ior).y :
- F_eta(in_Refraction_3.ior, NV);
-
- {
- /* Glossy reflections.
- * Separate Glass reflections and main specular reflections to match Cycles renderpasses. */
- out_Glossy_1.radiance = closure_mask_ssr_radiance(out_Glossy_1.radiance, ssr_id);
-
- vec2 split_sum = brdf_lut(NV, roughness);
-
- vec3 glossy_radiance_final = vec3(0.0);
- if (transmission > 1e-5) {
- /* Glass Reflection: Reuse radiance from Glossy1. */
- vec3 out_glass_refl_radiance = out_Glossy_1.radiance;
-
- /* Poor approximation since we baked the LUT using a fixed IOR. */
- vec3 f0 = mix(vec3(1.0), base_color.rgb, specular_tint);
- vec3 f90 = vec3(1);
-
- vec3 brdf = (do_multiscatter != 0.0) ? F_brdf_multi_scatter(f0, f90, split_sum) :
- F_brdf_single_scatter(f0, f90, split_sum);
-
- out_glass_refl_radiance *= brdf;
- out_glass_refl_radiance = render_pass_glossy_mask(vec3(1), out_glass_refl_radiance);
- out_glass_refl_radiance *= fresnel * transmission;
- glossy_radiance_final += out_glass_refl_radiance;
- }
- if (specular_weight > 1e-5) {
- vec3 dielectric_f0_color = mix(vec3(1.0), base_color_tint, specular_tint);
- vec3 metallic_f0_color = base_color.rgb;
- vec3 f0 = mix((0.08 * specular) * dielectric_f0_color, metallic_f0_color, metallic);
- /* Cycles does this blending using the microfacet fresnel factor. However, our fresnel
- * is already baked inside the split sum LUT. We approximate using by modifying the
- * changing the f90 color directly in a non linear fashion. */
- vec3 f90 = mix(f0, vec3(1), fast_sqrt(specular));
-
- vec3 brdf = (do_multiscatter != 0.0) ? F_brdf_multi_scatter(f0, f90, split_sum) :
- F_brdf_single_scatter(f0, f90, split_sum);
-
- out_Glossy_1.radiance *= brdf;
- out_Glossy_1.radiance = render_pass_glossy_mask(vec3(1), out_Glossy_1.radiance);
- out_Glossy_1.radiance *= specular_weight;
- glossy_radiance_final += out_Glossy_1.radiance;
- }
-
- closure_load_ssr_data(
- glossy_radiance_final, in_Glossy_1.roughness, in_Glossy_1.N, ssr_id, result);
+ ClosureReflection clearcoat_data;
+ clearcoat_data.weight = clearcoat_weight * weight;
+ clearcoat_data.N = CN;
+ clearcoat_data.roughness = clearcoat_roughness;
+ if (true) {
+ float NV = dot(clearcoat_data.N, V);
+ vec2 split_sum = brdf_lut(NV, clearcoat_data.roughness);
+ vec3 brdf = F_brdf_single_scatter(vec3(0.04), vec3(1.0), split_sum);
+ clearcoat_data.color = brdf;
}
- if (diffuse_weight > 1e-5) {
- /* Mask over all diffuse radiance. */
- out_Diffuse_0.radiance *= diffuse_weight;
+ /* Refraction. */
+ ClosureRefraction refraction_data;
+ refraction_data.weight = glass_transmission_weight * weight;
+ float btdf = (do_multiscatter != 0.0) ? 1.0 : btdf_lut(NV, roughness, ior).x;
- /* Sheen Coarse approximation: We reuse the diffuse radiance and just scale it. */
- vec3 sheen_color = mix(vec3(1), base_color_tint, sheen_tint);
- vec3 out_sheen_radiance = out_Diffuse_0.radiance * principled_sheen(NV);
- out_sheen_radiance = render_pass_diffuse_mask(vec3(1), out_sheen_radiance);
- out_sheen_radiance *= sheen * sheen_color;
- result.radiance += out_sheen_radiance;
+ refraction_data.color = base_color.rgb * btdf;
+ refraction_data.N = N;
+ refraction_data.roughness = do_multiscatter != 0.0 ? roughness :
+ max(roughness, transmission_roughness);
+ refraction_data.ior = ior;
- /* Diffuse / Subsurface. */
- float scale = avg(sss_scale) * subsurface;
- closure_load_sss_data(scale, out_Diffuse_0.radiance, in_Diffuse_0.albedo, int(sss_id), result);
+ if (do_diffuse == 0.0 && do_refraction == 0.0 && do_clearcoat != 0.0) {
+ /* Metallic & Clearcoat case. */
+ result = closure_eval(reflection_data, clearcoat_data);
}
-
- if (transmission > 1e-5) {
- float btdf = (do_multiscatter != 0.0) ?
- 1.0 :
- btdf_lut(NV, in_Refraction_3.roughness, in_Refraction_3.ior).x;
- /* TODO(@fclem): This could be going to a transmission render pass instead. */
- out_Refraction_3.radiance *= btdf;
- out_Refraction_3.radiance = render_pass_glossy_mask(vec3(1), out_Refraction_3.radiance);
- out_Refraction_3.radiance *= base_color.rgb;
- /* Simulate 2nd transmission event. */
- out_Refraction_3.radiance *= (refractionDepth > 0.0) ? base_color.rgb : vec3(1);
- out_Refraction_3.radiance *= (1.0 - fresnel) * transmission;
- result.radiance += out_Refraction_3.radiance;
+ else if (do_diffuse == 0.0 && do_refraction == 0.0 && do_clearcoat == 0.0) {
+ /* Metallic case. */
+ result = closure_eval(reflection_data);
}
-
- if (clearcoat > 1e-5) {
- float NV = dot(in_Glossy_2.N, V);
- vec2 split_sum = brdf_lut(NV, in_Glossy_2.roughness);
- vec3 brdf = F_brdf_single_scatter(vec3(0.04), vec3(1.0), split_sum);
-
- out_Glossy_2.radiance *= brdf * clearcoat * 0.25;
- out_Glossy_2.radiance = render_pass_glossy_mask(vec3(1), out_Glossy_2.radiance);
- result.radiance += out_Glossy_2.radiance;
+ else if (do_diffuse != 0.0 && do_refraction == 0.0 && do_clearcoat == 0.0) {
+ /* Dielectric case. */
+ result = closure_eval(diffuse_data, reflection_data);
}
-
- {
- vec3 out_emission_radiance = render_pass_emission_mask(emission.rgb);
- out_emission_radiance *= emission_strength;
- result.radiance += out_emission_radiance;
+ else if (do_diffuse == 0.0 && do_refraction != 0.0 && do_clearcoat == 0.0) {
+ /* Glass case. */
+ result = closure_eval(reflection_data, refraction_data);
}
-
- result.transmittance = vec3(1.0 - alpha);
- result.radiance *= alpha;
- result.ssr_data.rgb *= alpha;
-# ifdef USE_SSS
- result.sss_albedo *= alpha;
-# endif
+ else {
+ /* Un-optimized case. */
+ result = closure_eval(diffuse_data, reflection_data, clearcoat_data, refraction_data);
+ }
+ result = closure_add(result, closure_eval(emission_data));
+ result = closure_add(result, closure_eval(transparency_data));
}
-
-#else
-/* clang-format off */
-/* Stub principled because it is not compatible with volumetrics. */
-# define node_bsdf_principled(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, aa, bb, cc, dd, ee, ff, result) (result = CLOSURE_DEFAULT)
-/* clang-format on */
-#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl
index 8a42a131f43..871fa00b3d4 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl
@@ -1,32 +1,15 @@
-#ifndef VOLUMETRICS
-CLOSURE_EVAL_FUNCTION_DECLARE_1(node_bsdf_refraction, Refraction)
-
-void node_bsdf_refraction(vec4 color, float roughness, float ior, vec3 N, out Closure result)
+void node_bsdf_refraction(
+ vec4 color, float roughness, float ior, vec3 N, float weight, out Closure result)
{
- CLOSURE_VARS_DECLARE_1(Refraction);
-
- in_Refraction_0.N = N; /* Normalized during eval. */
- in_Refraction_0.roughness = roughness;
- in_Refraction_0.ior = ior;
-
- CLOSURE_EVAL_FUNCTION_1(node_bsdf_refraction, Refraction);
+ N = safe_normalize(N);
- result = CLOSURE_DEFAULT;
+ ClosureRefraction refraction_data;
+ refraction_data.weight = weight;
+ refraction_data.color = color.rgb;
+ refraction_data.N = N;
+ refraction_data.roughness = roughness;
+ refraction_data.ior = ior;
- out_Refraction_0.radiance = render_pass_glossy_mask(vec3(1.0), out_Refraction_0.radiance);
- out_Refraction_0.radiance *= color.rgb;
- /* Simulate 2nd absorption event. */
- out_Refraction_0.radiance *= (refractionDepth > 0.0) ? color.rgb : vec3(1.0);
-
- result.radiance = out_Refraction_0.radiance;
-
- /* TODO(@fclem): Try to not use this. */
- result.ssr_normal = normal_encode(mat3(ViewMatrix) * in_Refraction_0.N,
- viewCameraVec(viewPosition));
+ result = closure_eval(refraction_data);
}
-
-#else
-/* Stub refraction because it is not compatible with volumetrics. */
-# define node_bsdf_refraction(a, b, c, d, e) (e = CLOSURE_DEFAULT)
-#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_separate_hsv.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_separate_hsv.glsl
index fb64e424c6c..180e0fd1940 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_separate_hsv.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_separate_hsv.glsl
@@ -1,3 +1,5 @@
+#pragma BLENDER_REQUIRE(gpu_shader_material_color_util.glsl)
+
void separate_hsv(vec4 col, out float h, out float s, out float v)
{
vec4 hsv;
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_shader_to_rgba.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_shader_to_rgba.glsl
index f0f2f79c60e..d791f067821 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_shader_to_rgba.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_shader_to_rgba.glsl
@@ -1,29 +1,6 @@
-#ifndef VOLUMETRICS
-
-CLOSURE_EVAL_FUNCTION_DECLARE_1(node_shader_to_rgba, Glossy)
void node_shader_to_rgba(Closure cl, out vec4 outcol, out float outalpha)
{
- vec4 spec_accum = vec4(0.0);
- if (ssrToggle && FLAG_TEST(cl.flag, CLOSURE_SSR_FLAG)) {
- CLOSURE_VARS_DECLARE_1(Glossy);
-
- vec3 vN = normal_decode(cl.ssr_normal, viewCameraVec(viewPosition));
- vec3 N = transform_direction(ViewMatrixInverse, vN);
-
- in_Glossy_0.N = N; /* Normalized during eval. */
- in_Glossy_0.roughness = cl.ssr_data.a;
-
- CLOSURE_EVAL_FUNCTION_1(node_shader_to_rgba, Glossy);
-
- spec_accum.rgb = out_Glossy_0.radiance;
- }
-
- outalpha = saturate(1.0 - avg(cl.transmittance));
- outcol = vec4((spec_accum.rgb * cl.ssr_data.rgb) + cl.radiance, 1.0);
-
-# ifdef USE_SSS
- outcol.rgb += cl.sss_irradiance.rgb * cl.sss_albedo;
-# endif
+ outcol = closure_to_rgba(cl);
+ outalpha = outcol.a;
}
-#endif /* VOLUMETRICS */
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl
index 20b634aa801..c560dd01c4f 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl
@@ -1,6 +1,3 @@
-#ifndef VOLUMETRICS
-
-CLOSURE_EVAL_FUNCTION_DECLARE_1(node_subsurface_scattering, Diffuse)
void node_subsurface_scattering(vec4 color,
float scale,
@@ -8,25 +5,18 @@ void node_subsurface_scattering(vec4 color,
float ior,
float anisotropy,
vec3 N,
- float sss_id,
+ float weight,
+ float do_sss,
out Closure result)
{
- CLOSURE_VARS_DECLARE_1(Diffuse);
-
- in_Diffuse_0.N = N; /* Normalized during eval. */
- in_Diffuse_0.albedo = color.rgb;
-
- CLOSURE_EVAL_FUNCTION_1(node_subsurface_scattering, Diffuse);
+ N = safe_normalize(N);
- result = CLOSURE_DEFAULT;
+ ClosureDiffuse diffuse_data;
+ diffuse_data.weight = weight;
+ diffuse_data.color = color.rgb;
+ diffuse_data.N = N;
+ diffuse_data.sss_radius = radius * scale;
+ diffuse_data.sss_id = uint(do_sss);
- closure_load_sss_data(scale, out_Diffuse_0.radiance, color.rgb, int(sss_id), result);
-
- /* TODO(@fclem): Try to not use this. */
- closure_load_ssr_data(vec3(0.0), 0.0, in_Diffuse_0.N, -1.0, result);
+ result = closure_eval(diffuse_data);
}
-
-#else
-/* Stub subsurface scattering because it is not compatible with volumetrics. */
-# define node_subsurface_scattering(a, b, c, d, e, f, g, h) (h = CLOSURE_DEFAULT)
-#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tangent.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tangent.glsl
index ff2dbc7ead3..4e4bf759ec9 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_tangent.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_tangent.glsl
@@ -18,8 +18,8 @@ void node_tangentmap(vec4 attr_tangent, out vec3 tangent)
tangent = normalize(attr_tangent.xyz);
}
-void node_tangent(vec3 N, vec3 orco, mat4 objmat, out vec3 T)
+void node_tangent(vec3 orco, out vec3 T)
{
- T = (objmat * vec4(orco, 0.0)).xyz;
- T = cross(N, normalize(cross(T, N)));
+ T = transform_direction(ModelMatrix, orco);
+ T = cross(g_data.N, normalize(cross(T, g_data.N)));
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_brick.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_brick.glsl
index e252e5ba726..edc2fa32177 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_brick.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_brick.glsl
@@ -1,3 +1,6 @@
+#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl)
+
vec2 calc_brick_texture(vec3 p,
float mortar_size,
float mortar_smooth,
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl
index 434e07e7b86..89091316823 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl
@@ -1,19 +1,5 @@
-void node_tex_environment_texco(vec3 viewvec, out vec3 worldvec)
-{
-#ifdef MESH_SHADER
- worldvec = worldPosition;
-#else
- vec4 v = (ProjectionMatrix[3][3] == 0.0) ? vec4(viewvec, 1.0) : vec4(0.0, 0.0, 1.0, 1.0);
- vec4 co_homogeneous = (ProjectionMatrixInverse * v);
+#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl)
- vec3 co = co_homogeneous.xyz / co_homogeneous.w;
-# if defined(WORLD_BACKGROUND) || defined(PROBE_CAPTURE)
- worldvec = mat3(ViewMatrixInverse) * co;
-# else
- worldvec = mat3(ModelMatrixInverse) * (mat3(ViewMatrixInverse) * co);
-# endif
-#endif
-}
void node_tex_environment_equirectangular(vec3 co, out vec3 uv)
{
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl
index 586385b7e86..1552a2facc3 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl
@@ -1,3 +1,6 @@
+#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_material_noise.glsl)
+
/* 1D Musgrave fBm
*
* H: fractal increment parameter
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl
index 5745f11ede4..c90b2211dcf 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl
@@ -1,3 +1,7 @@
+#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_material_noise.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_material_fractal_noise.glsl)
+
/* The following offset functions generate random offsets to be added to texture
* coordinates to act as a seed since the noise functions don't have seed values.
* A seed value is needed for generating distortion textures and color outputs.
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl
index c8219848e29..dd12b602edf 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl
@@ -1,3 +1,6 @@
+#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl)
+
/*
* Original code is under the MIT License, Copyright (c) 2013 Inigo Quilez.
*
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl
index 070f42a5e30..eed98232d0b 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl
@@ -1,3 +1,7 @@
+#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_material_noise.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_material_fractal_noise.glsl)
+
float calc_wave(vec3 p,
float distortion,
float detail,
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl
index b11d13a0413..030b58f0736 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_white_noise.glsl
@@ -1,3 +1,5 @@
+#pragma BLENDER_REQUIRE(gpu_shader_material_hash.glsl)
+
/* White Noise */
void node_white_noise_1d(vec3 vector, float w, out float value, out vec4 color)
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_texture_coordinates.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_texture_coordinates.glsl
index a8ef9687b0a..a3666164cf7 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_texture_coordinates.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_texture_coordinates.glsl
@@ -1,38 +1,5 @@
-vec3 mtex_2d_mapping(vec3 vec)
-{
- return vec3(vec.xy * 0.5 + vec2(0.5), vec.z);
-}
-
-void generated_from_orco(vec3 orco, out vec3 generated)
-{
-#ifdef VOLUMETRICS
-# ifdef MESH_SHADER
- generated = volumeObjectLocalCoord;
-# else
- generated = worldPosition;
-# endif
-#else
- generated = orco;
-#endif
-}
-void generated_texco(vec3 I, vec3 attr_orco, out vec3 generated)
-{
- vec4 v = (ProjectionMatrix[3][3] == 0.0) ? vec4(I, 1.0) : vec4(0.0, 0.0, 1.0, 1.0);
- vec4 co_homogeneous = (ProjectionMatrixInverse * v);
- vec4 co = vec4(co_homogeneous.xyz / co_homogeneous.w, 0.0);
- co.xyz = normalize(co.xyz);
-#if defined(WORLD_BACKGROUND) || defined(PROBE_CAPTURE)
- generated = (ViewMatrixInverse * co).xyz;
-#else
- generated_from_orco(attr_orco, generated);
-#endif
-}
-
-void node_tex_coord(vec3 I,
- vec3 wN,
- mat4 obmatinv,
- vec4 camerafac,
+void node_tex_coord(mat4 obmatinv,
vec3 attr_orco,
vec3 attr_uv,
out vec3 generated,
@@ -44,49 +11,10 @@ void node_tex_coord(vec3 I,
out vec3 reflection)
{
generated = attr_orco;
- normal = normalize(normal_world_to_object(wN));
+ normal = normal_world_to_object(g_data.N);
uv = attr_uv;
- object = (obmatinv * (ViewMatrixInverse * vec4(I, 1.0))).xyz;
- camera = vec3(I.xy, -I.z);
- vec4 projvec = ProjectionMatrix * vec4(I, 1.0);
- window = vec3(mtex_2d_mapping(projvec.xyz / projvec.w).xy * camerafac.xy + camerafac.zw, 0.0);
- reflection = -reflect(cameraVec(worldPosition), normalize(wN));
-}
-
-void node_tex_coord_background(vec3 I,
- vec3 N,
- mat4 obmatinv,
- vec4 camerafac,
- vec3 attr_orco,
- vec3 attr_uv,
- out vec3 generated,
- out vec3 normal,
- out vec3 uv,
- out vec3 object,
- out vec3 camera,
- out vec3 window,
- out vec3 reflection)
-{
- vec4 v = (ProjectionMatrix[3][3] == 0.0) ? vec4(I, 1.0) : vec4(0.0, 0.0, 1.0, 1.0);
- vec4 co_homogeneous = (ProjectionMatrixInverse * v);
-
- vec4 co = vec4(co_homogeneous.xyz / co_homogeneous.w, 0.0);
-
- co = normalize(co);
-
- vec3 coords = (ViewMatrixInverse * co).xyz;
-
- generated = coords;
- normal = -coords;
- uv = vec3(attr_uv.xy, 0.0);
- object = (obmatinv * vec4(coords, 1.0)).xyz;
-
- camera = vec3(co.xy, -co.z);
- window = vec3(mtex_2d_mapping(I).xy * camerafac.xy + camerafac.zw, 0.0);
-
- reflection = -coords;
+ object = transform_point((obmatinv[3][3] == 0.0) ? ModelMatrixInverse : obmatinv, g_data.P);
+ camera = coordinate_camera(g_data.P);
+ window = coordinate_screen(g_data.P);
+ reflection = coordinate_reflect(g_data.P, g_data.N);
}
-
-#if defined(WORLD_BACKGROUND) || (defined(PROBE_CAPTURE) && !defined(MESH_SHADER))
-# define node_tex_coord node_tex_coord_background
-#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_toon.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_toon.glsl
index bbfc99ccc73..ae7d4fc5631 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_toon.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_toon.glsl
@@ -1,9 +1,15 @@
-#ifndef VOLUMETRICS
-void node_bsdf_toon(vec4 color, float size, float tsmooth, vec3 N, out Closure result)
+
+void node_bsdf_toon(
+ vec4 color, float size, float tsmooth, vec3 N, float weight, out Closure result)
{
- node_bsdf_diffuse(color, 0.0, N, result);
+ N = safe_normalize(N);
+
+ /* Fallback to diffuse. */
+ ClosureDiffuse diffuse_data;
+ diffuse_data.weight = weight;
+ diffuse_data.color = color.rgb;
+ diffuse_data.N = N;
+ diffuse_data.sss_id = 0u;
+
+ result = closure_eval(diffuse_data);
}
-#else
-/* Stub toon because it is not compatible with volumetrics. */
-# define node_bsdf_toon(a, b, c, d, e) (e = CLOSURE_DEFAULT)
-#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_translucent.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_translucent.glsl
index 80bd3941b22..0cc162f42f5 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_translucent.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_translucent.glsl
@@ -1,21 +1,12 @@
-#ifndef VOLUMETRICS
-CLOSURE_EVAL_FUNCTION_DECLARE_1(node_bsdf_translucent, Translucent)
-
-void node_bsdf_translucent(vec4 color, vec3 N, out Closure result)
+void node_bsdf_translucent(vec4 color, vec3 N, float weight, out Closure result)
{
- CLOSURE_VARS_DECLARE_1(Translucent);
-
- in_Translucent_0.N = -N; /* Normalized during eval. */
+ N = safe_normalize(N);
- CLOSURE_EVAL_FUNCTION_1(node_bsdf_translucent, Translucent);
+ ClosureTranslucent translucent_data;
+ translucent_data.weight = weight;
+ translucent_data.color = color.rgb;
+ translucent_data.N = -N;
- result = CLOSURE_DEFAULT;
- closure_load_ssr_data(vec3(0.0), 0.0, -in_Translucent_0.N, -1.0, result);
- result.radiance = render_pass_diffuse_mask(color.rgb, out_Translucent_0.radiance * color.rgb);
+ result = closure_eval(translucent_data);
}
-
-#else
-/* Stub translucent because it is not compatible with volumetrics. */
-# define node_bsdf_translucent(a, b, c) (c = CLOSURE_DEFAULT)
-#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_transparent.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_transparent.glsl
index 9040f62bd3f..c650f10b6e4 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_transparent.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_transparent.glsl
@@ -1,11 +1,10 @@
-#ifndef VOLUMETRICS
-void node_bsdf_transparent(vec4 color, out Closure result)
+
+void node_bsdf_transparent(vec4 color, float weight, out Closure result)
{
- result = CLOSURE_DEFAULT;
- result.radiance = vec3(0.0);
- result.transmittance = abs(color.rgb);
+ ClosureTransparency transparency_data;
+ transparency_data.weight = weight;
+ transparency_data.transmittance = color.rgb;
+ transparency_data.holdout = 0.0;
+
+ result = closure_eval(transparency_data);
}
-#else
-/* Stub transparent because it is not compatible with volumetrics. */
-# define node_bsdf_transparent(a, b) (b = CLOSURE_DEFAULT)
-#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_vector_displacement.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_vector_displacement.glsl
index 4b5ed172081..0ff074bc04f 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_vector_displacement.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_vector_displacement.glsl
@@ -1,30 +1,22 @@
-void node_vector_displacement_tangent(vec4 vector,
- float midlevel,
- float scale,
- vec4 tangent,
- vec3 normal,
- mat4 obmat,
- mat4 viewmat,
- out vec3 result)
+void node_vector_displacement_tangent(
+ vec4 vector, float midlevel, float scale, vec4 T, out vec3 result)
{
- /* TODO(fclem): this is broken. revisit latter. */
- vec3 N_object = normalize(((vec4(normal, 0.0) * viewmat) * obmat).xyz);
- vec3 T_object = normalize(((vec4(tangent.xyz, 0.0) * viewmat) * obmat).xyz);
- vec3 B_object = tangent.w * normalize(cross(N_object, T_object));
+ vec3 oN = normalize(normal_world_to_object(g_data.N));
+ vec3 oT = normalize(normal_world_to_object(T.xyz));
+ vec3 oB = T.w * normalize(cross(oN, oT));
- vec3 offset = (vector.xyz - vec3(midlevel)) * scale;
- result = offset.x * T_object + offset.y * N_object + offset.z * B_object;
- result = (obmat * vec4(result, 0.0)).xyz;
+ result = (vector.xyz - midlevel) * scale;
+ result = result.x * oT + result.y * oN + result.z * oB;
+ result = transform_point(ModelMatrix, result);
}
-void node_vector_displacement_object(
- vec4 vector, float midlevel, float scale, mat4 obmat, out vec3 result)
+void node_vector_displacement_object(vec4 vector, float midlevel, float scale, out vec3 result)
{
- result = (vector.xyz - vec3(midlevel)) * scale;
- result = (obmat * vec4(result, 0.0)).xyz;
+ result = (vector.xyz - midlevel) * scale;
+ result = transform_point(ModelMatrix, result);
}
void node_vector_displacement_world(vec4 vector, float midlevel, float scale, out vec3 result)
{
- result = (vector.xyz - vec3(midlevel)) * scale;
+ result = (vector.xyz - midlevel) * scale;
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl
index 4ad5d4232de..8f6bf17f195 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl
@@ -1,3 +1,5 @@
+#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl)
+
void vector_math_add(vec3 a, vec3 b, vec3 c, float scale, out vec3 outVector, out float outValue)
{
outVector = a + b;
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_vector_rotate.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_vector_rotate.glsl
index 41ad16cce0b..ff0fb1c0418 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_vector_rotate.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_vector_rotate.glsl
@@ -1,3 +1,5 @@
+#pragma BLENDER_REQUIRE(gpu_shader_material_math_util.glsl)
+
vec3 rotate_around_axis(vec3 p, vec3 axis, float angle)
{
float costheta = cos(angle);
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_velvet.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_velvet.glsl
index 989f18b881a..97726bfe66f 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_velvet.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_velvet.glsl
@@ -1,9 +1,14 @@
-#ifndef VOLUMETRICS
-void node_bsdf_velvet(vec4 color, float sigma, vec3 N, out Closure result)
+
+void node_bsdf_velvet(vec4 color, float roughness, vec3 N, float weight, out Closure result)
{
- node_bsdf_diffuse(color, 0.0, N, result);
+ N = safe_normalize(N);
+
+ /* Fallback to diffuse. */
+ ClosureDiffuse diffuse_data;
+ diffuse_data.weight = weight;
+ diffuse_data.color = color.rgb;
+ diffuse_data.N = N;
+ diffuse_data.sss_id = 0u;
+
+ result = closure_eval(diffuse_data);
}
-#else
-/* Stub velvet because it is not compatible with volumetrics. */
-# define node_bsdf_velvet(a, b, c, d) (d = CLOSURE_DEFAULT)
-#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_volume_absorption.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_volume_absorption.glsl
index e6c0880cd07..8fd2e179187 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_volume_absorption.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_volume_absorption.glsl
@@ -1,8 +1,9 @@
-void node_volume_absorption(vec4 color, float density, out Closure result)
+
+void node_volume_absorption(vec4 color, float density, float weight, out Closure result)
{
-#ifdef VOLUMETRICS
- result = Closure((1.0 - color.rgb) * density, vec3(0.0), vec3(0.0), 0.0);
-#else
- result = CLOSURE_DEFAULT;
-#endif
+ ClosureVolumeAbsorption volume_absorption_data;
+ volume_absorption_data.weight = weight;
+ volume_absorption_data.absorption = (1.0 - color.rgb) * density;
+
+ result = closure_eval(volume_absorption_data);
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_volume_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_volume_info.glsl
index e6d7b9d3721..464cf5227b4 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_volume_info.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_volume_info.glsl
@@ -4,14 +4,8 @@ uniform vec3 volumeColor = vec3(1.0);
uniform vec2 volumeTemperature = vec2(0.0);
/* Generic volume attribute. */
-void node_attribute_volume(sampler3D tex, mat4 transform, out vec3 outvec)
+void node_attribute_volume(sampler3D tex, mat4 transform, vec3 cos, out vec3 outvec)
{
-#if defined(MESH_SHADER) && defined(VOLUMETRICS)
- vec3 cos = volumeObjectLocalCoord;
-#else
- vec3 cos = vec3(0.0);
-#endif
-
/* Optional per-grid transform. */
if (transform[3][3] != 0.0) {
cos = (transform * vec4(cos, 1.0)).xyz;
@@ -21,14 +15,8 @@ void node_attribute_volume(sampler3D tex, mat4 transform, out vec3 outvec)
}
/* Special color attribute for smoke. */
-void node_attribute_volume_color(sampler3D tex, mat4 transform, out vec3 outvec)
+void node_attribute_volume_color(sampler3D tex, mat4 transform, vec3 cos, out vec3 outvec)
{
-#if defined(MESH_SHADER) && defined(VOLUMETRICS)
- vec3 cos = volumeObjectLocalCoord;
-#else
- vec3 cos = vec3(0.0);
-#endif
-
/* Optional per-grid transform. */
if (transform[3][3] != 0.0) {
cos = (transform * vec4(cos, 1.0)).xyz;
@@ -44,14 +32,8 @@ void node_attribute_volume_color(sampler3D tex, mat4 transform, out vec3 outvec)
}
/* Special temperature attribute for smoke. */
-void node_attribute_volume_temperature(sampler3D tex, mat4 transform, out float outf)
+void node_attribute_volume_temperature(sampler3D tex, mat4 transform, vec3 cos, out float outf)
{
-#if defined(MESH_SHADER) && defined(VOLUMETRICS)
- vec3 cos = volumeObjectLocalCoord;
-#else
- vec3 cos = vec3(0.0);
-#endif
-
/* Optional per-grid transform. */
if (transform[3][3] != 0.0) {
cos = (transform * vec4(cos, 1.0)).xyz;
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_volume_principled.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_volume_principled.glsl
index 884d5415c51..1127c34b3ac 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_volume_principled.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_volume_principled.glsl
@@ -1,3 +1,5 @@
+#pragma BLENDER_REQUIRE(gpu_shader_material_blackbody.glsl)
+
void node_volume_principled(vec4 color,
float density,
float anisotropy,
@@ -7,6 +9,7 @@ void node_volume_principled(vec4 color,
float blackbody_intensity,
vec4 blackbody_tint,
float temperature,
+ float weight,
float density_attribute,
vec4 color_attribute,
float temperature_attribute,
@@ -14,7 +17,6 @@ void node_volume_principled(vec4 color,
float layer,
out Closure result)
{
-#ifdef VOLUMETRICS
vec3 absorption_coeff = vec3(0.0);
vec3 scatter_coeff = vec3(0.0);
vec3 emission_coeff = vec3(0.0);
@@ -60,8 +62,18 @@ void node_volume_principled(vec4 color,
}
}
- result = Closure(absorption_coeff, scatter_coeff, emission_coeff, anisotropy);
-#else
- result = CLOSURE_DEFAULT;
-#endif
+ ClosureVolumeScatter volume_scatter_data;
+ volume_scatter_data.weight = weight;
+ volume_scatter_data.scattering = scatter_coeff;
+ volume_scatter_data.anisotropy = anisotropy;
+
+ ClosureVolumeAbsorption volume_absorption_data;
+ volume_absorption_data.weight = weight;
+ volume_absorption_data.absorption = absorption_coeff;
+
+ ClosureEmission emission_data;
+ emission_data.weight = weight;
+ emission_data.emission = emission_coeff;
+
+ result = closure_eval(volume_scatter_data, volume_absorption_data, emission_data);
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_volume_scatter.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_volume_scatter.glsl
index 02c54658be5..f01ead3618f 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_volume_scatter.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_volume_scatter.glsl
@@ -1,8 +1,11 @@
-void node_volume_scatter(vec4 color, float density, float anisotropy, out Closure result)
+
+void node_volume_scatter(
+ vec4 color, float density, float anisotropy, float weight, out Closure result)
{
-#ifdef VOLUMETRICS
- result = Closure(vec3(0.0), color.rgb * density, vec3(0.0), anisotropy);
-#else
- result = CLOSURE_DEFAULT;
-#endif
+ ClosureVolumeScatter volume_scatter_data;
+ volume_scatter_data.weight = weight;
+ volume_scatter_data.scattering = color.rgb * density;
+ volume_scatter_data.anisotropy = anisotropy;
+
+ result = closure_eval(volume_scatter_data);
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_wireframe.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_wireframe.glsl
index e2789e046e1..0c02dab3ae4 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_wireframe.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_wireframe.glsl
@@ -1,20 +1,21 @@
-#ifndef VOLUMETRICS
-void node_wireframe(float size, vec2 barycentric, vec3 barycentric_dist, out float fac)
+
+void node_wireframe(float size, out float fac)
{
- vec3 barys = barycentric.xyy;
- barys.z = 1.0 - barycentric.x - barycentric.y;
+ vec3 barys = g_data.barycentric_coords.xyy;
+ barys.z = 1.0 - barys.x - barys.y;
size *= 0.5;
- vec3 s = step(-size, -barys * barycentric_dist);
+ vec3 s = step(-size, -barys * g_data.barycentric_dists);
fac = max(s.x, max(s.y, s.z));
}
-void node_wireframe_screenspace(float size, vec2 barycentric, out float fac)
+void node_wireframe_screenspace(float size, out float fac)
{
- vec3 barys = barycentric.xyy;
- barys.z = 1.0 - barycentric.x - barycentric.y;
+ vec3 barys = g_data.barycentric_coords.xyy;
+ barys.z = 1.0 - barys.x - barys.y;
+#ifdef GPU_FRAGMENT_SHADER
size *= (1.0 / 3.0);
vec3 dx = dFdx(barys);
vec3 dy = dFdy(barys);
@@ -23,9 +24,7 @@ void node_wireframe_screenspace(float size, vec2 barycentric, out float fac)
vec3 s = step(-deltas * size, -barys);
fac = max(s.x, max(s.y, s.z));
-}
#else
-/* Stub wireframe because it is not compatible with volumetrics. */
-# define node_wireframe(a, b, c, d) (d = 0.0)
-# define node_wireframe_screenspace(a, b, c) (c = 0.0)
+ fac = 1.0;
#endif
+}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_world_normals.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_world_normals.glsl
index 40e46bc250c..5a0aeb2f932 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_world_normals.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_world_normals.glsl
@@ -1,25 +1,5 @@
-/* TODO: clean this `ifdef` mess. */
+
void world_normals_get(out vec3 N)
{
-#ifndef VOLUMETRICS
-# ifdef HAIR_SHADER
- vec3 B = normalize(cross(worldNormal, hairTangent));
- float cos_theta;
- if (hairThicknessRes == 1) {
- vec4 rand = texelfetch_noise_tex(gl_FragCoord.xy);
- /* Random cosine normal distribution on the hair surface. */
- cos_theta = rand.x * 2.0 - 1.0;
- }
- else {
- /* Shade as a cylinder. */
- cos_theta = hairThickTime / hairThickness;
- }
- float sin_theta = sqrt(max(0.0, 1.0 - cos_theta * cos_theta));
- N = normalize(worldNormal * sin_theta + B * cos_theta);
-# else
- N = gl_FrontFacing ? worldNormal : -worldNormal;
-# endif
-#else
- generated_from_orco(vec3(0.0), N);
-#endif
+ N = g_data.N;
}
diff --git a/source/blender/imbuf/IMB_colormanagement.h b/source/blender/imbuf/IMB_colormanagement.h
index 7cf2c02e657..97030d44047 100644
--- a/source/blender/imbuf/IMB_colormanagement.h
+++ b/source/blender/imbuf/IMB_colormanagement.h
@@ -256,10 +256,6 @@ void IMB_colormanagement_display_settings_from_ctx(
struct ColorManagedViewSettings **r_view_settings,
struct ColorManagedDisplaySettings **r_display_settings);
-const char *IMB_colormanagement_get_display_colorspace_name(
- const struct ColorManagedViewSettings *view_settings,
- const struct ColorManagedDisplaySettings *display_settings);
-
/**
* Acquire display buffer for given image buffer using specified view and display settings.
*/
diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c
index 193fda01816..53aa74edc61 100644
--- a/source/blender/imbuf/intern/colormanagement.c
+++ b/source/blender/imbuf/intern/colormanagement.c
@@ -794,9 +794,8 @@ void IMB_colormanagement_display_settings_from_ctx(
}
}
-const char *IMB_colormanagement_get_display_colorspace_name(
- const ColorManagedViewSettings *view_settings,
- const ColorManagedDisplaySettings *display_settings)
+static const char *get_display_colorspace_name(const ColorManagedViewSettings *view_settings,
+ const ColorManagedDisplaySettings *display_settings)
{
OCIO_ConstConfigRcPtr *config = OCIO_getCurrentConfig();
@@ -815,8 +814,7 @@ static ColorSpace *display_transform_get_colorspace(
const ColorManagedViewSettings *view_settings,
const ColorManagedDisplaySettings *display_settings)
{
- const char *colorspace_name = IMB_colormanagement_get_display_colorspace_name(view_settings,
- display_settings);
+ const char *colorspace_name = get_display_colorspace_name(view_settings, display_settings);
if (colorspace_name) {
return colormanage_colorspace_get_named(colorspace_name);
@@ -837,8 +835,14 @@ static OCIO_ConstCPUProcessorRcPtr *create_display_buffer_processor(const char *
const float scale = (exposure == 0.0f) ? 1.0f : powf(2.0f, exposure);
const float exponent = (gamma == 1.0f) ? 1.0f : 1.0f / max_ff(FLT_EPSILON, gamma);
- OCIO_ConstProcessorRcPtr *processor = OCIO_createDisplayProcessor(
- config, from_colorspace, view_transform, display, (use_look) ? look : "", scale, exponent);
+ OCIO_ConstProcessorRcPtr *processor = OCIO_createDisplayProcessor(config,
+ from_colorspace,
+ view_transform,
+ display,
+ (use_look) ? look : "",
+ scale,
+ exponent,
+ false);
OCIO_configRelease(config);
@@ -923,10 +927,8 @@ static OCIO_ConstCPUProcessorRcPtr *display_from_scene_linear_processor(
OCIO_ConstProcessorRcPtr *processor = NULL;
if (view_name && config) {
- const char *view_colorspace = OCIO_configGetDisplayColorSpaceName(
- config, display->name, view_name);
- processor = OCIO_configGetProcessorWithNames(
- config, global_role_scene_linear, view_colorspace);
+ processor = OCIO_createDisplayProcessor(
+ config, global_role_scene_linear, view_name, display->name, NULL, 1.0f, 1.0f, false);
OCIO_configRelease(config);
}
@@ -955,10 +957,8 @@ static OCIO_ConstCPUProcessorRcPtr *display_to_scene_linear_processor(ColorManag
OCIO_ConstProcessorRcPtr *processor = NULL;
if (view_name && config) {
- const char *view_colorspace = OCIO_configGetDisplayColorSpaceName(
- config, display->name, view_name);
- processor = OCIO_configGetProcessorWithNames(
- config, view_colorspace, global_role_scene_linear);
+ processor = OCIO_createDisplayProcessor(
+ config, global_role_scene_linear, view_name, display->name, NULL, 1.0f, 1.0f, true);
OCIO_configRelease(config);
}
@@ -1730,8 +1730,7 @@ static bool is_ibuf_rect_in_display_space(ImBuf *ibuf,
if ((view_settings->flag & COLORMANAGE_VIEW_USE_CURVES) == 0 &&
view_settings->exposure == 0.0f && view_settings->gamma == 1.0f) {
const char *from_colorspace = ibuf->rect_colorspace->name;
- const char *to_colorspace = IMB_colormanagement_get_display_colorspace_name(view_settings,
- display_settings);
+ const char *to_colorspace = get_display_colorspace_name(view_settings, display_settings);
ColorManagedLook *look_descr = colormanage_look_get_named(view_settings->look);
if (look_descr != NULL && !STREQ(look_descr->process_space, "")) {
return false;
diff --git a/source/blender/io/alembic/ABC_alembic.h b/source/blender/io/alembic/ABC_alembic.h
index d7d3adefc33..ded3258ff18 100644
--- a/source/blender/io/alembic/ABC_alembic.h
+++ b/source/blender/io/alembic/ABC_alembic.h
@@ -99,15 +99,19 @@ void ABC_get_transform(struct CacheReader *reader,
double time,
float scale);
+typedef struct ABCReadParams {
+ double time;
+ int read_flags;
+ const char *velocity_name;
+ float velocity_scale;
+} ABCReadParams;
+
/* Either modifies existing_mesh in-place or constructs a new mesh. */
struct Mesh *ABC_read_mesh(struct CacheReader *reader,
struct Object *ob,
struct Mesh *existing_mesh,
- double time,
- const char **err_str,
- int read_flags,
- const char *velocity_name,
- float velocity_scale);
+ const ABCReadParams *params,
+ const char **err_str);
bool ABC_mesh_topology_changed(struct CacheReader *reader,
struct Object *ob,
diff --git a/source/blender/io/alembic/intern/alembic_capi.cc b/source/blender/io/alembic/intern/alembic_capi.cc
index fe459ce4370..0d4e1d04db0 100644
--- a/source/blender/io/alembic/intern/alembic_capi.cc
+++ b/source/blender/io/alembic/intern/alembic_capi.cc
@@ -785,20 +785,21 @@ static ISampleSelector sample_selector_for_time(chrono_t time)
Mesh *ABC_read_mesh(CacheReader *reader,
Object *ob,
Mesh *existing_mesh,
- const double time,
- const char **err_str,
- const int read_flag,
- const char *velocity_name,
- const float velocity_scale)
+ const ABCReadParams *params,
+ const char **err_str)
{
AbcObjectReader *abc_reader = get_abc_reader(reader, ob, err_str);
if (abc_reader == nullptr) {
return nullptr;
}
- ISampleSelector sample_sel = sample_selector_for_time(time);
- return abc_reader->read_mesh(
- existing_mesh, sample_sel, read_flag, velocity_name, velocity_scale, err_str);
+ ISampleSelector sample_sel = sample_selector_for_time(params->time);
+ return abc_reader->read_mesh(existing_mesh,
+ sample_sel,
+ params->read_flags,
+ params->velocity_name,
+ params->velocity_scale,
+ err_str);
}
bool ABC_mesh_topology_changed(CacheReader *reader,
diff --git a/source/blender/io/common/CMakeLists.txt b/source/blender/io/common/CMakeLists.txt
index 02bd5b2b81f..b1add38bf01 100644
--- a/source/blender/io/common/CMakeLists.txt
+++ b/source/blender/io/common/CMakeLists.txt
@@ -7,6 +7,8 @@ set(INC
../../blenlib
../../depsgraph
../../makesdna
+ ../../../../intern/guardedalloc
+ ../../../../extern/fast_float
)
set(INC_SYS
@@ -17,9 +19,11 @@ set(SRC
intern/dupli_parent_finder.cc
intern/dupli_persistent_id.cc
intern/object_identifier.cc
+ intern/string_utils.cc
IO_abstract_hierarchy_iterator.h
IO_dupli_persistent_id.hh
+ IO_string_utils.hh
IO_types.h
intern/dupli_parent_finder.hh
)
@@ -38,6 +42,7 @@ if(WITH_GTESTS)
intern/abstract_hierarchy_iterator_test.cc
intern/hierarchy_context_order_test.cc
intern/object_identifier_test.cc
+ intern/string_utils_test.cc
)
set(TEST_INC
../../blenloader
diff --git a/source/blender/io/common/IO_string_utils.hh b/source/blender/io/common/IO_string_utils.hh
new file mode 100644
index 00000000000..25f1f01c6ed
--- /dev/null
+++ b/source/blender/io/common/IO_string_utils.hh
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "BLI_string_ref.hh"
+
+/*
+ * Various text parsing utilities commonly used by text-based input formats.
+ */
+
+namespace blender::io {
+
+/**
+ * Fetches next line from an input string buffer.
+ *
+ * The returned line will not have '\n' characters at the end;
+ * the `buffer` is modified to contain remaining text without
+ * the input line.
+ *
+ * Note that backslash (\) character is treated as a line
+ * continuation, similar to OBJ file format or a C preprocessor.
+ */
+StringRef read_next_line(StringRef &buffer);
+
+/**
+ * Drop leading white-space from a StringRef.
+ * Note that backslash character is considered white-space.
+ */
+StringRef drop_whitespace(StringRef str);
+
+/**
+ * Drop leading non-white-space from a StringRef.
+ * Note that backslash character is considered white-space.
+ */
+StringRef drop_non_whitespace(StringRef str);
+
+/**
+ * Parse an integer from an input string.
+ * The parsed result is stored in `dst`. The function skips
+ * leading white-space unless `skip_space=false`. If the
+ * number can't be parsed (invalid syntax, out of range),
+ * `fallback` value is stored instead.
+ *
+ * Returns the remainder of the input string after parsing.
+ */
+StringRef parse_int(StringRef str, int fallback, int &dst, bool skip_space = true);
+
+/**
+ * Parse a float from an input string.
+ * The parsed result is stored in `dst`. The function skips
+ * leading white-space unless `skip_space=false`. If the
+ * number can't be parsed (invalid syntax, out of range),
+ * `fallback` value is stored instead.
+ *
+ * Returns the remainder of the input string after parsing.
+ */
+StringRef parse_float(StringRef str, float fallback, float &dst, bool skip_space = true);
+
+/**
+ * Parse a number of white-space separated floats from an input string.
+ * The parsed `count` numbers are stored in `dst`. If a
+ * number can't be parsed (invalid syntax, out of range),
+ * `fallback` value is stored instead.
+ *
+ * Returns the remainder of the input string after parsing.
+ */
+StringRef parse_floats(StringRef str, float fallback, float *dst, int count);
+
+} // namespace blender::io
diff --git a/source/blender/io/common/intern/string_utils.cc b/source/blender/io/common/intern/string_utils.cc
new file mode 100644
index 00000000000..01107b0866e
--- /dev/null
+++ b/source/blender/io/common/intern/string_utils.cc
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "IO_string_utils.hh"
+
+/* Note: we could use C++17 <charconv> from_chars to parse
+ * floats, but even if some compilers claim full support,
+ * their standard libraries are not quite there yet.
+ * LLVM/libc++ only has a float parser since LLVM 14,
+ * and gcc/libstdc++ since 11.1. So until at least these are
+ * the mininum spec, use an external library. */
+#include "fast_float.h"
+#include <charconv>
+
+namespace blender::io {
+
+StringRef read_next_line(StringRef &buffer)
+{
+ const char *start = buffer.begin();
+ const char *end = buffer.end();
+ size_t len = 0;
+ char prev = 0;
+ const char *ptr = start;
+ while (ptr < end) {
+ char c = *ptr++;
+ if (c == '\n' && prev != '\\') {
+ break;
+ }
+ prev = c;
+ ++len;
+ }
+
+ buffer = StringRef(ptr, end);
+ return StringRef(start, len);
+}
+
+static bool is_whitespace(char c)
+{
+ return c <= ' ' || c == '\\';
+}
+
+StringRef drop_whitespace(StringRef str)
+{
+ while (!str.is_empty() && is_whitespace(str[0])) {
+ str = str.drop_prefix(1);
+ }
+ return str;
+}
+
+StringRef drop_non_whitespace(StringRef str)
+{
+ while (!str.is_empty() && !is_whitespace(str[0])) {
+ str = str.drop_prefix(1);
+ }
+ return str;
+}
+
+static StringRef drop_plus(StringRef str)
+{
+ if (!str.is_empty() && str[0] == '+') {
+ str = str.drop_prefix(1);
+ }
+ return str;
+}
+
+StringRef parse_float(StringRef str, float fallback, float &dst, bool skip_space)
+{
+ if (skip_space) {
+ str = drop_whitespace(str);
+ }
+ str = drop_plus(str);
+ fast_float::from_chars_result res = fast_float::from_chars(str.begin(), str.end(), dst);
+ if (res.ec == std::errc::invalid_argument || res.ec == std::errc::result_out_of_range) {
+ dst = fallback;
+ }
+ return StringRef(res.ptr, str.end());
+}
+
+StringRef parse_floats(StringRef str, float fallback, float *dst, int count)
+{
+ for (int i = 0; i < count; ++i) {
+ str = parse_float(str, fallback, dst[i]);
+ }
+ return str;
+}
+
+StringRef parse_int(StringRef str, int fallback, int &dst, bool skip_space)
+{
+ if (skip_space) {
+ str = drop_whitespace(str);
+ }
+ str = drop_plus(str);
+ std::from_chars_result res = std::from_chars(str.begin(), str.end(), dst);
+ if (res.ec == std::errc::invalid_argument || res.ec == std::errc::result_out_of_range) {
+ dst = fallback;
+ }
+ return StringRef(res.ptr, str.end());
+}
+
+} // namespace blender::io
diff --git a/source/blender/io/common/intern/string_utils_test.cc b/source/blender/io/common/intern/string_utils_test.cc
new file mode 100644
index 00000000000..a78bd7ab8a3
--- /dev/null
+++ b/source/blender/io/common/intern/string_utils_test.cc
@@ -0,0 +1,118 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+
+#include "IO_string_utils.hh"
+
+#include "testing/testing.h"
+
+namespace blender::io {
+
+#define EXPECT_STRREF_EQ(str1, str2) EXPECT_STREQ(str1, std::string(str2).c_str())
+
+TEST(string_utils, read_next_line)
+{
+ std::string str = "abc\n \n\nline with \\\ncontinuation\nCRLF ending:\r\na";
+ StringRef s = str;
+ EXPECT_STRREF_EQ("abc", read_next_line(s));
+ EXPECT_STRREF_EQ(" ", read_next_line(s));
+ EXPECT_STRREF_EQ("", read_next_line(s));
+ EXPECT_STRREF_EQ("line with \\\ncontinuation", read_next_line(s));
+ EXPECT_STRREF_EQ("CRLF ending:\r", read_next_line(s));
+ EXPECT_STRREF_EQ("a", read_next_line(s));
+ EXPECT_TRUE(s.is_empty());
+}
+
+TEST(string_utils, drop_whitespace)
+{
+ /* Empty */
+ EXPECT_STRREF_EQ("", drop_whitespace(""));
+ /* Only whitespace */
+ EXPECT_STRREF_EQ("", drop_whitespace(" "));
+ EXPECT_STRREF_EQ("", drop_whitespace(" "));
+ EXPECT_STRREF_EQ("", drop_whitespace(" \t\n\r "));
+ /* Drops leading whitespace */
+ EXPECT_STRREF_EQ("a", drop_whitespace(" a"));
+ EXPECT_STRREF_EQ("a b", drop_whitespace(" a b"));
+ EXPECT_STRREF_EQ("a b ", drop_whitespace(" a b "));
+ /* No leading whitespace */
+ EXPECT_STRREF_EQ("c", drop_whitespace("c"));
+ /* Case with backslash, should be treated as whitespace */
+ EXPECT_STRREF_EQ("d", drop_whitespace(" \\ d"));
+}
+
+TEST(string_utils, parse_int_valid)
+{
+ std::string str = "1 -10 \t 1234 1234567890 +7 123a";
+ StringRef s = str;
+ int val;
+ s = parse_int(s, 0, val);
+ EXPECT_EQ(1, val);
+ s = parse_int(s, 0, val);
+ EXPECT_EQ(-10, val);
+ s = parse_int(s, 0, val);
+ EXPECT_EQ(1234, val);
+ s = parse_int(s, 0, val);
+ EXPECT_EQ(1234567890, val);
+ s = parse_int(s, 0, val);
+ EXPECT_EQ(7, val);
+ s = parse_int(s, 0, val);
+ EXPECT_EQ(123, val);
+ EXPECT_STRREF_EQ("a", s);
+}
+
+TEST(string_utils, parse_int_invalid)
+{
+ int val;
+ /* Invalid syntax */
+ EXPECT_STRREF_EQ("--123", parse_int("--123", -1, val));
+ EXPECT_EQ(val, -1);
+ EXPECT_STRREF_EQ("foobar", parse_int("foobar", -2, val));
+ EXPECT_EQ(val, -2);
+ /* Out of integer range */
+ EXPECT_STRREF_EQ(" a", parse_int("1234567890123 a", -3, val));
+ EXPECT_EQ(val, -3);
+ /* Has leading white-space when we don't expect it */
+ EXPECT_STRREF_EQ(" 1", parse_int(" 1", -4, val, false));
+ EXPECT_EQ(val, -4);
+}
+
+TEST(string_utils, parse_float_valid)
+{
+ std::string str = "1 -10 123.5 -17.125 0.1 1e6 50.0e-1";
+ StringRef s = str;
+ float val;
+ s = parse_float(s, 0, val);
+ EXPECT_EQ(1.0f, val);
+ s = parse_float(s, 0, val);
+ EXPECT_EQ(-10.0f, val);
+ s = parse_float(s, 0, val);
+ EXPECT_EQ(123.5f, val);
+ s = parse_float(s, 0, val);
+ EXPECT_EQ(-17.125f, val);
+ s = parse_float(s, 0, val);
+ EXPECT_EQ(0.1f, val);
+ s = parse_float(s, 0, val);
+ EXPECT_EQ(1.0e6f, val);
+ s = parse_float(s, 0, val);
+ EXPECT_EQ(5.0f, val);
+ EXPECT_TRUE(s.is_empty());
+}
+
+TEST(string_utils, parse_float_invalid)
+{
+ float val;
+ /* Invalid syntax */
+ EXPECT_STRREF_EQ("_0", parse_float("_0", -1.0f, val));
+ EXPECT_EQ(val, -1.0f);
+ EXPECT_STRREF_EQ("..5", parse_float("..5", -2.0f, val));
+ EXPECT_EQ(val, -2.0f);
+ /* Out of float range. Current float parser (fast_float)
+ * clamps out of range numbers to +/- infinity, so this
+ * one gets a +inf instead of fallback -3.0. */
+ EXPECT_STRREF_EQ(" a", parse_float("9.0e500 a", -3.0f, val));
+ EXPECT_EQ(val, std::numeric_limits<float>::infinity());
+ /* Has leading white-space when we don't expect it */
+ EXPECT_STRREF_EQ(" 1", parse_float(" 1", -4.0f, val, false));
+ EXPECT_EQ(val, -4.0f);
+}
+
+} // namespace blender::io
diff --git a/source/blender/io/usd/CMakeLists.txt b/source/blender/io/usd/CMakeLists.txt
index 2b5ea39617e..e2e959814fa 100644
--- a/source/blender/io/usd/CMakeLists.txt
+++ b/source/blender/io/usd/CMakeLists.txt
@@ -16,6 +16,24 @@ add_definitions(-DPXR_STATIC)
# USD headers use deprecated TBB headers, silence warning.
add_definitions(-DTBB_SUPPRESS_DEPRECATED_MESSAGES=1)
+# Check if USD has the imaging headers available, if they are
+# add a USD_HAS_IMAGING define so code can dynamically detect this.
+# Cleanup of this variable is done at the end of the file since
+# test code further down uses it to add imaging tests.
+FIND_FILE(USD_IMAGING_HEADERS
+ NAMES
+ capsuleAdapter.h
+ PATHS
+ ${USD_INCLUDE_DIRS}
+ PATH_SUFFIXES
+ pxr/usdImaging/usdImaging/
+ NO_DEFAULT_PATH
+)
+
+if(USD_IMAGING_HEADERS)
+ add_definitions(-DUSD_HAS_IMAGING)
+endif()
+
set(INC
.
../common
@@ -129,7 +147,13 @@ target_link_libraries(bf_usd INTERFACE ${TBB_LIBRARIES})
if(WITH_GTESTS)
set(TEST_SRC
tests/usd_stage_creation_test.cc
+ tests/usd_tests_common.cc
+ tests/usd_tests_common.h
)
+ if(USD_IMAGING_HEADERS)
+ LIST(APPEND TEST_SRC tests/usd_imaging_test.cc)
+ endif()
+
set(TEST_INC
)
set(TEST_LIB
@@ -137,3 +161,7 @@ if(WITH_GTESTS)
include(GTestTesting)
blender_add_test_lib(bf_io_usd_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}")
endif()
+
+# In cmake version 3.21 and up, we can instead use the NO_CACHE option for
+# find_file so we don't need to clear it from the cache here.
+unset(USD_IMAGING_HEADERS CACHE)
diff --git a/source/blender/io/usd/tests/usd_imaging_test.cc b/source/blender/io/usd/tests/usd_imaging_test.cc
new file mode 100644
index 00000000000..497319c59bd
--- /dev/null
+++ b/source/blender/io/usd/tests/usd_imaging_test.cc
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+#include "testing/testing.h"
+
+#include "usd_tests_common.h"
+
+#include <pxr/usd/usd/stage.h>
+#include <pxr/usd/usdGeom/capsule.h>
+#include <pxr/usdImaging/usdImaging/capsuleAdapter.h>
+
+namespace blender::io::usd {
+
+class USDImagingTest : public testing::Test {
+};
+
+TEST_F(USDImagingTest, CapsuleAdapterTest)
+{
+ /* A simple test to exercise the UsdImagingGprimAdapter API to
+ * ensure the code compiles, links and returns reasonable results.
+ * We create a capsule shape on an in-memory stage and attempt
+ * to access the shape's points and topology. */
+
+ /* We must register USD plugin paths before creating the stage
+ * to avoid a crash in the USD asset resolver initialization code. */
+ if (register_usd_plugins_for_tests().empty()) {
+ FAIL();
+ return;
+ }
+
+ pxr::UsdStageRefPtr stage = pxr::UsdStage::CreateInMemory();
+
+ if (!stage) {
+ FAIL() << "Couldn't create in-memory stage.";
+ return;
+ }
+
+ pxr::UsdGeomCapsule capsule = pxr::UsdGeomCapsule::Define(stage, pxr::SdfPath("/Capsule"));
+
+ if (!capsule) {
+ FAIL() << "Couldn't create UsdGeomCapsule.";
+ return;
+ }
+
+ pxr::UsdImagingCapsuleAdapter capsule_adapter;
+ pxr::VtValue points_value = capsule_adapter.GetMeshPoints(capsule.GetPrim(),
+ pxr::UsdTimeCode::Default());
+ if (!points_value.IsHolding<pxr::VtArray<pxr::GfVec3f>>()) {
+ FAIL() << "Mesh points value holding unexpected type.";
+ return;
+ }
+
+ pxr::VtArray<pxr::GfVec3f> points = points_value.Get<pxr::VtArray<pxr::GfVec3f>>();
+ EXPECT_FALSE(points.empty());
+
+ pxr::VtValue topology_value = capsule_adapter.GetMeshTopology();
+
+ if (!topology_value.IsHolding<pxr::HdMeshTopology>()) {
+ FAIL() << "Mesh topology value holding unexpected type.";
+ return;
+ }
+
+ pxr::HdMeshTopology topology = topology_value.Get<pxr::HdMeshTopology>();
+
+ pxr::VtArray<int> vertex_counts = topology.GetFaceVertexCounts();
+ EXPECT_FALSE(vertex_counts.empty());
+
+ pxr::VtArray<int> vertex_indices = topology.GetFaceVertexIndices();
+ EXPECT_FALSE(vertex_indices.empty());
+}
+
+} // namespace blender::io::usd
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 dbe5f8cd9ce..1aa6f9691ff 100644
--- a/source/blender/io/usd/tests/usd_stage_creation_test.cc
+++ b/source/blender/io/usd/tests/usd_stage_creation_test.cc
@@ -2,6 +2,8 @@
* Copyright 2019 Blender Foundation. All rights reserved. */
#include "testing/testing.h"
+#include "usd_tests_common.h"
+
#include <pxr/base/plug/registry.h>
#include <pxr/usd/usd/stage.h>
@@ -19,23 +21,12 @@ class USDStageCreationTest : public testing::Test {
TEST_F(USDStageCreationTest, JSONFileLoadingTest)
{
- const std::string &release_dir = blender::tests::flags_test_release_dir();
- if (release_dir.empty()) {
+ std::string usd_datafiles_dir = register_usd_plugins_for_tests();
+ if (usd_datafiles_dir.empty()) {
FAIL();
+ return;
}
- char usd_datafiles_dir[FILE_MAX];
- 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
- * recognize the path as directory. */
- BLI_assert(path_len + 1 < FILE_MAX);
- usd_datafiles_dir[path_len] = '/';
- usd_datafiles_dir[path_len + 1] = '\0';
-
- 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
* files. Practically, this is a test to see whether the USD JSON files can be found and
diff --git a/source/blender/io/usd/tests/usd_tests_common.cc b/source/blender/io/usd/tests/usd_tests_common.cc
new file mode 100644
index 00000000000..9f18a289433
--- /dev/null
+++ b/source/blender/io/usd/tests/usd_tests_common.cc
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+#include "usd_tests_common.h"
+
+#include "testing/testing.h"
+
+#include <pxr/base/plug/registry.h>
+
+#include "BLI_path_util.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_appdir.h"
+
+namespace blender::io::usd {
+
+std::string register_usd_plugins_for_tests()
+{
+ static char usd_datafiles_dir[FILE_MAX] = {'\0'};
+ static bool plugin_path_registered = false;
+ if (plugin_path_registered) {
+ return usd_datafiles_dir;
+ }
+ plugin_path_registered = true;
+
+ const std::string &release_dir = blender::tests::flags_test_release_dir();
+ if (release_dir.empty()) {
+ return "";
+ }
+
+ 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
+ * recognize the path as directory. */
+ BLI_assert(path_len + 1 < FILE_MAX);
+ usd_datafiles_dir[path_len] = '/';
+ usd_datafiles_dir[path_len + 1] = '\0';
+
+ pxr::PlugRegistry::GetInstance().RegisterPlugins(usd_datafiles_dir);
+
+ return usd_datafiles_dir;
+}
+
+} // namespace blender::io::usd
diff --git a/source/blender/io/usd/tests/usd_tests_common.h b/source/blender/io/usd/tests/usd_tests_common.h
new file mode 100644
index 00000000000..b298a253ddc
--- /dev/null
+++ b/source/blender/io/usd/tests/usd_tests_common.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+#pragma once
+
+#include <string>
+
+namespace blender::io::usd {
+
+/* Calls the function to load the USD plugins from the
+ * USD data directory under the Blender bin directory
+ * that was supplied as the --test-release-dir flag to ctest.
+ * Thus function must be called before instantiating a USD
+ * stage to avoid errors. The returned string is the path to
+ * the USD data files directory from which the plugins were
+ * loaded. If the USD data files directory can't be determined,
+ * plugin registration is skipped and the empty string is
+ * returned. */
+std::string register_usd_plugins_for_tests();
+
+} // namespace blender::io::usd
diff --git a/source/blender/io/wavefront_obj/CMakeLists.txt b/source/blender/io/wavefront_obj/CMakeLists.txt
index 9cdd96ee7be..67cec000778 100644
--- a/source/blender/io/wavefront_obj/CMakeLists.txt
+++ b/source/blender/io/wavefront_obj/CMakeLists.txt
@@ -4,6 +4,7 @@ set(INC
.
./exporter
./importer
+ ../common
../../blenkernel
../../blenlib
../../bmesh
@@ -35,7 +36,6 @@ set(SRC
importer/obj_import_mtl.cc
importer/obj_import_nurbs.cc
importer/obj_importer.cc
- importer/parser_string_utils.cc
IO_wavefront_obj.h
exporter/obj_export_file_writer.hh
@@ -51,11 +51,11 @@ set(SRC
importer/obj_import_nurbs.hh
importer/obj_import_objects.hh
importer/obj_importer.hh
- importer/parser_string_utils.hh
)
set(LIB
bf_blenkernel
+ bf_io_common
)
if(WITH_TBB)
@@ -70,6 +70,7 @@ if(WITH_GTESTS)
set(TEST_SRC
tests/obj_exporter_tests.cc
tests/obj_importer_tests.cc
+ tests/obj_mtl_parser_tests.cc
tests/obj_exporter_tests.hh
)
diff --git a/source/blender/io/wavefront_obj/IO_wavefront_obj.h b/source/blender/io/wavefront_obj/IO_wavefront_obj.h
index b06ccbf8702..8b71ec750c0 100644
--- a/source/blender/io/wavefront_obj/IO_wavefront_obj.h
+++ b/source/blender/io/wavefront_obj/IO_wavefront_obj.h
@@ -84,6 +84,7 @@ struct OBJImportParams {
float clamp_size;
eTransformAxisForward forward_axis;
eTransformAxisUp up_axis;
+ bool validate_meshes;
};
/**
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc
index f78ef334d4d..96b342252c4 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc
@@ -9,6 +9,7 @@
#include "BKE_blender_version.h"
+#include "BLI_enumerable_thread_specific.hh"
#include "BLI_path_util.h"
#include "BLI_task.hh"
@@ -308,6 +309,9 @@ void OBJWriter::write_poly_elements(FormatHandler<eFileType::OBJ> &fh,
obj_mesh_data.tot_uv_vertices());
const int tot_polygons = obj_mesh_data.tot_polygons();
+ const int tot_deform_groups = obj_mesh_data.tot_deform_groups();
+ threading::EnumerableThreadSpecific<Vector<float>> group_weights;
+
obj_parallel_chunked_output(fh, tot_polygons, [&](FormatHandler<eFileType::OBJ> &buf, int idx) {
/* Polygon order for writing into the file is not necessarily the same
* as order in the mesh; it will be sorted by material indices. Remap current
@@ -330,9 +334,12 @@ void OBJWriter::write_poly_elements(FormatHandler<eFileType::OBJ> &fh,
/* Write vertex group if different from previous. */
if (export_params_.export_vertex_groups) {
+ Vector<float> &local_weights = group_weights.local();
+ local_weights.resize(tot_deform_groups);
const int16_t prev_group = idx == 0 ? NEGATIVE_INIT :
- obj_mesh_data.get_poly_deform_group_index(prev_i);
- const int16_t group = obj_mesh_data.get_poly_deform_group_index(i);
+ obj_mesh_data.get_poly_deform_group_index(
+ prev_i, local_weights);
+ const int16_t group = obj_mesh_data.get_poly_deform_group_index(i, local_weights);
if (group != prev_group) {
buf.write<eOBJSyntaxElement::object_group>(
group == NOT_FOUND ? DEFORM_GROUP_DISABLED :
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc
index 8c9b04a5ac3..c2a9e0574eb 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc
@@ -426,6 +426,9 @@ void OBJMesh::store_normal_coords_and_indices()
Vector<int> OBJMesh::calc_poly_normal_indices(const int poly_index) const
{
+ if (loop_to_normal_index_.is_empty()) {
+ return {};
+ }
const MPoly &mpoly = export_mesh_eval_->mpoly[poly_index];
const int totloop = mpoly.totloop;
Vector<int> r_poly_normal_indices(totloop);
@@ -436,46 +439,47 @@ Vector<int> OBJMesh::calc_poly_normal_indices(const int poly_index) const
return r_poly_normal_indices;
}
-int16_t OBJMesh::get_poly_deform_group_index(const int poly_index) const
+int OBJMesh::tot_deform_groups() const
+{
+ if (!BKE_object_supports_vertex_groups(&export_object_eval_)) {
+ return 0;
+ }
+ return BKE_object_defgroup_count(&export_object_eval_);
+}
+
+int16_t OBJMesh::get_poly_deform_group_index(const int poly_index,
+ MutableSpan<float> group_weights) const
{
BLI_assert(poly_index < export_mesh_eval_->totpoly);
- const MPoly &mpoly = export_mesh_eval_->mpoly[poly_index];
- const MLoop *mloop = &export_mesh_eval_->mloop[mpoly.loopstart];
- const Object *obj = &export_object_eval_;
- const int tot_deform_groups = BKE_object_defgroup_count(obj);
- /* Indices of the vector index into deform groups of an object; values are the]
- * number of vertex members in one deform group. */
- Vector<int16_t> deform_group_members(tot_deform_groups, 0);
- /* Whether at least one vertex in the polygon belongs to any group. */
- bool found_group = false;
-
- const MDeformVert *dvert_orig = static_cast<MDeformVert *>(
+ BLI_assert(group_weights.size() == BKE_object_defgroup_count(&export_object_eval_));
+
+ const MDeformVert *dvert_layer = static_cast<MDeformVert *>(
CustomData_get_layer(&export_mesh_eval_->vdata, CD_MDEFORMVERT));
- if (!dvert_orig) {
+ if (!dvert_layer) {
return NOT_FOUND;
}
- const MDeformWeight *curr_weight = nullptr;
- const MDeformVert *dvert = nullptr;
- for (int loop_index = 0; loop_index < mpoly.totloop; loop_index++) {
- dvert = &dvert_orig[(mloop + loop_index)->v];
- curr_weight = dvert->dw;
- if (curr_weight) {
- bDeformGroup *vertex_group = static_cast<bDeformGroup *>(
- BLI_findlink(BKE_object_defgroup_list(obj), curr_weight->def_nr));
- if (vertex_group) {
- deform_group_members[curr_weight->def_nr] += 1;
- found_group = true;
+ group_weights.fill(0);
+ bool found_any_group = false;
+ const MPoly &mpoly = export_mesh_eval_->mpoly[poly_index];
+ const MLoop *mloop = &export_mesh_eval_->mloop[mpoly.loopstart];
+ for (int loop_i = 0; loop_i < mpoly.totloop; ++loop_i, ++mloop) {
+ const MDeformVert &dvert = dvert_layer[mloop->v];
+ for (int weight_i = 0; weight_i < dvert.totweight; ++weight_i) {
+ const auto group = dvert.dw[weight_i].def_nr;
+ if (group < group_weights.size()) {
+ group_weights[group] += dvert.dw[weight_i].weight;
+ found_any_group = true;
}
}
}
- if (!found_group) {
+ if (!found_any_group) {
return NOT_FOUND;
}
/* Index of the group with maximum vertices. */
- int16_t max_idx = std::max_element(deform_group_members.begin(), deform_group_members.end()) -
- deform_group_members.begin();
+ int16_t max_idx = std::max_element(group_weights.begin(), group_weights.end()) -
+ group_weights.begin();
return max_idx;
}
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh
index 7650a220810..f47ca423dbc 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh
@@ -116,6 +116,7 @@ class OBJMesh : NonCopyable {
int tot_uv_vertices() const;
int tot_normal_indices() const;
int tot_edges() const;
+ int tot_deform_groups() const;
bool is_mirrored_transform() const
{
return mirrored_transform_;
@@ -204,13 +205,15 @@ class OBJMesh : NonCopyable {
*/
Vector<int> calc_poly_normal_indices(int poly_index) const;
/**
- * Find the index of the vertex group with the maximum number of vertices in a polygon.
- * The index indices into the #Object.defbase.
+ * Find the most representative vertex group of a polygon.
+ *
+ * This adds up vertex group weights, and the group with the largest
+ * weight sum across the polygon is the one returned.
*
- * If two or more groups have the same number of vertices (maximum), group name depends on the
- * implementation of #std::max_element.
+ * group_weights is temporary storage to avoid reallocations, it must
+ * be the size of amount of vertex groups in the object.
*/
- int16_t get_poly_deform_group_index(int poly_index) const;
+ int16_t get_poly_deform_group_index(int poly_index, MutableSpan<float> group_weights) const;
/**
* Find the name of the vertex deform group at the given index.
* The index indices into the #Object.defbase.
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.cc b/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.cc
index 17069c18295..c247048ce13 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.cc
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_nurbs.cc
@@ -72,12 +72,12 @@ float3 OBJCurve::vertex_coordinates(const int spline_index,
int OBJCurve::total_spline_control_points(const int spline_index) const
{
const Nurb *const nurb = static_cast<Nurb *>(BLI_findlink(&export_curve_->nurb, spline_index));
- const int r_nurbs_degree = nurb->orderu - 1;
+ int degree = nurb->type == CU_POLY ? 1 : nurb->orderu - 1;
/* Total control points = Number of points in the curve (+ degree of the
* curve if it is cyclic). */
int r_tot_control_points = nurb->pntsv * nurb->pntsu;
if (nurb->flagu & CU_NURB_CYCLIC) {
- r_tot_control_points += r_nurbs_degree;
+ r_tot_control_points += degree;
}
return r_tot_control_points;
}
@@ -85,7 +85,7 @@ int OBJCurve::total_spline_control_points(const int spline_index) const
int OBJCurve::get_nurbs_degree(const int spline_index) const
{
const Nurb *const nurb = static_cast<Nurb *>(BLI_findlink(&export_curve_->nurb, spline_index));
- return nurb->orderu - 1;
+ return nurb->type == CU_POLY ? 1 : nurb->orderu - 1;
}
short OBJCurve::get_nurbs_flagu(const int spline_index) const
diff --git a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc
index 584d8ae4ec0..78b709c884a 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc
+++ b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc
@@ -68,6 +68,17 @@ static void print_exception_error(const std::system_error &ex)
<< std::endl;
}
+static bool is_curve_nurbs_compatible(const Nurb *nurb)
+{
+ while (nurb) {
+ if (nurb->type == CU_BEZIER || nurb->pntsv != 1) {
+ return false;
+ }
+ nurb = nurb->next;
+ }
+ return true;
+}
+
/**
* Filter supported objects from the Scene.
*
@@ -104,27 +115,13 @@ filter_supported_objects(Depsgraph *depsgraph, const OBJExportParams &export_par
}
break;
}
- switch (nurb->type) {
- case CU_NURBS:
- if (export_params.export_curves_as_nurbs) {
- /* Export in parameter form: control points. */
- r_exportable_nurbs.append(
- std::make_unique<OBJCurve>(depsgraph, export_params, object));
- }
- else {
- /* Export in mesh form: edges and vertices. */
- r_exportable_meshes.append(
- std::make_unique<OBJMesh>(depsgraph, export_params, object));
- }
- break;
- case CU_BEZIER:
- /* Always export in mesh form: edges and vertices. */
- r_exportable_meshes.append(
- std::make_unique<OBJMesh>(depsgraph, export_params, object));
- break;
- default:
- /* Other curve types are not supported. */
- break;
+ if (export_params.export_curves_as_nurbs && is_curve_nurbs_compatible(nurb)) {
+ /* Export in parameter form: control points. */
+ r_exportable_nurbs.append(std::make_unique<OBJCurve>(depsgraph, export_params, object));
+ }
+ else {
+ /* Export in mesh form: edges and vertices. */
+ r_exportable_meshes.append(std::make_unique<OBJMesh>(depsgraph, export_params, object));
}
break;
}
diff --git a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc
index d3d4e1ba860..3e722f8a0dd 100644
--- a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc
+++ b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc
@@ -8,7 +8,7 @@
#include "BLI_string_ref.hh"
#include "BLI_vector.hh"
-#include "parser_string_utils.hh"
+#include "IO_string_utils.hh"
#include "obj_import_file_reader.hh"
@@ -34,7 +34,8 @@ static Geometry *create_geometry(Geometry *const prev_geometry,
Geometry *g = r_all_geometries.last().get();
g->geom_type_ = new_type;
g->geometry_name_ = name.is_empty() ? "New object" : name;
- r_offset.set_index_offset(global_vertices.vertices.size());
+ g->vertex_start_ = global_vertices.vertices.size();
+ r_offset.set_index_offset(g->vertex_start_);
return g;
};
@@ -66,48 +67,40 @@ static Geometry *create_geometry(Geometry *const prev_geometry,
}
static void geom_add_vertex(Geometry *geom,
- const StringRef rest_line,
+ const StringRef line,
GlobalVertices &r_global_vertices)
{
- float3 curr_vert;
- Vector<StringRef> str_vert_split;
- split_by_char(rest_line, ' ', str_vert_split);
- copy_string_to_float(str_vert_split, FLT_MAX, {curr_vert, 3});
- r_global_vertices.vertices.append(curr_vert);
- geom->vertex_indices_.append(r_global_vertices.vertices.size() - 1);
+ float3 vert;
+ parse_floats(line, FLT_MAX, vert, 3);
+ r_global_vertices.vertices.append(vert);
+ geom->vertex_count_++;
}
static void geom_add_vertex_normal(Geometry *geom,
- const StringRef rest_line,
+ const StringRef line,
GlobalVertices &r_global_vertices)
{
- float3 curr_vert_normal;
- Vector<StringRef> str_vert_normal_split;
- split_by_char(rest_line, ' ', str_vert_normal_split);
- copy_string_to_float(str_vert_normal_split, FLT_MAX, {curr_vert_normal, 3});
- r_global_vertices.vertex_normals.append(curr_vert_normal);
+ float3 normal;
+ parse_floats(line, FLT_MAX, normal, 3);
+ r_global_vertices.vertex_normals.append(normal);
geom->has_vertex_normals_ = true;
}
-static void geom_add_uv_vertex(const StringRef rest_line, GlobalVertices &r_global_vertices)
+static void geom_add_uv_vertex(const StringRef line, GlobalVertices &r_global_vertices)
{
- float2 curr_uv_vert;
- Vector<StringRef> str_uv_vert_split;
- split_by_char(rest_line, ' ', str_uv_vert_split);
- copy_string_to_float(str_uv_vert_split, FLT_MAX, {curr_uv_vert, 2});
- r_global_vertices.uv_vertices.append(curr_uv_vert);
+ float2 uv;
+ parse_floats(line, FLT_MAX, uv, 2);
+ r_global_vertices.uv_vertices.append(uv);
}
static void geom_add_edge(Geometry *geom,
- const StringRef rest_line,
+ StringRef line,
const VertexIndexOffset &offsets,
GlobalVertices &r_global_vertices)
{
- int edge_v1 = -1, edge_v2 = -1;
- Vector<StringRef> str_edge_split;
- split_by_char(rest_line, ' ', str_edge_split);
- copy_string_to_int(str_edge_split[0], -1, edge_v1);
- copy_string_to_int(str_edge_split[1], -1, edge_v2);
+ int edge_v1, edge_v2;
+ line = parse_int(line, -1, edge_v1);
+ line = parse_int(line, -1, edge_v2);
/* Always keep stored indices non-negative and zero-based. */
edge_v1 += edge_v1 < 0 ? r_global_vertices.vertices.size() : -offsets.get_index_offset() - 1;
edge_v2 += edge_v2 < 0 ? r_global_vertices.vertices.size() : -offsets.get_index_offset() - 1;
@@ -116,78 +109,45 @@ static void geom_add_edge(Geometry *geom,
}
static void geom_add_polygon(Geometry *geom,
- const StringRef rest_line,
+ StringRef line,
const GlobalVertices &global_vertices,
const VertexIndexOffset &offsets,
- const StringRef state_material_name,
- const StringRef state_object_group,
- const bool state_shaded_smooth)
+ const int material_index,
+ const int group_index,
+ const bool shaded_smooth)
{
PolyElem curr_face;
- curr_face.shaded_smooth = state_shaded_smooth;
- if (!state_material_name.is_empty()) {
- curr_face.material_name = state_material_name;
- }
- if (!state_object_group.is_empty()) {
- curr_face.vertex_group = state_object_group;
- /* Yes it repeats several times, but another if-check will not reduce steps either. */
+ curr_face.shaded_smooth = shaded_smooth;
+ curr_face.material_index = material_index;
+ if (group_index >= 0) {
+ curr_face.vertex_group_index = group_index;
geom->use_vertex_groups_ = true;
}
+ const int orig_corners_size = geom->face_corners_.size();
+ curr_face.start_index_ = orig_corners_size;
+
bool face_valid = true;
- Vector<StringRef> str_corners_split;
- split_by_char(rest_line, ' ', str_corners_split);
- for (StringRef str_corner : str_corners_split) {
+ while (!line.is_empty() && face_valid) {
PolyCorner corner;
- const size_t n_slash = std::count(str_corner.begin(), str_corner.end(), '/');
bool got_uv = false, got_normal = false;
- if (n_slash == 0) {
- /* Case: "f v1 v2 v3". */
- copy_string_to_int(str_corner, INT32_MAX, corner.vert_index);
- }
- else if (n_slash == 1) {
- /* Case: "f v1/vt1 v2/vt2 v3/vt3". */
- Vector<StringRef> vert_uv_split;
- split_by_char(str_corner, '/', vert_uv_split);
- if (vert_uv_split.size() != 1 && vert_uv_split.size() != 2) {
- fprintf(stderr, "Invalid face syntax '%s', ignoring\n", std::string(str_corner).c_str());
- face_valid = false;
+ /* Parse vertex index. */
+ line = parse_int(line, INT32_MAX, corner.vert_index, false);
+ face_valid &= corner.vert_index != INT32_MAX;
+ if (!line.is_empty() && line[0] == '/') {
+ /* Parse UV index. */
+ line = line.drop_prefix(1);
+ if (!line.is_empty() && line[0] != '/') {
+ line = parse_int(line, INT32_MAX, corner.uv_vert_index, false);
+ got_uv = corner.uv_vert_index != INT32_MAX;
}
- else {
- copy_string_to_int(vert_uv_split[0], INT32_MAX, corner.vert_index);
- if (vert_uv_split.size() == 2) {
- copy_string_to_int(vert_uv_split[1], INT32_MAX, corner.uv_vert_index);
- got_uv = corner.uv_vert_index != INT32_MAX;
- }
+ /* Parse normal index. */
+ if (!line.is_empty() && line[0] == '/') {
+ line = line.drop_prefix(1);
+ line = parse_int(line, INT32_MAX, corner.vertex_normal_index, false);
+ got_normal = corner.uv_vert_index != INT32_MAX;
}
}
- else if (n_slash == 2) {
- /* Case: "f v1//vn1 v2//vn2 v3//vn3". */
- /* Case: "f v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3". */
- Vector<StringRef> vert_uv_normal_split;
- split_by_char(str_corner, '/', vert_uv_normal_split);
- if (vert_uv_normal_split.size() != 2 && vert_uv_normal_split.size() != 3) {
- fprintf(stderr, "Invalid face syntax '%s', ignoring\n", std::string(str_corner).c_str());
- face_valid = false;
- }
- else {
- copy_string_to_int(vert_uv_normal_split[0], INT32_MAX, corner.vert_index);
- if (vert_uv_normal_split.size() == 3) {
- copy_string_to_int(vert_uv_normal_split[1], INT32_MAX, corner.uv_vert_index);
- got_uv = corner.uv_vert_index != INT32_MAX;
- copy_string_to_int(vert_uv_normal_split[2], INT32_MAX, corner.vertex_normal_index);
- got_normal = corner.vertex_normal_index != INT32_MAX;
- }
- else {
- copy_string_to_int(vert_uv_normal_split[1], INT32_MAX, corner.vertex_normal_index);
- got_normal = corner.vertex_normal_index != INT32_MAX;
- }
- }
- }
- else {
- fprintf(stderr, "Invalid face syntax '%s', ignoring\n", std::string(str_corner).c_str());
- face_valid = false;
- }
/* Always keep stored indices non-negative and zero-based. */
corner.vert_index += corner.vert_index < 0 ? global_vertices.vertices.size() :
-offsets.get_index_offset() - 1;
@@ -221,19 +181,28 @@ static void geom_add_polygon(Geometry *geom,
face_valid = false;
}
}
- curr_face.face_corners.append(corner);
+ geom->face_corners_.append(corner);
+ curr_face.corner_count_++;
+
+ /* Skip whitespace to get to the next face corner. */
+ line = drop_whitespace(line);
}
if (face_valid) {
geom->face_elements_.append(curr_face);
- geom->total_loops_ += curr_face.face_corners.size();
+ geom->total_loops_ += curr_face.corner_count_;
+ }
+ else {
+ /* Remove just-added corners for the invalid face. */
+ geom->face_corners_.resize(orig_corners_size);
+ geom->has_invalid_polys_ = true;
}
}
static Geometry *geom_set_curve_type(Geometry *geom,
const StringRef rest_line,
const GlobalVertices &global_vertices,
- const StringRef state_object_group,
+ const StringRef group_name,
VertexIndexOffset &r_offsets,
Vector<std::unique_ptr<Geometry>> &r_all_geometries)
{
@@ -242,254 +211,411 @@ static Geometry *geom_set_curve_type(Geometry *geom,
return geom;
}
geom = create_geometry(
- geom, GEOM_CURVE, state_object_group, global_vertices, r_all_geometries, r_offsets);
- geom->nurbs_element_.group_ = state_object_group;
+ geom, GEOM_CURVE, group_name, global_vertices, r_all_geometries, r_offsets);
+ geom->nurbs_element_.group_ = group_name;
return geom;
}
-static void geom_set_curve_degree(Geometry *geom, const StringRef rest_line)
+static void geom_set_curve_degree(Geometry *geom, const StringRef line)
{
- copy_string_to_int(rest_line, 3, geom->nurbs_element_.degree);
+ parse_int(line, 3, geom->nurbs_element_.degree);
}
static void geom_add_curve_vertex_indices(Geometry *geom,
- const StringRef rest_line,
+ StringRef line,
const GlobalVertices &global_vertices)
{
- Vector<StringRef> str_curv_split;
- split_by_char(rest_line, ' ', str_curv_split);
- /* Remove "0.0" and "1.0" from the strings. They are hardcoded. */
- str_curv_split.remove(0);
- str_curv_split.remove(0);
- geom->nurbs_element_.curv_indices.resize(str_curv_split.size());
- copy_string_to_int(str_curv_split, INT32_MAX, geom->nurbs_element_.curv_indices);
- for (int &curv_index : geom->nurbs_element_.curv_indices) {
+ /* Curve lines always have "0.0" and "1.0", skip over them. */
+ float dummy[2];
+ line = parse_floats(line, 0, dummy, 2);
+ /* Parse indices. */
+ while (!line.is_empty()) {
+ int index;
+ line = parse_int(line, INT32_MAX, index);
+ if (index == INT32_MAX) {
+ return;
+ }
/* Always keep stored indices non-negative and zero-based. */
- curv_index += curv_index < 0 ? global_vertices.vertices.size() : -1;
+ index += index < 0 ? global_vertices.vertices.size() : -1;
+ geom->nurbs_element_.curv_indices.append(index);
}
}
-static void geom_add_curve_parameters(Geometry *geom, const StringRef rest_line)
+static void geom_add_curve_parameters(Geometry *geom, StringRef line)
{
- Vector<StringRef> str_parm_split;
- split_by_char(rest_line, ' ', str_parm_split);
- if (str_parm_split[0] != "u" && str_parm_split[0] != "v") {
- std::cerr << "Surfaces are not supported:'" << str_parm_split[0] << "'" << std::endl;
+ line = drop_whitespace(line);
+ if (line.is_empty()) {
+ std::cerr << "Invalid OBJ curve parm line: '" << line << "'" << std::endl;
+ return;
+ }
+ if (line[0] != 'u') {
+ std::cerr << "OBJ curve surfaces are not supported: '" << line[0] << "'" << std::endl;
return;
}
- str_parm_split.remove(0);
- geom->nurbs_element_.parm.resize(str_parm_split.size());
- copy_string_to_float(str_parm_split, FLT_MAX, geom->nurbs_element_.parm);
+ line = line.drop_prefix(1);
+
+ while (!line.is_empty()) {
+ float val;
+ line = parse_float(line, FLT_MAX, val);
+ if (val != FLT_MAX) {
+ geom->nurbs_element_.parm.append(val);
+ }
+ else {
+ std::cerr << "OBJ curve parm line has invalid number" << std::endl;
+ return;
+ }
+ }
}
-static void geom_update_object_group(const StringRef rest_line, std::string &r_state_object_group)
+static void geom_update_group(const StringRef rest_line, std::string &r_group_name)
{
if (rest_line.find("off") != string::npos || rest_line.find("null") != string::npos ||
rest_line.find("default") != string::npos) {
/* Set group for future elements like faces or curves to empty. */
- r_state_object_group = "";
+ r_group_name = "";
return;
}
- r_state_object_group = rest_line;
-}
-
-static void geom_update_polygon_material(Geometry *geom,
- const StringRef rest_line,
- std::string &r_state_material_name)
-{
- /* Materials may repeat if faces are written without sorting. */
- geom->material_names_.add(string(rest_line));
- r_state_material_name = rest_line;
+ r_group_name = rest_line;
}
-static void geom_update_smooth_group(const StringRef rest_line, bool &r_state_shaded_smooth)
+static void geom_update_smooth_group(StringRef line, bool &r_state_shaded_smooth)
{
+ line = drop_whitespace(line);
/* Some implementations use "0" and "null" too, in addition to "off". */
- if (rest_line != "0" && rest_line.find("off") == StringRef::not_found &&
- rest_line.find("null") == StringRef::not_found) {
- int smooth = 0;
- copy_string_to_int(rest_line, 0, smooth);
- r_state_shaded_smooth = smooth != 0;
- }
- else {
- /* The OBJ file explicitly set shading to off. */
+ if (line == "0" || line.startswith("off") || line.startswith("null")) {
r_state_shaded_smooth = false;
+ return;
}
+
+ int smooth = 0;
+ parse_int(line, 0, smooth);
+ r_state_shaded_smooth = smooth != 0;
}
-OBJParser::OBJParser(const OBJImportParams &import_params) : import_params_(import_params)
+OBJParser::OBJParser(const OBJImportParams &import_params, size_t read_buffer_size = 64 * 1024)
+ : import_params_(import_params), read_buffer_size_(read_buffer_size)
{
- obj_file_.open(import_params_.filepath);
- if (!obj_file_.good()) {
+ obj_file_ = BLI_fopen(import_params_.filepath, "rb");
+ if (!obj_file_) {
fprintf(stderr, "Cannot read from OBJ file:'%s'.\n", import_params_.filepath);
return;
}
}
+OBJParser::~OBJParser()
+{
+ if (obj_file_) {
+ fclose(obj_file_);
+ }
+}
+
void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries,
GlobalVertices &r_global_vertices)
{
- if (!obj_file_.good()) {
+ if (!obj_file_) {
return;
}
- string line;
- /* Store vertex coordinates that belong to other Geometry instances. */
VertexIndexOffset offsets;
- /* Non owning raw pointer to a Geometry. To be updated while creating a new Geometry. */
Geometry *curr_geom = create_geometry(
nullptr, GEOM_MESH, "", r_global_vertices, r_all_geometries, offsets);
- /* State-setting variables: if set, they remain the same for the remaining
+ /* State variables: once set, they remain the same for the remaining
* elements in the object. */
bool state_shaded_smooth = false;
- string state_object_group;
+ string state_group_name;
+ int state_group_index = -1;
string state_material_name;
+ int state_material_index = -1;
+
+ /* Read the input file in chunks. We need up to twice the possible chunk size,
+ * to possibly store remainder of the previous input line that got broken mid-chunk. */
+ Array<char> buffer(read_buffer_size_ * 2);
+
+ size_t buffer_offset = 0;
+ size_t line_number = 0;
+ while (true) {
+ /* Read a chunk of input from the file. */
+ size_t bytes_read = fread(buffer.data() + buffer_offset, 1, read_buffer_size_, obj_file_);
+ if (bytes_read == 0 && buffer_offset == 0) {
+ break; /* No more data to read. */
+ }
- while (std::getline(obj_file_, line)) {
- /* Keep reading new lines if the last character is `\`. */
- /* Another way is to make a getline wrapper and use it in the while condition. */
- read_next_line(obj_file_, line);
+ /* Ensure buffer ends in a newline. */
+ if (bytes_read < read_buffer_size_) {
+ if (bytes_read == 0 || buffer[buffer_offset + bytes_read - 1] != '\n') {
+ buffer[buffer_offset + bytes_read] = '\n';
+ bytes_read++;
+ }
+ }
- StringRef line_key, rest_line;
- split_line_key_rest(line, line_key, rest_line);
- if (line.empty() || rest_line.is_empty()) {
- continue;
+ size_t buffer_end = buffer_offset + bytes_read;
+ if (buffer_end == 0) {
+ break;
}
- switch (line_key_str_to_enum(line_key)) {
- case eOBJLineKey::V: {
- geom_add_vertex(curr_geom, rest_line, r_global_vertices);
- break;
+
+ /* Find last newline. */
+ size_t last_nl = buffer_end;
+ while (last_nl > 0) {
+ --last_nl;
+ if (buffer[last_nl] == '\n') {
+ if (last_nl < 1 || buffer[last_nl - 1] != '\\') {
+ break;
+ }
}
- case eOBJLineKey::VN: {
- geom_add_vertex_normal(curr_geom, rest_line, r_global_vertices);
- break;
+ }
+ if (buffer[last_nl] != '\n') {
+ /* Whole line did not fit into our read buffer. Warn and exit. */
+ fprintf(stderr,
+ "OBJ file contains a line #%zu that is too long (max. length %zu)\n",
+ line_number,
+ read_buffer_size_);
+ break;
+ }
+ ++last_nl;
+
+ /* Parse the buffer (until last newline) that we have so far,
+ * line by line. */
+ StringRef buffer_str{buffer.data(), (int64_t)last_nl};
+ while (!buffer_str.is_empty()) {
+ StringRef line = read_next_line(buffer_str);
+ ++line_number;
+ if (line.is_empty()) {
+ continue;
}
- case eOBJLineKey::VT: {
- geom_add_uv_vertex(rest_line, r_global_vertices);
- break;
+ /* Most common things that start with 'v': vertices, normals, UVs. */
+ if (line[0] == 'v') {
+ if (line.startswith("v ")) {
+ line = line.drop_prefix(2);
+ geom_add_vertex(curr_geom, line, r_global_vertices);
+ }
+ else if (line.startswith("vn ")) {
+ line = line.drop_prefix(3);
+ geom_add_vertex_normal(curr_geom, line, r_global_vertices);
+ }
+ else if (line.startswith("vt ")) {
+ line = line.drop_prefix(3);
+ geom_add_uv_vertex(line, r_global_vertices);
+ }
}
- case eOBJLineKey::F: {
+ /* Faces. */
+ else if (line.startswith("f ")) {
+ line = line.drop_prefix(2);
geom_add_polygon(curr_geom,
- rest_line,
+ line,
r_global_vertices,
offsets,
- state_material_name,
- state_material_name,
+ state_material_index,
+ state_group_index, /* TODO was wrongly material name! */
state_shaded_smooth);
- break;
- }
- case eOBJLineKey::L: {
- geom_add_edge(curr_geom, rest_line, offsets, r_global_vertices);
- break;
- }
- case eOBJLineKey::CSTYPE: {
- curr_geom = geom_set_curve_type(curr_geom,
- rest_line,
- r_global_vertices,
- state_object_group,
- offsets,
- r_all_geometries);
- break;
- }
- case eOBJLineKey::DEG: {
- geom_set_curve_degree(curr_geom, rest_line);
- break;
- }
- case eOBJLineKey::CURV: {
- geom_add_curve_vertex_indices(curr_geom, rest_line, r_global_vertices);
- break;
- }
- case eOBJLineKey::PARM: {
- geom_add_curve_parameters(curr_geom, rest_line);
- break;
- }
- case eOBJLineKey::O: {
+ }
+ /* Faces. */
+ else if (line.startswith("l ")) {
+ line = line.drop_prefix(2);
+ geom_add_edge(curr_geom, line, offsets, r_global_vertices);
+ }
+ /* Objects. */
+ else if (line.startswith("o ")) {
+ line = line.drop_prefix(2);
state_shaded_smooth = false;
- state_object_group = "";
+ state_group_name = "";
state_material_name = "";
curr_geom = create_geometry(
- curr_geom, GEOM_MESH, rest_line, r_global_vertices, r_all_geometries, offsets);
- break;
- }
- case eOBJLineKey::G: {
- geom_update_object_group(rest_line, state_object_group);
- break;
- }
- case eOBJLineKey::S: {
- geom_update_smooth_group(rest_line, state_shaded_smooth);
- break;
- }
- case eOBJLineKey::USEMTL: {
- geom_update_polygon_material(curr_geom, rest_line, state_material_name);
- break;
- }
- case eOBJLineKey::MTLLIB: {
- mtl_libraries_.append(string(rest_line));
- break;
- }
- case eOBJLineKey::COMMENT:
- break;
- default:
- std::cout << "Element not recognised: '" << line_key << "'" << std::endl;
- break;
+ curr_geom, GEOM_MESH, line, r_global_vertices, r_all_geometries, offsets);
+ }
+ /* Groups. */
+ else if (line.startswith("g ")) {
+ line = line.drop_prefix(2);
+ geom_update_group(line, state_group_name);
+ int new_index = curr_geom->group_indices_.size();
+ state_group_index = curr_geom->group_indices_.lookup_or_add(state_group_name, new_index);
+ if (new_index == state_group_index) {
+ curr_geom->group_order_.append(state_group_name);
+ }
+ }
+ /* Smoothing groups. */
+ else if (line.startswith("s ")) {
+ line = line.drop_prefix(2);
+ geom_update_smooth_group(line, state_shaded_smooth);
+ }
+ /* Materials and their libraries. */
+ else if (line.startswith("usemtl ")) {
+ line = line.drop_prefix(7);
+ state_material_name = line;
+ int new_mat_index = curr_geom->material_indices_.size();
+ state_material_index = curr_geom->material_indices_.lookup_or_add(state_material_name,
+ new_mat_index);
+ if (new_mat_index == state_material_index) {
+ curr_geom->material_order_.append(state_material_name);
+ }
+ }
+ else if (line.startswith("mtllib ")) {
+ line = line.drop_prefix(7);
+ mtl_libraries_.append(string(line));
+ }
+ /* Comments. */
+ else if (line.startswith("#")) {
+ /* Nothing to do. */
+ }
+ /* Curve related things. */
+ else if (line.startswith("cstype ")) {
+ line = line.drop_prefix(7);
+ curr_geom = geom_set_curve_type(
+ curr_geom, line, r_global_vertices, state_group_name, offsets, r_all_geometries);
+ }
+ else if (line.startswith("deg ")) {
+ line = line.drop_prefix(4);
+ geom_set_curve_degree(curr_geom, line);
+ }
+ else if (line.startswith("curv ")) {
+ line = line.drop_prefix(5);
+ geom_add_curve_vertex_indices(curr_geom, line, r_global_vertices);
+ }
+ else if (line.startswith("parm ")) {
+ line = line.drop_prefix(5);
+ geom_add_curve_parameters(curr_geom, line);
+ }
+ else if (line.startswith("end")) {
+ /* End of curve definition, nothing else to do. */
+ }
+ else {
+ std::cout << "OBJ element not recognised: '" << line << "'" << std::endl;
+ }
}
+
+ /* We might have a line that was cut in the middle by the previous buffer;
+ * copy it over for next chunk reading. */
+ size_t left_size = buffer_end - last_nl;
+ memmove(buffer.data(), buffer.data() + last_nl, left_size);
+ buffer_offset = left_size;
}
}
-/**
- * Skip all texture map options and get the filepath from a "map_" line.
- */
-static StringRef skip_unsupported_options(StringRef line)
+static eMTLSyntaxElement mtl_line_start_to_enum(StringRef &line)
{
- TextureMapOptions map_options;
- StringRef last_option;
- int64_t last_option_pos = 0;
-
- /* Find the last texture map option. */
- for (StringRef option : map_options.all_options()) {
- const int64_t pos{line.find(option)};
- /* Equality (>=) takes care of finding an option in the beginning of the line. Avoid messing
- * with signed-unsigned int comparison. */
- if (pos != StringRef::not_found && pos >= last_option_pos) {
- last_option = option;
- last_option_pos = pos;
- }
+ if (line.startswith("map_Kd")) {
+ line = line.drop_prefix(6);
+ return eMTLSyntaxElement::map_Kd;
}
-
- if (last_option.is_empty()) {
- /* No option found, line is the filepath */
- return line;
+ if (line.startswith("map_Ks")) {
+ line = line.drop_prefix(6);
+ return eMTLSyntaxElement::map_Ks;
+ }
+ if (line.startswith("map_Ns")) {
+ line = line.drop_prefix(6);
+ return eMTLSyntaxElement::map_Ns;
+ }
+ if (line.startswith("map_d")) {
+ line = line.drop_prefix(5);
+ return eMTLSyntaxElement::map_d;
}
+ if (line.startswith("refl")) {
+ line = line.drop_prefix(4);
+ return eMTLSyntaxElement::map_refl;
+ }
+ if (line.startswith("map_refl")) {
+ line = line.drop_prefix(8);
+ return eMTLSyntaxElement::map_refl;
+ }
+ if (line.startswith("map_Ke")) {
+ line = line.drop_prefix(6);
+ return eMTLSyntaxElement::map_Ke;
+ }
+ if (line.startswith("bump")) {
+ line = line.drop_prefix(4);
+ return eMTLSyntaxElement::map_Bump;
+ }
+ if (line.startswith("map_Bump") || line.startswith("map_bump")) {
+ line = line.drop_prefix(8);
+ return eMTLSyntaxElement::map_Bump;
+ }
+ return eMTLSyntaxElement::string;
+}
- /* Remove up to start of the last option + size of the last option + space after it. */
- line = line.drop_prefix(last_option_pos + last_option.size() + 1);
- for (int i = 0; i < map_options.number_of_args(last_option); i++) {
- const int64_t pos_space{line.find_first_of(' ')};
- if (pos_space != StringRef::not_found) {
- BLI_assert(pos_space + 1 < line.size());
- line = line.drop_prefix(pos_space + 1);
+static const std::pair<const char *, int> unsupported_texture_options[] = {
+ {"-blendu ", 1},
+ {"-blendv ", 1},
+ {"-boost ", 1},
+ {"-cc ", 1},
+ {"-clamp ", 1},
+ {"-imfchan ", 1},
+ {"-mm ", 2},
+ {"-t ", 3},
+ {"-texres ", 1},
+};
+
+static bool parse_texture_option(StringRef &line, MTLMaterial *material, tex_map_XX &tex_map)
+{
+ line = drop_whitespace(line);
+ if (line.startswith("-o ")) {
+ line = line.drop_prefix(3);
+ line = parse_floats(line, 0.0f, tex_map.translation, 3);
+ return true;
+ }
+ if (line.startswith("-s ")) {
+ line = line.drop_prefix(3);
+ line = parse_floats(line, 1.0f, tex_map.scale, 3);
+ return true;
+ }
+ if (line.startswith("-bm ")) {
+ line = line.drop_prefix(4);
+ line = parse_float(line, 0.0f, material->map_Bump_strength);
+ return true;
+ }
+ if (line.startswith("-type ")) {
+ line = line.drop_prefix(6);
+ line = drop_whitespace(line);
+ /* Only sphere is supported. */
+ tex_map.projection_type = SHD_PROJ_SPHERE;
+ if (!line.startswith("sphere")) {
+ std::cerr << "OBJ import: only sphere MTL projection type is supported: '" << line << "'"
+ << std::endl;
+ }
+ line = drop_non_whitespace(line);
+ return true;
+ }
+ /* Check for unsupported options and skip them. */
+ for (const auto &opt : unsupported_texture_options) {
+ if (line.startswith(opt.first)) {
+ /* Drop the option name. */
+ line = line.drop_known_prefix(opt.first);
+ /* Drop the arguments. */
+ for (int i = 0; i < opt.second; ++i) {
+ line = drop_whitespace(line);
+ line = drop_non_whitespace(line);
+ }
+ return true;
}
}
- return line;
+ return false;
}
-/**
- * Fix incoming texture map line keys for variations due to other exporters.
- */
-static string fix_bad_map_keys(StringRef map_key)
+static void parse_texture_map(StringRef line, MTLMaterial *material, const char *mtl_dir_path)
{
- string new_map_key(map_key);
- if (map_key == "refl") {
- new_map_key = "map_refl";
+ bool is_map = line.startswith("map_");
+ bool is_refl = line.startswith("refl");
+ bool is_bump = line.startswith("bump");
+ if (!is_map && !is_refl && !is_bump) {
+ return;
+ }
+ eMTLSyntaxElement key = mtl_line_start_to_enum(line);
+ if (key == eMTLSyntaxElement::string || !material->texture_maps.contains(key)) {
+ /* No supported texture map found. */
+ std::cerr << "OBJ import: MTL texture map type not supported: '" << line << "'" << std::endl;
+ return;
}
- if (map_key.find("bump") != StringRef::not_found) {
- /* Handles both "bump" and "map_Bump" */
- new_map_key = "map_Bump";
+ tex_map_XX &tex_map = material->texture_maps.lookup(key);
+ tex_map.mtl_dir_path = mtl_dir_path;
+
+ /* Parse texture map options. */
+ while (parse_texture_option(line, material, tex_map)) {
}
- return new_map_key;
+
+ /* What remains is the image path. */
+ line = line.trim();
+ tex_map.image_path = line;
}
Span<std::string> OBJParser::mtl_libraries() const
@@ -503,125 +629,73 @@ MTLParser::MTLParser(StringRef mtl_library, StringRefNull obj_filepath)
BLI_split_dir_part(obj_filepath.data(), obj_file_dir, FILE_MAXDIR);
BLI_path_join(mtl_file_path_, FILE_MAX, obj_file_dir, mtl_library.data(), NULL);
BLI_split_dir_part(mtl_file_path_, mtl_dir_path_, FILE_MAXDIR);
- mtl_file_.open(mtl_file_path_);
- if (!mtl_file_.good()) {
- fprintf(stderr, "Cannot read from MTL file:'%s'\n", mtl_file_path_);
- return;
- }
}
-void MTLParser::parse_and_store(Map<string, std::unique_ptr<MTLMaterial>> &r_mtl_materials)
+void MTLParser::parse_and_store(Map<string, std::unique_ptr<MTLMaterial>> &r_materials)
{
- if (!mtl_file_.good()) {
+ size_t buffer_len;
+ void *buffer = BLI_file_read_text_as_mem(mtl_file_path_, 0, &buffer_len);
+ if (buffer == nullptr) {
+ fprintf(stderr, "OBJ import: cannot read from MTL file: '%s'\n", mtl_file_path_);
return;
}
- string line;
- MTLMaterial *current_mtlmaterial = nullptr;
+ MTLMaterial *material = nullptr;
- while (std::getline(mtl_file_, line)) {
- StringRef line_key, rest_line;
- split_line_key_rest(line, line_key, rest_line);
- if (line.empty() || rest_line.is_empty()) {
+ StringRef buffer_str{(const char *)buffer, (int64_t)buffer_len};
+ while (!buffer_str.is_empty()) {
+ StringRef line = read_next_line(buffer_str);
+ if (line.is_empty()) {
continue;
}
- /* Fix lower case/ incomplete texture map identifiers. */
- const string fixed_key = fix_bad_map_keys(line_key);
- line_key = fixed_key;
-
- if (line_key == "newmtl") {
- if (r_mtl_materials.remove_as(rest_line)) {
- std::cerr << "Duplicate material found:'" << rest_line
+ if (line.startswith("newmtl ")) {
+ line = line.drop_prefix(7);
+ if (r_materials.remove_as(line)) {
+ std::cerr << "Duplicate material found:'" << line
<< "', using the last encountered Material definition." << std::endl;
}
- current_mtlmaterial =
- r_mtl_materials.lookup_or_add(string(rest_line), std::make_unique<MTLMaterial>()).get();
- }
- else if (line_key == "Ns") {
- copy_string_to_float(rest_line, 324.0f, current_mtlmaterial->Ns);
- }
- else if (line_key == "Ka") {
- Vector<StringRef> str_ka_split;
- split_by_char(rest_line, ' ', str_ka_split);
- copy_string_to_float(str_ka_split, 0.0f, {current_mtlmaterial->Ka, 3});
- }
- else if (line_key == "Kd") {
- Vector<StringRef> str_kd_split;
- split_by_char(rest_line, ' ', str_kd_split);
- copy_string_to_float(str_kd_split, 0.8f, {current_mtlmaterial->Kd, 3});
- }
- else if (line_key == "Ks") {
- Vector<StringRef> str_ks_split;
- split_by_char(rest_line, ' ', str_ks_split);
- copy_string_to_float(str_ks_split, 0.5f, {current_mtlmaterial->Ks, 3});
- }
- else if (line_key == "Ke") {
- Vector<StringRef> str_ke_split;
- split_by_char(rest_line, ' ', str_ke_split);
- copy_string_to_float(str_ke_split, 0.0f, {current_mtlmaterial->Ke, 3});
- }
- else if (line_key == "Ni") {
- copy_string_to_float(rest_line, 1.45f, current_mtlmaterial->Ni);
- }
- else if (line_key == "d") {
- copy_string_to_float(rest_line, 1.0f, current_mtlmaterial->d);
+ material = r_materials.lookup_or_add(string(line), std::make_unique<MTLMaterial>()).get();
}
- else if (line_key == "illum") {
- copy_string_to_int(rest_line, 2, current_mtlmaterial->illum);
- }
-
- /* Parse image textures. */
- else if (line_key.find("map_") != StringRef::not_found) {
- /* TODO(@howardt): fix this. */
- eMTLSyntaxElement line_key_enum = mtl_line_key_str_to_enum(line_key);
- if (line_key_enum == eMTLSyntaxElement::string ||
- !current_mtlmaterial->texture_maps.contains_as(line_key_enum)) {
- /* No supported texture map found. */
- std::cerr << "Texture map type not supported:'" << line_key << "'" << std::endl;
- continue;
+ else if (material != nullptr) {
+ if (line.startswith("Ns ")) {
+ line = line.drop_prefix(3);
+ parse_float(line, 324.0f, material->Ns);
}
- tex_map_XX &tex_map = current_mtlmaterial->texture_maps.lookup(line_key_enum);
- Vector<StringRef> str_map_xx_split;
- split_by_char(rest_line, ' ', str_map_xx_split);
-
- /* TODO(@ankitm): use `skip_unsupported_options` for parsing these options too? */
- const int64_t pos_o{str_map_xx_split.first_index_of_try("-o")};
- if (pos_o != -1 && pos_o + 3 < str_map_xx_split.size()) {
- copy_string_to_float({str_map_xx_split[pos_o + 1],
- str_map_xx_split[pos_o + 2],
- str_map_xx_split[pos_o + 3]},
- 0.0f,
- {tex_map.translation, 3});
- }
- const int64_t pos_s{str_map_xx_split.first_index_of_try("-s")};
- if (pos_s != -1 && pos_s + 3 < str_map_xx_split.size()) {
- copy_string_to_float({str_map_xx_split[pos_s + 1],
- str_map_xx_split[pos_s + 2],
- str_map_xx_split[pos_s + 3]},
- 1.0f,
- {tex_map.scale, 3});
- }
- /* Only specific to Normal Map node. */
- const int64_t pos_bm{str_map_xx_split.first_index_of_try("-bm")};
- if (pos_bm != -1 && pos_bm + 1 < str_map_xx_split.size()) {
- copy_string_to_float(
- str_map_xx_split[pos_bm + 1], 0.0f, current_mtlmaterial->map_Bump_strength);
- }
- const int64_t pos_projection{str_map_xx_split.first_index_of_try("-type")};
- if (pos_projection != -1 && pos_projection + 1 < str_map_xx_split.size()) {
- /* Only Sphere is supported, so whatever the type is, set it to Sphere. */
- tex_map.projection_type = SHD_PROJ_SPHERE;
- if (str_map_xx_split[pos_projection + 1] != "sphere") {
- std::cerr << "Using projection type 'sphere', not:'"
- << str_map_xx_split[pos_projection + 1] << "'." << std::endl;
- }
+ else if (line.startswith("Ka ")) {
+ line = line.drop_prefix(3);
+ parse_floats(line, 0.0f, material->Ka, 3);
+ }
+ else if (line.startswith("Kd ")) {
+ line = line.drop_prefix(3);
+ parse_floats(line, 0.8f, material->Kd, 3);
+ }
+ else if (line.startswith("Ks ")) {
+ line = line.drop_prefix(3);
+ parse_floats(line, 0.5f, material->Ks, 3);
+ }
+ else if (line.startswith("Ke ")) {
+ line = line.drop_prefix(3);
+ parse_floats(line, 0.0f, material->Ke, 3);
+ }
+ else if (line.startswith("Ni ")) {
+ line = line.drop_prefix(3);
+ parse_float(line, 1.45f, material->Ni);
+ }
+ else if (line.startswith("d ")) {
+ line = line.drop_prefix(2);
+ parse_float(line, 1.0f, material->d);
+ }
+ else if (line.startswith("illum ")) {
+ line = line.drop_prefix(6);
+ parse_int(line, 2, material->illum);
+ }
+ else {
+ parse_texture_map(line, material, mtl_dir_path_);
}
-
- /* Skip all unsupported options and arguments. */
- tex_map.image_path = string(skip_unsupported_options(rest_line));
- tex_map.mtl_dir_path = mtl_dir_path_;
}
}
+
+ MEM_freeN(buffer);
}
} // namespace blender::io::obj
diff --git a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.hh b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.hh
index 24d026d75e5..8093417fcda 100644
--- a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.hh
+++ b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.hh
@@ -18,14 +18,16 @@ namespace blender::io::obj {
class OBJParser {
private:
const OBJImportParams &import_params_;
- blender::fstream obj_file_;
+ FILE *obj_file_;
Vector<std::string> mtl_libraries_;
+ size_t read_buffer_size_;
public:
/**
* Open OBJ file at the path given in import parameters.
*/
- OBJParser(const OBJImportParams &import_params);
+ OBJParser(const OBJImportParams &import_params, size_t read_buffer_size);
+ ~OBJParser();
/**
* Read the OBJ file line by line and create OBJ Geometry instances. Also store all the vertex
@@ -39,111 +41,6 @@ class OBJParser {
Span<std::string> mtl_libraries() const;
};
-enum class eOBJLineKey {
- V,
- VN,
- VT,
- F,
- L,
- CSTYPE,
- DEG,
- CURV,
- PARM,
- O,
- G,
- S,
- USEMTL,
- MTLLIB,
- COMMENT
-};
-
-constexpr eOBJLineKey line_key_str_to_enum(const std::string_view key_str)
-{
- if (key_str == "v" || key_str == "V") {
- return eOBJLineKey::V;
- }
- if (key_str == "vn" || key_str == "VN") {
- return eOBJLineKey::VN;
- }
- if (key_str == "vt" || key_str == "VT") {
- return eOBJLineKey::VT;
- }
- if (key_str == "f" || key_str == "F") {
- return eOBJLineKey::F;
- }
- if (key_str == "l" || key_str == "L") {
- return eOBJLineKey::L;
- }
- if (key_str == "cstype" || key_str == "CSTYPE") {
- return eOBJLineKey::CSTYPE;
- }
- if (key_str == "deg" || key_str == "DEG") {
- return eOBJLineKey::DEG;
- }
- if (key_str == "curv" || key_str == "CURV") {
- return eOBJLineKey::CURV;
- }
- if (key_str == "parm" || key_str == "PARM") {
- return eOBJLineKey::PARM;
- }
- if (key_str == "o" || key_str == "O") {
- return eOBJLineKey::O;
- }
- if (key_str == "g" || key_str == "G") {
- return eOBJLineKey::G;
- }
- if (key_str == "s" || key_str == "S") {
- return eOBJLineKey::S;
- }
- if (key_str == "usemtl" || key_str == "USEMTL") {
- return eOBJLineKey::USEMTL;
- }
- if (key_str == "mtllib" || key_str == "MTLLIB") {
- return eOBJLineKey::MTLLIB;
- }
- if (key_str == "#") {
- return eOBJLineKey::COMMENT;
- }
- return eOBJLineKey::COMMENT;
-}
-
-/**
- * All texture map options with number of arguments they accept.
- */
-class TextureMapOptions {
- private:
- Map<const std::string, int> tex_map_options;
-
- public:
- TextureMapOptions()
- {
- tex_map_options.add_new("-blendu", 1);
- tex_map_options.add_new("-blendv", 1);
- tex_map_options.add_new("-boost", 1);
- tex_map_options.add_new("-mm", 2);
- tex_map_options.add_new("-o", 3);
- tex_map_options.add_new("-s", 3);
- tex_map_options.add_new("-t", 3);
- tex_map_options.add_new("-texres", 1);
- tex_map_options.add_new("-clamp", 1);
- tex_map_options.add_new("-bm", 1);
- tex_map_options.add_new("-imfchan", 1);
- }
-
- /**
- * All valid option strings.
- */
- Map<const std::string, int>::KeyIterator all_options() const
- {
- return tex_map_options.keys();
- }
-
- int number_of_args(StringRef option) const
- {
- return tex_map_options.lookup_as(std::string(option));
- }
-};
-
class MTLParser {
private:
char mtl_file_path_[FILE_MAX];
@@ -151,7 +48,6 @@ class MTLParser {
* Directory in which the MTL file is found.
*/
char mtl_dir_path_[FILE_MAX];
- blender::fstream mtl_file_;
public:
/**
@@ -162,6 +58,6 @@ class MTLParser {
/**
* Read MTL file(s) and add MTLMaterial instances to the given Map reference.
*/
- void parse_and_store(Map<std::string, std::unique_ptr<MTLMaterial>> &r_mtl_materials);
+ void parse_and_store(Map<std::string, std::unique_ptr<MTLMaterial>> &r_materials);
};
} // namespace blender::io::obj
diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc
index 55b2873a3de..01a2d63927e 100644
--- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc
+++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc
@@ -18,6 +18,7 @@
#include "BLI_math_vector.h"
#include "BLI_set.hh"
+#include "IO_wavefront_obj.h"
#include "importer_mesh_utils.hh"
#include "obj_import_mesh.hh"
@@ -35,7 +36,7 @@ Object *MeshFromGeometry::create_mesh(
}
fixup_invalid_faces();
- const int64_t tot_verts_object{mesh_geometry_.vertex_indices_.size()};
+ const int64_t tot_verts_object{mesh_geometry_.vertex_count_};
/* Total explicitly imported edges, not the ones belonging the polygons to be created. */
const int64_t tot_edges{mesh_geometry_.edges_.size()};
const int64_t tot_face_elems{mesh_geometry_.face_elements_.size()};
@@ -52,11 +53,13 @@ Object *MeshFromGeometry::create_mesh(
create_normals(mesh);
create_materials(bmain, materials, created_materials, obj);
- bool verbose_validate = false;
+ if (import_params.validate_meshes || mesh_geometry_.has_invalid_polys_) {
+ bool verbose_validate = false;
#ifdef DEBUG
- verbose_validate = true;
+ verbose_validate = true;
#endif
- BKE_mesh_validate(mesh, verbose_validate, false);
+ BKE_mesh_validate(mesh, verbose_validate, false);
+ }
transform_object(obj, import_params);
/* FIXME: after 2.80; `mesh->flag` isn't copied by #BKE_mesh_nomain_to_mesh() */
@@ -73,9 +76,9 @@ void MeshFromGeometry::fixup_invalid_faces()
for (int64_t face_idx = 0; face_idx < mesh_geometry_.face_elements_.size(); ++face_idx) {
const PolyElem &curr_face = mesh_geometry_.face_elements_[face_idx];
- if (curr_face.face_corners.size() < 3) {
+ if (curr_face.corner_count_ < 3) {
/* Skip and remove faces that have fewer than 3 corners. */
- mesh_geometry_.total_loops_ -= curr_face.face_corners.size();
+ mesh_geometry_.total_loops_ -= curr_face.corner_count_;
mesh_geometry_.face_elements_.remove_and_reorder(face_idx);
continue;
}
@@ -84,12 +87,14 @@ void MeshFromGeometry::fixup_invalid_faces()
* basically whether it has duplicate vertex indices. */
bool valid = true;
Set<int, 8> used_verts;
- for (const PolyCorner &corner : curr_face.face_corners) {
- if (used_verts.contains(corner.vert_index)) {
+ for (int i = 0; i < curr_face.corner_count_; ++i) {
+ int corner_idx = curr_face.start_index_ + i;
+ int vertex_idx = mesh_geometry_.face_corners_[corner_idx].vert_index;
+ if (used_verts.contains(vertex_idx)) {
valid = false;
break;
}
- used_verts.add(corner.vert_index);
+ used_verts.add(vertex_idx);
}
if (valid) {
continue;
@@ -100,20 +105,22 @@ void MeshFromGeometry::fixup_invalid_faces()
Vector<int, 8> face_verts;
Vector<int, 8> face_uvs;
Vector<int, 8> face_normals;
- face_verts.reserve(curr_face.face_corners.size());
- face_uvs.reserve(curr_face.face_corners.size());
- face_normals.reserve(curr_face.face_corners.size());
- for (const PolyCorner &corner : curr_face.face_corners) {
+ face_verts.reserve(curr_face.corner_count_);
+ face_uvs.reserve(curr_face.corner_count_);
+ face_normals.reserve(curr_face.corner_count_);
+ for (int i = 0; i < curr_face.corner_count_; ++i) {
+ int corner_idx = curr_face.start_index_ + i;
+ const PolyCorner &corner = mesh_geometry_.face_corners_[corner_idx];
face_verts.append(corner.vert_index);
face_normals.append(corner.vertex_normal_index);
face_uvs.append(corner.uv_vert_index);
}
- std::string face_vertex_group = curr_face.vertex_group;
- std::string face_material_name = curr_face.material_name;
+ int face_vertex_group = curr_face.vertex_group_index;
+ int face_material = curr_face.material_index;
bool face_shaded_smooth = curr_face.shaded_smooth;
/* Remove the invalid face. */
- mesh_geometry_.total_loops_ -= curr_face.face_corners.size();
+ mesh_geometry_.total_loops_ -= curr_face.corner_count_;
mesh_geometry_.face_elements_.remove_and_reorder(face_idx);
Vector<Vector<int>> new_faces = fixup_invalid_polygon(global_vertices_.vertices, face_verts);
@@ -124,13 +131,14 @@ void MeshFromGeometry::fixup_invalid_faces()
continue;
}
PolyElem new_face{};
- new_face.vertex_group = face_vertex_group;
- new_face.material_name = face_material_name;
+ new_face.vertex_group_index = face_vertex_group;
+ new_face.material_index = face_material;
new_face.shaded_smooth = face_shaded_smooth;
- new_face.face_corners.reserve(face.size());
+ new_face.start_index_ = mesh_geometry_.face_corners_.size();
+ new_face.corner_count_ = face.size();
for (int idx : face) {
BLI_assert(idx >= 0 && idx < face_verts.size());
- new_face.face_corners.append({face_verts[idx], face_uvs[idx], face_normals[idx]});
+ mesh_geometry_.face_corners_.append({face_verts[idx], face_uvs[idx], face_normals[idx]});
}
mesh_geometry_.face_elements_.append(new_face);
mesh_geometry_.total_loops_ += face.size();
@@ -140,13 +148,14 @@ void MeshFromGeometry::fixup_invalid_faces()
void MeshFromGeometry::create_vertices(Mesh *mesh)
{
- const int64_t tot_verts_object{mesh_geometry_.vertex_indices_.size()};
+ const int tot_verts_object{mesh_geometry_.vertex_count_};
for (int i = 0; i < tot_verts_object; ++i) {
- if (mesh_geometry_.vertex_indices_[i] < global_vertices_.vertices.size()) {
- copy_v3_v3(mesh->mvert[i].co, global_vertices_.vertices[mesh_geometry_.vertex_indices_[i]]);
+ int vi = mesh_geometry_.vertex_start_ + i;
+ if (vi < global_vertices_.vertices.size()) {
+ copy_v3_v3(mesh->mvert[i].co, global_vertices_.vertices[vi]);
}
else {
- std::cerr << "Vertex index:" << mesh_geometry_.vertex_indices_[i]
+ std::cerr << "Vertex index:" << vi
<< " larger than total vertices:" << global_vertices_.vertices.size() << " ."
<< std::endl;
}
@@ -158,7 +167,7 @@ void MeshFromGeometry::create_polys_loops(Object *obj, Mesh *mesh)
/* Will not be used if vertex groups are not imported. */
mesh->dvert = nullptr;
float weight = 0.0f;
- const int64_t total_verts = mesh_geometry_.vertex_indices_.size();
+ const int64_t total_verts = mesh_geometry_.vertex_count_;
if (total_verts && mesh_geometry_.use_vertex_groups_) {
mesh->dvert = static_cast<MDeformVert *>(
CustomData_add_layer(&mesh->vdata, CD_MDEFORMVERT, CD_CALLOC, nullptr, total_verts));
@@ -168,34 +177,32 @@ void MeshFromGeometry::create_polys_loops(Object *obj, Mesh *mesh)
UNUSED_VARS(weight);
}
- /* Do not remove elements from the VectorSet since order of insertion is required.
- * StringRef is fine since per-face deform group name outlives the VectorSet. */
- VectorSet<StringRef> group_names;
const int64_t tot_face_elems{mesh->totpoly};
int tot_loop_idx = 0;
for (int poly_idx = 0; poly_idx < tot_face_elems; ++poly_idx) {
const PolyElem &curr_face = mesh_geometry_.face_elements_[poly_idx];
- if (curr_face.face_corners.size() < 3) {
+ if (curr_face.corner_count_ < 3) {
/* Don't add single vertex face, or edges. */
std::cerr << "Face with less than 3 vertices found, skipping." << std::endl;
continue;
}
MPoly &mpoly = mesh->mpoly[poly_idx];
- mpoly.totloop = curr_face.face_corners.size();
+ mpoly.totloop = curr_face.corner_count_;
mpoly.loopstart = tot_loop_idx;
if (curr_face.shaded_smooth) {
mpoly.flag |= ME_SMOOTH;
}
- mpoly.mat_nr = mesh_geometry_.material_names_.index_of_try(curr_face.material_name);
+ mpoly.mat_nr = curr_face.material_index;
/* Importing obj files without any materials would result in negative indices, which is not
* supported. */
if (mpoly.mat_nr < 0) {
mpoly.mat_nr = 0;
}
- for (const PolyCorner &curr_corner : curr_face.face_corners) {
+ for (int idx = 0; idx < curr_face.corner_count_; ++idx) {
+ const PolyCorner &curr_corner = mesh_geometry_.face_corners_[curr_face.start_index_ + idx];
MLoop &mloop = mesh->mloop[tot_loop_idx];
tot_loop_idx++;
mloop.v = curr_corner.vert_index;
@@ -212,23 +219,17 @@ void MeshFromGeometry::create_polys_loops(Object *obj, Mesh *mesh)
MEM_callocN(sizeof(MDeformWeight), "OBJ Import Deform Weight"));
}
/* Every vertex in a face is assigned the same deform group. */
- int64_t pos_name{group_names.index_of_try(curr_face.vertex_group)};
- if (pos_name == -1) {
- group_names.add_new(curr_face.vertex_group);
- pos_name = group_names.size() - 1;
- }
- BLI_assert(pos_name >= 0);
+ int group_idx = curr_face.vertex_group_index;
/* Deform group number (def_nr) must behave like an index into the names' list. */
- *(def_vert.dw) = {static_cast<unsigned int>(pos_name), weight};
+ *(def_vert.dw) = {static_cast<unsigned int>(group_idx), weight};
}
}
if (!mesh->dvert) {
return;
}
- /* Add deform group(s) to the object's defbase. */
- for (StringRef name : group_names) {
- /* Adding groups in this order assumes that def_nr is an index into the names' list. */
+ /* Add deform group names. */
+ for (const std::string &name : mesh_geometry_.group_order_) {
BKE_object_defgroup_add_name(obj, name.data());
}
}
@@ -236,7 +237,7 @@ void MeshFromGeometry::create_polys_loops(Object *obj, Mesh *mesh)
void MeshFromGeometry::create_edges(Mesh *mesh)
{
const int64_t tot_edges{mesh_geometry_.edges_.size()};
- const int64_t total_verts{mesh_geometry_.vertex_indices_.size()};
+ const int64_t total_verts{mesh_geometry_.vertex_count_};
UNUSED_VARS_NDEBUG(total_verts);
for (int i = 0; i < tot_edges; ++i) {
const MEdge &src_edge = mesh_geometry_.edges_[i];
@@ -263,7 +264,8 @@ void MeshFromGeometry::create_uv_verts(Mesh *mesh)
int tot_loop_idx = 0;
for (const PolyElem &curr_face : mesh_geometry_.face_elements_) {
- for (const PolyCorner &curr_corner : curr_face.face_corners) {
+ for (int idx = 0; idx < curr_face.corner_count_; ++idx) {
+ const PolyCorner &curr_corner = mesh_geometry_.face_corners_[curr_face.start_index_ + idx];
if (curr_corner.uv_vert_index >= 0 &&
curr_corner.uv_vert_index < global_vertices_.uv_vertices.size()) {
const float2 &mluv_src = global_vertices_.uv_vertices[curr_corner.uv_vert_index];
@@ -317,7 +319,7 @@ void MeshFromGeometry::create_materials(
Map<std::string, Material *> &created_materials,
Object *obj)
{
- for (const std::string &name : mesh_geometry_.material_names_) {
+ for (const std::string &name : mesh_geometry_.material_order_) {
Material *mat = get_or_create_material(bmain, name, materials, created_materials);
if (mat == nullptr) {
continue;
@@ -340,7 +342,8 @@ void MeshFromGeometry::create_normals(Mesh *mesh)
MEM_malloc_arrayN(mesh_geometry_.total_loops_, sizeof(float[3]), __func__));
int tot_loop_idx = 0;
for (const PolyElem &curr_face : mesh_geometry_.face_elements_) {
- for (const PolyCorner &curr_corner : curr_face.face_corners) {
+ for (int idx = 0; idx < curr_face.corner_count_; ++idx) {
+ const PolyCorner &curr_corner = mesh_geometry_.face_corners_[curr_face.start_index_ + idx];
int n_index = curr_corner.vertex_normal_index;
float3 normal(0, 0, 0);
if (n_index >= 0) {
diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.hh b/source/blender/io/wavefront_obj/importer/obj_import_mesh.hh
index 2a838215421..7cc7ed25ad1 100644
--- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.hh
+++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.hh
@@ -45,11 +45,8 @@ class MeshFromGeometry : NonMovable, NonCopyable {
void fixup_invalid_faces();
void create_vertices(Mesh *mesh);
/**
- * Create polygons for the Mesh, set smooth shading flag, deform group name,
- * assigned material also.
- *
- * It must receive all polygons to be added to the mesh.
- * Remove holes from polygons before * calling this.
+ * Create polygons for the Mesh, set smooth shading flags, deform group names,
+ * Materials.
*/
void create_polys_loops(Object *obj, Mesh *mesh);
/**
diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc b/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc
index 88a8c07e325..f2a8941e8a7 100644
--- a/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc
+++ b/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc
@@ -13,12 +13,13 @@
#include "DNA_material_types.h"
#include "DNA_node_types.h"
+#include "IO_string_utils.hh"
+
#include "NOD_shader.h"
/* TODO: move eMTLSyntaxElement out of following file into a more neutral place */
#include "obj_export_io.hh"
#include "obj_import_mtl.hh"
-#include "parser_string_utils.hh"
namespace blender::io::obj {
@@ -96,13 +97,16 @@ static bool load_texture_image(Main *bmain, const tex_map_XX &tex_map, bNode *r_
return true;
}
/* Try removing quotes. */
- std::string no_quote_path{replace_all_occurences(tex_path, "\"", "")};
+ std::string no_quote_path{tex_path};
+ auto end_pos = std::remove(no_quote_path.begin(), no_quote_path.end(), '"');
+ no_quote_path.erase(end_pos, no_quote_path.end());
if (no_quote_path != tex_path &&
load_texture_image_at_path(bmain, tex_map, r_node, no_quote_path)) {
return true;
}
/* Try replacing underscores with spaces. */
- std::string no_underscore_path{replace_all_occurences(no_quote_path, "_", " ")};
+ std::string no_underscore_path{no_quote_path};
+ std::replace(no_underscore_path.begin(), no_underscore_path.end(), '_', ' ');
if (no_underscore_path != no_quote_path && no_underscore_path != tex_path &&
load_texture_image_at_path(bmain, tex_map, r_node, no_underscore_path)) {
return true;
diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mtl.hh b/source/blender/io/wavefront_obj/importer/obj_import_mtl.hh
index 4b7827b2035..74bc9f21bc4 100644
--- a/source/blender/io/wavefront_obj/importer/obj_import_mtl.hh
+++ b/source/blender/io/wavefront_obj/importer/obj_import_mtl.hh
@@ -90,29 +90,4 @@ class ShaderNodetreeWrap {
void add_image_textures(Main *bmain, Material *mat);
};
-constexpr eMTLSyntaxElement mtl_line_key_str_to_enum(const std::string_view key_str)
-{
- if (key_str == "map_Kd") {
- return eMTLSyntaxElement::map_Kd;
- }
- if (key_str == "map_Ks") {
- return eMTLSyntaxElement::map_Ks;
- }
- if (key_str == "map_Ns") {
- return eMTLSyntaxElement::map_Ns;
- }
- if (key_str == "map_d") {
- return eMTLSyntaxElement::map_d;
- }
- if (key_str == "refl" || key_str == "map_refl") {
- return eMTLSyntaxElement::map_refl;
- }
- if (key_str == "map_Ke") {
- return eMTLSyntaxElement::map_Ke;
- }
- if (key_str == "map_Bump" || key_str == "bump") {
- return eMTLSyntaxElement::map_Bump;
- }
- return eMTLSyntaxElement::string;
-}
} // namespace blender::io::obj
diff --git a/source/blender/io/wavefront_obj/importer/obj_import_objects.hh b/source/blender/io/wavefront_obj/importer/obj_import_objects.hh
index c6ce7d3c434..b67ba46af03 100644
--- a/source/blender/io/wavefront_obj/importer/obj_import_objects.hh
+++ b/source/blender/io/wavefront_obj/importer/obj_import_objects.hh
@@ -8,6 +8,7 @@
#include "BKE_lib_id.h"
+#include "BLI_map.hh"
#include "BLI_math_vec_types.hh"
#include "BLI_vector.hh"
#include "BLI_vector_set.hh"
@@ -61,10 +62,11 @@ struct PolyCorner {
};
struct PolyElem {
- std::string vertex_group;
- std::string material_name;
+ int vertex_group_index = -1;
+ int material_index = -1;
bool shaded_smooth = false;
- Vector<PolyCorner> face_corners;
+ int start_index_ = 0;
+ int corner_count_ = 0;
};
/**
@@ -93,15 +95,20 @@ enum eGeometryType {
struct Geometry {
eGeometryType geom_type_ = GEOM_MESH;
std::string geometry_name_;
- VectorSet<std::string> material_names_;
- /**
- * Indices in the vector range from zero to total vertices in a geometry.
- * Values range from zero to total coordinates in the global list.
- */
- Vector<int> vertex_indices_;
+ Map<std::string, int> group_indices_;
+ Vector<std::string> group_order_;
+ Map<std::string, int> material_indices_;
+ Vector<std::string> material_order_;
+
+ int vertex_start_ = 0;
+ int vertex_count_ = 0;
/** Edges written in the file in addition to (or even without polygon) elements. */
Vector<MEdge> edges_;
+
+ Vector<PolyCorner> face_corners_;
Vector<PolyElem> face_elements_;
+
+ bool has_invalid_polys_ = false;
bool has_vertex_normals_ = false;
bool use_vertex_groups_ = false;
NurbsElement nurbs_element_;
diff --git a/source/blender/io/wavefront_obj/importer/obj_importer.cc b/source/blender/io/wavefront_obj/importer/obj_importer.cc
index 631ddcc5cf4..c21d2d9583c 100644
--- a/source/blender/io/wavefront_obj/importer/obj_importer.cc
+++ b/source/blender/io/wavefront_obj/importer/obj_importer.cc
@@ -42,6 +42,12 @@ static void geometry_to_blender_objects(
BKE_view_layer_base_deselect_all(view_layer);
LayerCollection *lc = BKE_layer_collection_get_active(view_layer);
+ /* Don't do collection syncs for each object, will do once after the loop. */
+ BKE_layer_collection_resync_forbid();
+
+ /* Create all the objects. */
+ Vector<Object *> objects;
+ objects.reserve(all_geometries.size());
for (const std::unique_ptr<Geometry> &geometry : all_geometries) {
Object *obj = nullptr;
if (geometry->geom_type_ == GEOM_MESH) {
@@ -54,17 +60,25 @@ static void geometry_to_blender_objects(
}
if (obj != nullptr) {
BKE_collection_object_add(bmain, lc->collection, obj);
- Base *base = BKE_view_layer_base_find(view_layer, obj);
- /* TODO: is setting active needed? */
- BKE_view_layer_base_select_and_set_active(view_layer, base);
-
- DEG_id_tag_update(&lc->collection->id, ID_RECALC_COPY_ON_WRITE);
- DEG_id_tag_update_ex(bmain,
- &obj->id,
- ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION |
- ID_RECALC_BASE_FLAGS);
+ objects.append(obj);
}
}
+
+ /* Sync the collection after all objects are created. */
+ BKE_layer_collection_resync_allow();
+ BKE_main_collection_sync(bmain);
+
+ /* After collection sync, select objects in the view layer and do DEG updates. */
+ for (Object *obj : objects) {
+ Base *base = BKE_view_layer_base_find(view_layer, obj);
+ BKE_view_layer_base_select_and_set_active(view_layer, base);
+
+ DEG_id_tag_update(&lc->collection->id, ID_RECALC_COPY_ON_WRITE);
+ int flags = ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION |
+ ID_RECALC_BASE_FLAGS;
+ DEG_id_tag_update_ex(bmain, &obj->id, flags);
+ }
+
DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
DEG_relations_tag_update(bmain);
}
@@ -81,7 +95,8 @@ void importer_main(bContext *C, const OBJImportParams &import_params)
void importer_main(Main *bmain,
Scene *scene,
ViewLayer *view_layer,
- const OBJImportParams &import_params)
+ const OBJImportParams &import_params,
+ size_t read_buffer_size)
{
/* List of Geometry instances to be parsed from OBJ file. */
Vector<std::unique_ptr<Geometry>> all_geometries;
@@ -91,7 +106,7 @@ void importer_main(Main *bmain,
Map<std::string, std::unique_ptr<MTLMaterial>> materials;
Map<std::string, Material *> created_materials;
- OBJParser obj_parser{import_params};
+ OBJParser obj_parser{import_params, read_buffer_size};
obj_parser.parse(all_geometries, global_vertices);
for (StringRef mtl_library : obj_parser.mtl_libraries()) {
diff --git a/source/blender/io/wavefront_obj/importer/obj_importer.hh b/source/blender/io/wavefront_obj/importer/obj_importer.hh
index fd83117ebc6..35f401d7cb0 100644
--- a/source/blender/io/wavefront_obj/importer/obj_importer.hh
+++ b/source/blender/io/wavefront_obj/importer/obj_importer.hh
@@ -17,6 +17,7 @@ void importer_main(bContext *C, const OBJImportParams &import_params);
void importer_main(Main *bmain,
Scene *scene,
ViewLayer *view_layer,
- const OBJImportParams &import_params);
+ const OBJImportParams &import_params,
+ size_t read_buffer_size = 64 * 1024);
} // namespace blender::io::obj
diff --git a/source/blender/io/wavefront_obj/importer/parser_string_utils.cc b/source/blender/io/wavefront_obj/importer/parser_string_utils.cc
deleted file mode 100644
index 6671a86f5ee..00000000000
--- a/source/blender/io/wavefront_obj/importer/parser_string_utils.cc
+++ /dev/null
@@ -1,174 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#include <fstream>
-#include <iostream>
-#include <sstream>
-
-#include "BLI_math_vec_types.hh"
-#include "BLI_span.hh"
-#include "BLI_string_ref.hh"
-#include "BLI_vector.hh"
-
-#include "parser_string_utils.hh"
-
-/* Note: these OBJ parser helper functions are planned to get fairly large
- * changes "soon", so don't read too much into current implementation... */
-
-namespace blender::io::obj {
-using std::string;
-
-void read_next_line(std::fstream &file, string &r_line)
-{
- std::string new_line;
- while (file.good() && !r_line.empty() && r_line.back() == '\\') {
- new_line.clear();
- const bool ok = static_cast<bool>(std::getline(file, new_line));
- /* Remove the last backslash character. */
- r_line.pop_back();
- r_line.append(new_line);
- if (!ok || new_line.empty()) {
- return;
- }
- }
-}
-
-void split_line_key_rest(const StringRef line, StringRef &r_line_key, StringRef &r_rest_line)
-{
- if (line.is_empty()) {
- return;
- }
-
- const int64_t pos_split{line.find_first_of(' ')};
- if (pos_split == StringRef::not_found) {
- /* Use the first character if no space is found in the line. It's usually a comment like:
- * #This is a comment. */
- r_line_key = line.substr(0, 1);
- }
- else {
- r_line_key = line.substr(0, pos_split);
- }
-
- /* Eat the delimiter also using "+ 1". */
- r_rest_line = line.drop_prefix(r_line_key.size() + 1);
- if (r_rest_line.is_empty()) {
- return;
- }
-
- /* Remove any leading spaces, trailing spaces & \r character, if any. */
- const int64_t leading_space{r_rest_line.find_first_not_of(' ')};
- if (leading_space != StringRef::not_found) {
- r_rest_line = r_rest_line.drop_prefix(leading_space);
- }
-
- /* Another way is to do a test run before the actual parsing to find the newline
- * character and use it in the getline. */
- const int64_t carriage_return{r_rest_line.find_first_of('\r')};
- if (carriage_return != StringRef::not_found) {
- r_rest_line = r_rest_line.substr(0, carriage_return + 1);
- }
-
- const int64_t trailing_space{r_rest_line.find_last_not_of(' ')};
- if (trailing_space != StringRef::not_found) {
- /* The position is of a character that is not ' ', so count of characters is position + 1. */
- r_rest_line = r_rest_line.substr(0, trailing_space + 1);
- }
-}
-
-void split_by_char(StringRef in_string, const char delimiter, Vector<StringRef> &r_out_list)
-{
- r_out_list.clear();
-
- while (!in_string.is_empty()) {
- const int64_t pos_delim{in_string.find_first_of(delimiter)};
- const int64_t word_len = pos_delim == StringRef::not_found ? in_string.size() : pos_delim;
-
- StringRef word{in_string.data(), word_len};
- if (!word.is_empty() && !(word == " " && !(word[0] == '\0'))) {
- r_out_list.append(word);
- }
- if (pos_delim == StringRef::not_found) {
- return;
- }
- /* Skip the word already stored. */
- in_string = in_string.drop_prefix(word_len);
- /* Skip all delimiters. */
- const int64_t pos_non_delim = in_string.find_first_not_of(delimiter);
- if (pos_non_delim == StringRef::not_found) {
- return;
- }
- in_string = in_string.drop_prefix(std::min(pos_non_delim, in_string.size()));
- }
-}
-
-void copy_string_to_float(StringRef src, const float fallback_value, float &r_dst)
-{
- try {
- r_dst = std::stof(string(src));
- }
- catch (const std::invalid_argument &inv_arg) {
- std::cerr << "Bad conversion to float:'" << inv_arg.what() << "':'" << src << "'" << std::endl;
- r_dst = fallback_value;
- }
- catch (const std::out_of_range &out_of_range) {
- std::cerr << "Out of range for float:'" << out_of_range.what() << ":'" << src << "'"
- << std::endl;
- r_dst = fallback_value;
- }
-}
-
-void copy_string_to_float(Span<StringRef> src,
- const float fallback_value,
- MutableSpan<float> r_dst)
-{
- for (int i = 0; i < r_dst.size(); ++i) {
- if (i < src.size()) {
- copy_string_to_float(src[i], fallback_value, r_dst[i]);
- }
- else {
- r_dst[i] = fallback_value;
- }
- }
-}
-
-void copy_string_to_int(StringRef src, const int fallback_value, int &r_dst)
-{
- try {
- r_dst = std::stoi(string(src));
- }
- catch (const std::invalid_argument &inv_arg) {
- std::cerr << "Bad conversion to int:'" << inv_arg.what() << "':'" << src << "'" << std::endl;
- r_dst = fallback_value;
- }
- catch (const std::out_of_range &out_of_range) {
- std::cerr << "Out of range for int:'" << out_of_range.what() << ":'" << src << "'"
- << std::endl;
- r_dst = fallback_value;
- }
-}
-
-void copy_string_to_int(Span<StringRef> src, const int fallback_value, MutableSpan<int> r_dst)
-{
- for (int i = 0; i < r_dst.size(); ++i) {
- if (i < src.size()) {
- copy_string_to_int(src[i], fallback_value, r_dst[i]);
- }
- else {
- r_dst[i] = fallback_value;
- }
- }
-}
-
-std::string replace_all_occurences(StringRef original, StringRef to_remove, StringRef to_add)
-{
- std::string clean{original};
- while (true) {
- const std::string::size_type pos = clean.find(to_remove);
- if (pos == std::string::npos) {
- break;
- }
- clean.replace(pos, to_add.size(), to_add);
- }
- return clean;
-}
-
-} // namespace blender::io::obj
diff --git a/source/blender/io/wavefront_obj/importer/parser_string_utils.hh b/source/blender/io/wavefront_obj/importer/parser_string_utils.hh
deleted file mode 100644
index 62cfbebccf3..00000000000
--- a/source/blender/io/wavefront_obj/importer/parser_string_utils.hh
+++ /dev/null
@@ -1,54 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-namespace blender::io::obj {
-
-/* Note: these OBJ parser helper functions are planned to get fairly large
- * changes "soon", so don't read too much into current implementation... */
-
-/**
- * Store multiple lines separated by an escaped newline character: `\\n`.
- * Use this before doing any parse operations on the read string.
- */
-void read_next_line(std::fstream &file, std::string &r_line);
-/**
- * Split a line string into the first word (key) and the rest of the line.
- * Also remove leading & trailing spaces as well as `\r` carriage return
- * character if present.
- */
-void split_line_key_rest(StringRef line, StringRef &r_line_key, StringRef &r_rest_line);
-/**
- * Split the given string by the delimiter and fill the given vector.
- * If an intermediate string is empty, or space or null character, it is not appended to the
- * vector.
- */
-void split_by_char(StringRef in_string, const char delimiter, Vector<StringRef> &r_out_list);
-/**
- * Convert the given string to float and assign it to the destination value.
- *
- * If the string cannot be converted to a float, the fallback value is used.
- */
-void copy_string_to_float(StringRef src, const float fallback_value, float &r_dst);
-/**
- * Convert all members of the Span of strings to floats and assign them to the float
- * array members. Usually used for values like coordinates.
- *
- * If a string cannot be converted to a float, the fallback value is used.
- */
-void copy_string_to_float(Span<StringRef> src,
- const float fallback_value,
- MutableSpan<float> r_dst);
-/**
- * Convert the given string to int and assign it to the destination value.
- *
- * If the string cannot be converted to an integer, the fallback value is used.
- */
-void copy_string_to_int(StringRef src, const int fallback_value, int &r_dst);
-/**
- * Convert the given strings to ints and fill the destination int buffer.
- *
- * If a string cannot be converted to an integer, the fallback value is used.
- */
-void copy_string_to_int(Span<StringRef> src, const int fallback_value, MutableSpan<int> r_dst);
-std::string replace_all_occurences(StringRef original, StringRef to_remove, StringRef to_add);
-
-} // namespace blender::io::obj
diff --git a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc
index 8599b38d55b..f74bfc155dd 100644
--- a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc
+++ b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc
@@ -48,7 +48,6 @@ class obj_exporter_test : public BlendfileLoadingBaseTest {
};
const std::string all_objects_file = "io_tests/blend_scene/all_objects.blend";
-const std::string all_curve_objects_file = "io_tests/blend_scene/all_curves.blend";
TEST_F(obj_exporter_test, filter_objects_curves_as_mesh)
{
@@ -58,7 +57,7 @@ TEST_F(obj_exporter_test, filter_objects_curves_as_mesh)
return;
}
auto [objmeshes, objcurves]{filter_supported_objects(depsgraph, _export.params)};
- EXPECT_EQ(objmeshes.size(), 19);
+ EXPECT_EQ(objmeshes.size(), 20);
EXPECT_EQ(objcurves.size(), 0);
}
@@ -72,7 +71,7 @@ TEST_F(obj_exporter_test, filter_objects_curves_as_nurbs)
_export.params.export_curves_as_nurbs = true;
auto [objmeshes, objcurves]{filter_supported_objects(depsgraph, _export.params)};
EXPECT_EQ(objmeshes.size(), 18);
- EXPECT_EQ(objcurves.size(), 2);
+ EXPECT_EQ(objcurves.size(), 3);
}
TEST_F(obj_exporter_test, filter_objects_selected)
@@ -111,64 +110,6 @@ TEST(obj_exporter_utils, append_positive_frame_to_filename)
EXPECT_EQ_ARRAY(path_with_frame, path_truth, BLI_strlen_utf8(path_truth));
}
-TEST_F(obj_exporter_test, curve_nurbs_points)
-{
- if (!load_file_and_depsgraph(all_curve_objects_file)) {
- ADD_FAILURE();
- return;
- }
-
- OBJExportParamsDefault _export;
- _export.params.export_curves_as_nurbs = true;
- auto [objmeshes_unused, objcurves]{filter_supported_objects(depsgraph, _export.params)};
-
- for (auto &objcurve : objcurves) {
- if (all_nurbs_truth.count(objcurve->get_curve_name()) != 1) {
- ADD_FAILURE();
- return;
- }
- const NurbsObject *const nurbs_truth = all_nurbs_truth.at(objcurve->get_curve_name()).get();
- EXPECT_EQ(objcurve->total_splines(), nurbs_truth->total_splines());
- for (int spline_index : IndexRange(objcurve->total_splines())) {
- EXPECT_EQ(objcurve->total_spline_vertices(spline_index),
- nurbs_truth->total_spline_vertices(spline_index));
- EXPECT_EQ(objcurve->get_nurbs_degree(spline_index),
- nurbs_truth->get_nurbs_degree(spline_index));
- EXPECT_EQ(objcurve->total_spline_control_points(spline_index),
- nurbs_truth->total_spline_control_points(spline_index));
- }
- }
-}
-
-TEST_F(obj_exporter_test, curve_coordinates)
-{
- if (!load_file_and_depsgraph(all_curve_objects_file)) {
- ADD_FAILURE();
- return;
- }
-
- OBJExportParamsDefault _export;
- _export.params.export_curves_as_nurbs = true;
- auto [objmeshes_unused, objcurves]{filter_supported_objects(depsgraph, _export.params)};
-
- for (auto &objcurve : objcurves) {
- if (all_nurbs_truth.count(objcurve->get_curve_name()) != 1) {
- ADD_FAILURE();
- return;
- }
- const NurbsObject *const nurbs_truth = all_nurbs_truth.at(objcurve->get_curve_name()).get();
- EXPECT_EQ(objcurve->total_splines(), nurbs_truth->total_splines());
- for (int spline_index : IndexRange(objcurve->total_splines())) {
- for (int vertex_index : IndexRange(objcurve->total_spline_vertices(spline_index))) {
- EXPECT_V3_NEAR(objcurve->vertex_coordinates(
- spline_index, vertex_index, _export.params.scaling_factor),
- nurbs_truth->vertex_coordinates(spline_index, vertex_index),
- 0.000001f);
- }
- }
- }
-}
-
static std::unique_ptr<OBJWriter> init_writer(const OBJExportParams &params,
const std::string out_filepath)
{
@@ -467,6 +408,19 @@ TEST_F(obj_exporter_regression_test, cube_normal_edit)
_export.params);
}
+TEST_F(obj_exporter_regression_test, cube_vertex_groups)
+{
+ OBJExportParamsDefault _export;
+ _export.params.export_materials = false;
+ _export.params.export_normals = false;
+ _export.params.export_uv = false;
+ _export.params.export_vertex_groups = true;
+ compare_obj_export_to_golden("io_tests/blend_geometry/cube_vertex_groups.blend",
+ "io_tests/obj/cube_vertex_groups.obj",
+ "",
+ _export.params);
+}
+
TEST_F(obj_exporter_regression_test, cubes_positioned)
{
OBJExportParamsDefault _export;
@@ -504,6 +458,25 @@ TEST_F(obj_exporter_regression_test, suzanne_all_data)
_export.params);
}
+TEST_F(obj_exporter_regression_test, all_curves)
+{
+ OBJExportParamsDefault _export;
+ _export.params.export_materials = false;
+ compare_obj_export_to_golden(
+ "io_tests/blend_scene/all_curves.blend", "io_tests/obj/all_curves.obj", "", _export.params);
+}
+
+TEST_F(obj_exporter_regression_test, all_curves_as_nurbs)
+{
+ OBJExportParamsDefault _export;
+ _export.params.export_materials = false;
+ _export.params.export_curves_as_nurbs = true;
+ compare_obj_export_to_golden("io_tests/blend_scene/all_curves.blend",
+ "io_tests/obj/all_curves_as_nurbs.obj",
+ "",
+ _export.params);
+}
+
TEST_F(obj_exporter_regression_test, all_objects)
{
OBJExportParamsDefault _export;
diff --git a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh
index 42660bbbe56..6a821e0b1bf 100644
--- a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh
+++ b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh
@@ -1,77 +1,11 @@
/* SPDX-License-Identifier: Apache-2.0 */
-/**
- * This file contains default values for several items like
- * vertex coordinates, export parameters, MTL values etc.
- */
-
#pragma once
-#include <array>
-#include <gtest/gtest.h>
-#include <string>
-#include <vector>
-
#include "IO_wavefront_obj.h"
namespace blender::io::obj {
-using array_float_3 = std::array<float, 3>;
-
-/**
- * This matches #OBJCurve's member functions, except that all the numbers and names are known
- * constants. Used to store expected values of NURBS curves objects.
- */
-class NurbsObject {
- private:
- std::string nurbs_name_;
- /* The indices in these vectors are spline indices. */
- std::vector<std::vector<array_float_3>> coordinates_;
- std::vector<int> degrees_;
- std::vector<int> control_points_;
-
- public:
- NurbsObject(const std::string nurbs_name,
- const std::vector<std::vector<array_float_3>> coordinates,
- const std::vector<int> degrees,
- const std::vector<int> control_points)
- : nurbs_name_(nurbs_name),
- coordinates_(coordinates),
- degrees_(degrees),
- control_points_(control_points)
- {
- }
-
- int total_splines() const
- {
- return coordinates_.size();
- }
-
- int total_spline_vertices(const int spline_index) const
- {
- if (spline_index >= coordinates_.size()) {
- ADD_FAILURE();
- return 0;
- }
- return coordinates_[spline_index].size();
- }
-
- const float *vertex_coordinates(const int spline_index, const int vertex_index) const
- {
- return coordinates_[spline_index][vertex_index].data();
- }
-
- int get_nurbs_degree(const int spline_index) const
- {
- return degrees_[spline_index];
- }
-
- int total_spline_control_points(const int spline_index) const
- {
- return control_points_[spline_index];
- }
-};
-
struct OBJExportParamsDefault {
OBJExportParams params;
OBJExportParamsDefault()
@@ -103,48 +37,4 @@ struct OBJExportParamsDefault {
}
};
-const std::vector<std::vector<array_float_3>> coordinates_NurbsCurve{
- {{6.94742, 0.000000, 0.000000},
- {7.44742, 0.000000, -1.000000},
- {9.44742, 0.000000, -1.000000},
- {9.94742, 0.000000, 0.000000}}};
-const std::vector<std::vector<array_float_3>> coordinates_NurbsCircle{
- {{11.463165, 0.000000, 1.000000},
- {10.463165, 0.000000, 1.000000},
- {10.463165, 0.000000, 0.000000},
- {10.463165, 0.000000, -1.000000},
- {11.463165, 0.000000, -1.000000},
- {12.463165, 0.000000, -1.000000},
- {12.463165, 0.000000, 0.000000},
- {12.463165, 0.000000, 1.000000}}};
-const std::vector<std::vector<array_float_3>> coordinates_NurbsPathCurve{
- {{13.690557, 0.000000, 0.000000},
- {14.690557, 0.000000, 0.000000},
- {15.690557, 0.000000, 0.000000},
- {16.690557, 0.000000, 0.000000},
- {17.690557, 0.000000, 0.000000}},
- {{14.192808, 0.000000, 0.000000},
- {14.692808, 0.000000, -1.000000},
- {16.692808, 0.000000, -1.000000},
- {17.192808, 0.000000, 0.000000}}};
-
-const std::map<std::string, std::unique_ptr<NurbsObject>> all_nurbs_truth = []() {
- std::map<std::string, std::unique_ptr<NurbsObject>> all_nurbs;
- all_nurbs.emplace(
- "NurbsCurve",
- /* Name, coordinates, degrees of splines, control points of splines. */
- std::make_unique<NurbsObject>(
- "NurbsCurve", coordinates_NurbsCurve, std::vector<int>{3}, std::vector<int>{4}));
- all_nurbs.emplace(
- "NurbsCircle",
- std::make_unique<NurbsObject>(
- "NurbsCircle", coordinates_NurbsCircle, std::vector<int>{3}, std::vector<int>{11}));
- /* This is actually an Object containing a NurbsPath and a NurbsCurve spline. */
- all_nurbs.emplace("NurbsPathCurve",
- std::make_unique<NurbsObject>("NurbsPathCurve",
- coordinates_NurbsPathCurve,
- std::vector<int>{3, 3},
- std::vector<int>{5, 4}));
- return all_nurbs;
-}();
} // namespace blender::io::obj
diff --git a/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc b/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc
index 9dd9e7c1a37..3d34fb6f9c6 100644
--- a/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc
+++ b/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc
@@ -60,7 +60,8 @@ class obj_importer_test : public BlendfileLoadingBaseTest {
std::string obj_path = blender::tests::flags_test_asset_dir() + "/io_tests/obj/" + path;
strncpy(params.filepath, obj_path.c_str(), FILE_MAX - 1);
- importer_main(bfile->main, bfile->curscene, bfile->cur_view_layer, params);
+ const size_t read_buffer_size = 650;
+ importer_main(bfile->main, bfile->curscene, bfile->cur_view_layer, params, read_buffer_size);
depsgraph_create(DAG_EVAL_VIEWPORT);
@@ -444,6 +445,7 @@ TEST_F(obj_importer_test, import_all_objects)
float3(5, 1, 1),
float3(0, 0, 1),
float2(0.654526f, 0.579873f)},
+ {"OBNurbsCircle.001", OB_MESH, 4, 4, 0, 0, float3(2, -3, 0), float3(3, -2, 0)},
{"OBSurface",
OB_MESH,
256,
diff --git a/source/blender/io/wavefront_obj/tests/obj_mtl_parser_tests.cc b/source/blender/io/wavefront_obj/tests/obj_mtl_parser_tests.cc
new file mode 100644
index 00000000000..176d32a0be4
--- /dev/null
+++ b/source/blender/io/wavefront_obj/tests/obj_mtl_parser_tests.cc
@@ -0,0 +1,172 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+
+#include <gtest/gtest.h>
+
+#include "testing/testing.h"
+
+#include "obj_import_file_reader.hh"
+
+namespace blender::io::obj {
+
+class obj_mtl_parser_test : public testing::Test {
+ public:
+ void check(const char *file, const MTLMaterial *expect, size_t expect_count)
+ {
+ std::string obj_dir = blender::tests::flags_test_asset_dir() + "/io_tests/obj/";
+ MTLParser parser(file, obj_dir + "dummy.obj");
+ Map<std::string, std::unique_ptr<MTLMaterial>> materials;
+ parser.parse_and_store(materials);
+
+ for (int i = 0; i < expect_count; ++i) {
+ const MTLMaterial &exp = expect[i];
+ if (!materials.contains(exp.name)) {
+ fprintf(stderr, "Material '%s' was expected in parsed result\n", exp.name.c_str());
+ ADD_FAILURE();
+ continue;
+ }
+ const MTLMaterial &got = *materials.lookup(exp.name);
+ const float tol = 0.0001f;
+ EXPECT_V3_NEAR(exp.Ka, got.Ka, tol);
+ EXPECT_V3_NEAR(exp.Kd, got.Kd, tol);
+ EXPECT_V3_NEAR(exp.Ks, got.Ks, tol);
+ EXPECT_V3_NEAR(exp.Ke, got.Ke, tol);
+ EXPECT_NEAR(exp.Ns, got.Ns, tol);
+ EXPECT_NEAR(exp.Ni, got.Ni, tol);
+ EXPECT_NEAR(exp.d, got.d, tol);
+ EXPECT_NEAR(exp.map_Bump_strength, got.map_Bump_strength, tol);
+ EXPECT_EQ(exp.illum, got.illum);
+ for (const auto &it : exp.texture_maps.items()) {
+ const tex_map_XX &exp_tex = it.value;
+ const tex_map_XX &got_tex = got.texture_maps.lookup(it.key);
+ EXPECT_STREQ(exp_tex.image_path.c_str(), got_tex.image_path.c_str());
+ EXPECT_V3_NEAR(exp_tex.translation, got_tex.translation, tol);
+ EXPECT_V3_NEAR(exp_tex.scale, got_tex.scale, tol);
+ EXPECT_EQ(exp_tex.projection_type, got_tex.projection_type);
+ }
+ }
+ EXPECT_EQ(materials.size(), expect_count);
+ }
+};
+
+TEST_F(obj_mtl_parser_test, cube)
+{
+ MTLMaterial mat;
+ mat.name = "red";
+ mat.Ka = {0.2f, 0.2f, 0.2f};
+ mat.Kd = {1, 0, 0};
+ check("cube.mtl", &mat, 1);
+}
+
+TEST_F(obj_mtl_parser_test, all_objects)
+{
+ MTLMaterial mat[7];
+ for (auto &m : mat) {
+ m.Ka = {1, 1, 1};
+ m.Ks = {0.5f, 0.5f, 0.5f};
+ m.Ke = {0, 0, 0};
+ m.Ns = 250;
+ m.Ni = 1;
+ m.d = 1;
+ m.illum = 2;
+ }
+ mat[0].name = "Blue";
+ mat[0].Kd = {0, 0, 1};
+ mat[1].name = "BlueDark";
+ mat[1].Kd = {0, 0, 0.5f};
+ mat[2].name = "Green";
+ mat[2].Kd = {0, 1, 0};
+ mat[3].name = "GreenDark";
+ mat[3].Kd = {0, 0.5f, 0};
+ mat[4].name = "Material";
+ mat[4].Kd = {0.8f, 0.8f, 0.8f};
+ mat[5].name = "Red";
+ mat[5].Kd = {1, 0, 0};
+ mat[6].name = "RedDark";
+ mat[6].Kd = {0.5f, 0, 0};
+ check("all_objects.mtl", mat, ARRAY_SIZE(mat));
+}
+
+TEST_F(obj_mtl_parser_test, materials)
+{
+ MTLMaterial mat[5];
+ mat[0].name = "no_textures_red";
+ mat[0].Ka = {0.3f, 0.3f, 0.3f};
+ mat[0].Kd = {0.8f, 0.3f, 0.1f};
+ mat[0].Ns = 5.624998f;
+
+ mat[1].name = "four_maps";
+ mat[1].Ka = {1, 1, 1};
+ mat[1].Kd = {0.8f, 0.8f, 0.8f};
+ mat[1].Ks = {0.5f, 0.5f, 0.5f};
+ mat[1].Ke = {0, 0, 0};
+ mat[1].Ns = 1000;
+ mat[1].Ni = 1.45f;
+ mat[1].d = 1;
+ mat[1].illum = 2;
+ mat[1].map_Bump_strength = 1;
+ {
+ tex_map_XX &kd = mat[1].tex_map_of_type(eMTLSyntaxElement::map_Kd);
+ kd.image_path = "texture.png";
+ tex_map_XX &ns = mat[1].tex_map_of_type(eMTLSyntaxElement::map_Ns);
+ ns.image_path = "sometexture_Roughness.png";
+ tex_map_XX &refl = mat[1].tex_map_of_type(eMTLSyntaxElement::map_refl);
+ refl.image_path = "sometexture_Metallic.png";
+ tex_map_XX &bump = mat[1].tex_map_of_type(eMTLSyntaxElement::map_Bump);
+ bump.image_path = "sometexture_Normal.png";
+ }
+
+ mat[2].name = "Clay";
+ mat[2].Ka = {1, 1, 1};
+ mat[2].Kd = {0.8f, 0.682657f, 0.536371f};
+ mat[2].Ks = {0.5f, 0.5f, 0.5f};
+ mat[2].Ke = {0, 0, 0};
+ mat[2].Ns = 440.924042f;
+ mat[2].Ni = 1.45f;
+ mat[2].d = 1;
+ mat[2].illum = 2;
+
+ mat[3].name = "Hat";
+ mat[3].Ka = {1, 1, 1};
+ mat[3].Kd = {0.8f, 0.8f, 0.8f};
+ mat[3].Ks = {0.5f, 0.5f, 0.5f};
+ mat[3].Ns = 800;
+ mat[3].map_Bump_strength = 0.5f;
+ {
+ tex_map_XX &kd = mat[3].tex_map_of_type(eMTLSyntaxElement::map_Kd);
+ kd.image_path = "someHatTexture_BaseColor.jpg";
+ tex_map_XX &ns = mat[3].tex_map_of_type(eMTLSyntaxElement::map_Ns);
+ ns.image_path = "someHatTexture_Roughness.jpg";
+ tex_map_XX &refl = mat[3].tex_map_of_type(eMTLSyntaxElement::map_refl);
+ refl.image_path = "someHatTexture_Metalness.jpg";
+ tex_map_XX &bump = mat[3].tex_map_of_type(eMTLSyntaxElement::map_Bump);
+ bump.image_path = "someHatTexture_Normal.jpg";
+ }
+
+ mat[4].name = "Parser_Test";
+ mat[4].Ka = {0.1f, 0.2f, 0.3f};
+ mat[4].Kd = {0.4f, 0.5f, 0.6f};
+ mat[4].Ks = {0.7f, 0.8f, 0.9f};
+ mat[4].illum = 6;
+ mat[4].Ns = 15.5;
+ mat[4].Ni = 1.5;
+ mat[4].d = 0.5;
+ mat[4].map_Bump_strength = 0.1f;
+ {
+ tex_map_XX &kd = mat[4].tex_map_of_type(eMTLSyntaxElement::map_Kd);
+ kd.image_path = "sometex_d.png";
+ tex_map_XX &ns = mat[4].tex_map_of_type(eMTLSyntaxElement::map_Ns);
+ ns.image_path = "sometex_ns.psd";
+ tex_map_XX &refl = mat[4].tex_map_of_type(eMTLSyntaxElement::map_refl);
+ refl.image_path = "clouds.tiff";
+ refl.scale = {1.5f, 2.5f, 3.5f};
+ refl.translation = {4.5f, 5.5f, 6.5f};
+ refl.projection_type = SHD_PROJ_SPHERE;
+ tex_map_XX &bump = mat[4].tex_map_of_type(eMTLSyntaxElement::map_Bump);
+ bump.image_path = "somebump.tga";
+ bump.scale = {3, 4, 5};
+ }
+
+ check("materials.mtl", mat, ARRAY_SIZE(mat));
+}
+
+} // namespace blender::io::obj
diff --git a/source/blender/makesdna/DNA_anim_types.h b/source/blender/makesdna/DNA_anim_types.h
index 5d3c8872d47..99737aa3b67 100644
--- a/source/blender/makesdna/DNA_anim_types.h
+++ b/source/blender/makesdna/DNA_anim_types.h
@@ -1128,6 +1128,8 @@ typedef enum eAnimData_Flag {
ADT_NLA_EDIT_NOMAP = (1 << 3),
/** NLA-Strip F-Curves are expanded in UI. */
ADT_NLA_SKEYS_COLLAPSED = (1 << 4),
+ /* Evaluate tracks above tweaked strip. Only relevant in tweak mode. */
+ ADT_NLA_EVAL_UPPER_TRACKS = (1 << 5),
/** Drivers expanded in UI. */
ADT_DRIVERS_COLLAPSED = (1 << 10),
diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h
index 96a97648ac9..ee78b610276 100644
--- a/source/blender/makesdna/DNA_brush_enums.h
+++ b/source/blender/makesdna/DNA_brush_enums.h
@@ -462,7 +462,6 @@ typedef enum eBrushCurvesSculptTool {
CURVES_SCULPT_TOOL_SNAKE_HOOK = 2,
CURVES_SCULPT_TOOL_ADD = 3,
CURVES_SCULPT_TOOL_GROW_SHRINK = 4,
- CURVES_SCULPT_TOOL_TEST1 = 5,
} eBrushCurvesSculptTool;
/** When #BRUSH_ACCUMULATE is used */
diff --git a/source/blender/makesdna/DNA_curve_types.h b/source/blender/makesdna/DNA_curve_types.h
index 556e467c4b6..305b913b19e 100644
--- a/source/blender/makesdna/DNA_curve_types.h
+++ b/source/blender/makesdna/DNA_curve_types.h
@@ -127,6 +127,8 @@ typedef struct BPoint {
* also, it should be NURBS (Nurb isn't the singular of Nurbs).
*/
typedef struct Nurb {
+ DNA_DEFINE_CXX_METHODS(Nurb)
+
/** Multiple nurbs per curve object are allowed. */
struct Nurb *next, *prev;
short type;
@@ -169,6 +171,8 @@ typedef struct TextBox {
#
#
typedef struct EditNurb {
+ DNA_DEFINE_CXX_METHODS(EditNurb)
+
/* base of nurbs' list (old Curve->editnurb) */
ListBase nurbs;
@@ -187,6 +191,8 @@ typedef struct EditNurb {
} EditNurb;
typedef struct Curve {
+ DNA_DEFINE_CXX_METHODS(Curve)
+
ID id;
/** Animation data (must be immediately after id for utilities to use it). */
struct AnimData *adt;
diff --git a/source/blender/makesdna/DNA_lattice_types.h b/source/blender/makesdna/DNA_lattice_types.h
index b2775aa771a..907f1150ce5 100644
--- a/source/blender/makesdna/DNA_lattice_types.h
+++ b/source/blender/makesdna/DNA_lattice_types.h
@@ -23,6 +23,8 @@ struct MDeformVert;
#
#
typedef struct EditLatt {
+ DNA_DEFINE_CXX_METHODS(EditLatt)
+
struct Lattice *latt;
int shapenr;
@@ -35,6 +37,8 @@ typedef struct EditLatt {
} EditLatt;
typedef struct Lattice {
+ DNA_DEFINE_CXX_METHODS(Lattice)
+
ID id;
struct AnimData *adt;
diff --git a/source/blender/makesdna/DNA_light_types.h b/source/blender/makesdna/DNA_light_types.h
index e78a381603b..9202d7c2d51 100644
--- a/source/blender/makesdna/DNA_light_types.h
+++ b/source/blender/makesdna/DNA_light_types.h
@@ -24,6 +24,8 @@ struct Ipo;
struct bNodeTree;
typedef struct Light {
+ DNA_DEFINE_CXX_METHODS(Light)
+
ID id;
/** Animation data (must be immediately after id for utilities to use it). */
struct AnimData *adt;
diff --git a/source/blender/makesdna/DNA_linestyle_types.h b/source/blender/makesdna/DNA_linestyle_types.h
index b8279e7f2ca..0755803ea8f 100644
--- a/source/blender/makesdna/DNA_linestyle_types.h
+++ b/source/blender/makesdna/DNA_linestyle_types.h
@@ -29,6 +29,8 @@ struct Object;
struct bNodeTree;
typedef struct LineStyleModifier {
+ DNA_DEFINE_CXX_METHODS(LineStyleModifier)
+
struct LineStyleModifier *next, *prev;
/** MAX_NAME. */
@@ -92,12 +94,16 @@ typedef struct LineStyleModifier {
/* Along Stroke modifiers */
typedef struct LineStyleColorModifier_AlongStroke {
+ DNA_DEFINE_CXX_METHODS(LineStyleColorModifier_AlongStroke)
+
struct LineStyleModifier modifier;
struct ColorBand *color_ramp;
} LineStyleColorModifier_AlongStroke;
typedef struct LineStyleAlphaModifier_AlongStroke {
+ DNA_DEFINE_CXX_METHODS(LineStyleAlphaModifier_AlongStroke)
+
struct LineStyleModifier modifier;
struct CurveMapping *curve;
@@ -106,6 +112,8 @@ typedef struct LineStyleAlphaModifier_AlongStroke {
} LineStyleAlphaModifier_AlongStroke;
typedef struct LineStyleThicknessModifier_AlongStroke {
+ DNA_DEFINE_CXX_METHODS(LineStyleThicknessModifier_AlongStroke)
+
struct LineStyleModifier modifier;
struct CurveMapping *curve;
@@ -117,6 +125,8 @@ typedef struct LineStyleThicknessModifier_AlongStroke {
/* Distance from Camera modifiers */
typedef struct LineStyleColorModifier_DistanceFromCamera {
+ DNA_DEFINE_CXX_METHODS(LineStyleColorModifier_DistanceFromCamera)
+
struct LineStyleModifier modifier;
struct ColorBand *color_ramp;
@@ -124,6 +134,8 @@ typedef struct LineStyleColorModifier_DistanceFromCamera {
} LineStyleColorModifier_DistanceFromCamera;
typedef struct LineStyleAlphaModifier_DistanceFromCamera {
+ DNA_DEFINE_CXX_METHODS(LineStyleAlphaModifier_DistanceFromCamera)
+
struct LineStyleModifier modifier;
struct CurveMapping *curve;
@@ -133,6 +145,8 @@ typedef struct LineStyleAlphaModifier_DistanceFromCamera {
} LineStyleAlphaModifier_DistanceFromCamera;
typedef struct LineStyleThicknessModifier_DistanceFromCamera {
+ DNA_DEFINE_CXX_METHODS(LineStyleThicknessModifier_DistanceFromCamera)
+
struct LineStyleModifier modifier;
struct CurveMapping *curve;
@@ -145,6 +159,8 @@ typedef struct LineStyleThicknessModifier_DistanceFromCamera {
/* Distance from Object modifiers */
typedef struct LineStyleColorModifier_DistanceFromObject {
+ DNA_DEFINE_CXX_METHODS(LineStyleColorModifier_DistanceFromObject)
+
struct LineStyleModifier modifier;
struct Object *target;
@@ -153,6 +169,8 @@ typedef struct LineStyleColorModifier_DistanceFromObject {
} LineStyleColorModifier_DistanceFromObject;
typedef struct LineStyleAlphaModifier_DistanceFromObject {
+ DNA_DEFINE_CXX_METHODS(LineStyleAlphaModifier_DistanceFromObject)
+
struct LineStyleModifier modifier;
struct Object *target;
@@ -163,6 +181,8 @@ typedef struct LineStyleAlphaModifier_DistanceFromObject {
} LineStyleAlphaModifier_DistanceFromObject;
typedef struct LineStyleThicknessModifier_DistanceFromObject {
+ DNA_DEFINE_CXX_METHODS(LineStyleThicknessModifier_DistanceFromObject)
+
struct LineStyleModifier modifier;
struct Object *target;
@@ -176,6 +196,8 @@ typedef struct LineStyleThicknessModifier_DistanceFromObject {
/* 3D curvature modifiers */
typedef struct LineStyleColorModifier_Curvature_3D {
+ DNA_DEFINE_CXX_METHODS(LineStyleColorModifier_Curvature_3D)
+
struct LineStyleModifier modifier;
float min_curvature, max_curvature;
@@ -184,6 +206,8 @@ typedef struct LineStyleColorModifier_Curvature_3D {
} LineStyleColorModifier_Curvature_3D;
typedef struct LineStyleAlphaModifier_Curvature_3D {
+ DNA_DEFINE_CXX_METHODS(LineStyleAlphaModifier_Curvature_3D)
+
struct LineStyleModifier modifier;
struct CurveMapping *curve;
@@ -193,6 +217,8 @@ typedef struct LineStyleAlphaModifier_Curvature_3D {
} LineStyleAlphaModifier_Curvature_3D;
typedef struct LineStyleThicknessModifier_Curvature_3D {
+ DNA_DEFINE_CXX_METHODS(LineStyleThicknessModifier_Curvature_3D)
+
struct LineStyleModifier modifier;
struct CurveMapping *curve;
@@ -205,6 +231,8 @@ typedef struct LineStyleThicknessModifier_Curvature_3D {
/* Noise modifiers (for color, alpha and thickness) */
typedef struct LineStyleColorModifier_Noise {
+ DNA_DEFINE_CXX_METHODS(LineStyleColorModifier_Noise)
+
struct LineStyleModifier modifier;
struct ColorBand *color_ramp;
@@ -214,6 +242,8 @@ typedef struct LineStyleColorModifier_Noise {
} LineStyleColorModifier_Noise;
typedef struct LineStyleAlphaModifier_Noise {
+ DNA_DEFINE_CXX_METHODS(LineStyleAlphaModifier_Noise)
+
struct LineStyleModifier modifier;
struct CurveMapping *curve;
@@ -223,6 +253,8 @@ typedef struct LineStyleAlphaModifier_Noise {
} LineStyleAlphaModifier_Noise;
typedef struct LineStyleThicknessModifier_Noise {
+ DNA_DEFINE_CXX_METHODS(LineStyleThicknessModifier_Noise)
+
struct LineStyleModifier modifier;
float period, amplitude;
@@ -233,6 +265,8 @@ typedef struct LineStyleThicknessModifier_Noise {
/* Crease Angle modifiers */
typedef struct LineStyleColorModifier_CreaseAngle {
+ DNA_DEFINE_CXX_METHODS(LineStyleColorModifier_CreaseAngle)
+
struct LineStyleModifier modifier;
struct ColorBand *color_ramp;
@@ -240,6 +274,8 @@ typedef struct LineStyleColorModifier_CreaseAngle {
} LineStyleColorModifier_CreaseAngle;
typedef struct LineStyleAlphaModifier_CreaseAngle {
+ DNA_DEFINE_CXX_METHODS(LineStyleAlphaModifier_CreaseAngle)
+
struct LineStyleModifier modifier;
struct CurveMapping *curve;
@@ -249,6 +285,8 @@ typedef struct LineStyleAlphaModifier_CreaseAngle {
} LineStyleAlphaModifier_CreaseAngle;
typedef struct LineStyleThicknessModifier_CreaseAngle {
+ DNA_DEFINE_CXX_METHODS(LineStyleThicknessModifier_CreaseAngle)
+
struct LineStyleModifier modifier;
struct CurveMapping *curve;
@@ -261,12 +299,16 @@ typedef struct LineStyleThicknessModifier_CreaseAngle {
/* Tangent modifiers */
typedef struct LineStyleColorModifier_Tangent {
+ DNA_DEFINE_CXX_METHODS(LineStyleColorModifier_Tangent)
+
struct LineStyleModifier modifier;
struct ColorBand *color_ramp;
} LineStyleColorModifier_Tangent;
typedef struct LineStyleAlphaModifier_Tangent {
+ DNA_DEFINE_CXX_METHODS(LineStyleAlphaModifier_Tangent)
+
struct LineStyleModifier modifier;
struct CurveMapping *curve;
@@ -275,6 +317,8 @@ typedef struct LineStyleAlphaModifier_Tangent {
} LineStyleAlphaModifier_Tangent;
typedef struct LineStyleThicknessModifier_Tangent {
+ DNA_DEFINE_CXX_METHODS(LineStyleThicknessModifier_Tangent)
+
struct LineStyleModifier modifier;
struct CurveMapping *curve;
@@ -303,6 +347,8 @@ typedef struct LineStyleThicknessModifier_Tangent {
#define LS_MODIFIER_MATERIAL_LINE_A 15
typedef struct LineStyleColorModifier_Material {
+ DNA_DEFINE_CXX_METHODS(LineStyleColorModifier_Material)
+
struct LineStyleModifier modifier;
struct ColorBand *color_ramp;
@@ -311,6 +357,8 @@ typedef struct LineStyleColorModifier_Material {
} LineStyleColorModifier_Material;
typedef struct LineStyleAlphaModifier_Material {
+ DNA_DEFINE_CXX_METHODS(LineStyleAlphaModifier_Material)
+
struct LineStyleModifier modifier;
struct CurveMapping *curve;
@@ -319,6 +367,8 @@ typedef struct LineStyleAlphaModifier_Material {
} LineStyleAlphaModifier_Material;
typedef struct LineStyleThicknessModifier_Material {
+ DNA_DEFINE_CXX_METHODS(LineStyleThicknessModifier_Material)
+
struct LineStyleModifier modifier;
struct CurveMapping *curve;
@@ -330,6 +380,8 @@ typedef struct LineStyleThicknessModifier_Material {
/* Geometry modifiers */
typedef struct LineStyleGeometryModifier_Sampling {
+ DNA_DEFINE_CXX_METHODS(LineStyleGeometryModifier_Sampling)
+
struct LineStyleModifier modifier;
float sampling;
@@ -337,6 +389,8 @@ typedef struct LineStyleGeometryModifier_Sampling {
} LineStyleGeometryModifier_Sampling;
typedef struct LineStyleGeometryModifier_BezierCurve {
+ DNA_DEFINE_CXX_METHODS(LineStyleGeometryModifier_BezierCurve)
+
struct LineStyleModifier modifier;
float error;
@@ -344,6 +398,8 @@ typedef struct LineStyleGeometryModifier_BezierCurve {
} LineStyleGeometryModifier_BezierCurve;
typedef struct LineStyleGeometryModifier_SinusDisplacement {
+ DNA_DEFINE_CXX_METHODS(LineStyleGeometryModifier_SinusDisplacement)
+
struct LineStyleModifier modifier;
float wavelength, amplitude, phase;
@@ -355,6 +411,8 @@ typedef struct LineStyleGeometryModifier_SinusDisplacement {
#define LS_MODIFIER_SPATIAL_NOISE_PURERANDOM 2
typedef struct LineStyleGeometryModifier_SpatialNoise {
+ DNA_DEFINE_CXX_METHODS(LineStyleGeometryModifier_SpatialNoise)
+
struct LineStyleModifier modifier;
float amplitude, scale;
@@ -363,6 +421,8 @@ typedef struct LineStyleGeometryModifier_SpatialNoise {
} LineStyleGeometryModifier_SpatialNoise;
typedef struct LineStyleGeometryModifier_PerlinNoise1D {
+ DNA_DEFINE_CXX_METHODS(LineStyleGeometryModifier_PerlinNoise1D)
+
struct LineStyleModifier modifier;
float frequency, amplitude;
@@ -374,6 +434,8 @@ typedef struct LineStyleGeometryModifier_PerlinNoise1D {
} LineStyleGeometryModifier_PerlinNoise1D;
typedef struct LineStyleGeometryModifier_PerlinNoise2D {
+ DNA_DEFINE_CXX_METHODS(LineStyleGeometryModifier_PerlinNoise2D)
+
struct LineStyleModifier modifier;
float frequency, amplitude;
@@ -385,6 +447,8 @@ typedef struct LineStyleGeometryModifier_PerlinNoise2D {
} LineStyleGeometryModifier_PerlinNoise2D;
typedef struct LineStyleGeometryModifier_BackboneStretcher {
+ DNA_DEFINE_CXX_METHODS(LineStyleGeometryModifier_BackboneStretcher)
+
struct LineStyleModifier modifier;
float backbone_length;
@@ -392,6 +456,8 @@ typedef struct LineStyleGeometryModifier_BackboneStretcher {
} LineStyleGeometryModifier_BackboneStretcher;
typedef struct LineStyleGeometryModifier_TipRemover {
+ DNA_DEFINE_CXX_METHODS(LineStyleGeometryModifier_TipRemover)
+
struct LineStyleModifier modifier;
float tip_length;
@@ -399,6 +465,8 @@ typedef struct LineStyleGeometryModifier_TipRemover {
} LineStyleGeometryModifier_TipRemover;
typedef struct LineStyleGeometryModifier_Polygonalization {
+ DNA_DEFINE_CXX_METHODS(LineStyleGeometryModifier_Polygonalization)
+
struct LineStyleModifier modifier;
float error;
@@ -406,6 +474,8 @@ typedef struct LineStyleGeometryModifier_Polygonalization {
} LineStyleGeometryModifier_Polygonalization;
typedef struct LineStyleGeometryModifier_GuidingLines {
+ DNA_DEFINE_CXX_METHODS(LineStyleGeometryModifier_GuidingLines)
+
struct LineStyleModifier modifier;
float offset;
@@ -418,6 +488,8 @@ typedef struct LineStyleGeometryModifier_GuidingLines {
#define LS_MODIFIER_BLUEPRINT_SQUARES 4
typedef struct LineStyleGeometryModifier_Blueprint {
+ DNA_DEFINE_CXX_METHODS(LineStyleGeometryModifier_Blueprint)
+
struct LineStyleModifier modifier;
int flags;
@@ -429,6 +501,8 @@ typedef struct LineStyleGeometryModifier_Blueprint {
} LineStyleGeometryModifier_Blueprint;
typedef struct LineStyleGeometryModifier_2DOffset {
+ DNA_DEFINE_CXX_METHODS(LineStyleGeometryModifier_2DOffset)
+
struct LineStyleModifier modifier;
float start, end;
@@ -443,6 +517,8 @@ typedef struct LineStyleGeometryModifier_2DOffset {
#define LS_MODIFIER_2D_TRANSFORM_PIVOT_ABSOLUTE 5
typedef struct LineStyleGeometryModifier_2DTransform {
+ DNA_DEFINE_CXX_METHODS(LineStyleGeometryModifier_2DTransform)
+
struct LineStyleModifier modifier;
int pivot;
@@ -455,6 +531,8 @@ typedef struct LineStyleGeometryModifier_2DTransform {
} LineStyleGeometryModifier_2DTransform;
typedef struct LineStyleGeometryModifier_Simplification {
+ DNA_DEFINE_CXX_METHODS(LineStyleGeometryModifier_Simplification)
+
struct LineStyleModifier modifier;
float tolerance;
@@ -464,6 +542,8 @@ typedef struct LineStyleGeometryModifier_Simplification {
/* Calligraphic thickness modifier */
typedef struct LineStyleThicknessModifier_Calligraphy {
+ DNA_DEFINE_CXX_METHODS(LineStyleThicknessModifier_Calligraphy)
+
struct LineStyleModifier modifier;
float min_thickness, max_thickness;
@@ -527,6 +607,8 @@ typedef struct LineStyleThicknessModifier_Calligraphy {
#define LS_INTEGRATION_LAST 5
typedef struct FreestyleLineStyle {
+ DNA_DEFINE_CXX_METHODS(FreestyleLineStyle)
+
ID id;
struct AnimData *adt;
diff --git a/source/blender/makesdna/DNA_material_types.h b/source/blender/makesdna/DNA_material_types.h
index b535d3cdb8a..332317142c7 100644
--- a/source/blender/makesdna/DNA_material_types.h
+++ b/source/blender/makesdna/DNA_material_types.h
@@ -27,8 +27,12 @@ struct bNodeTree;
/* WATCH IT: change type? also make changes in ipo.h */
typedef struct TexPaintSlot {
+ DNA_DEFINE_CXX_METHODS(TexPaintSlot)
+
/** Image to be painted on. Mutual exclusive with attribute_name. */
struct Image *ima;
+ struct ImageUser *image_user;
+
/** Custom-data index for uv layer, #MAX_NAME. */
char *uvname;
/**
@@ -43,6 +47,8 @@ typedef struct TexPaintSlot {
} TexPaintSlot;
typedef struct MaterialGPencilStyle {
+ DNA_DEFINE_CXX_METHODS(MaterialGPencilStyle)
+
/** Texture image for strokes. */
struct Image *sima;
/** Texture image for filling. */
@@ -155,6 +161,8 @@ typedef enum eMaterialLineArtFlags {
} eMaterialLineArtFlags;
typedef struct Material {
+ DNA_DEFINE_CXX_METHODS(Material)
+
ID id;
/** Animation data (must be immediately after id for utilities to use it). */
struct AnimData *adt;
diff --git a/source/blender/makesdna/DNA_mesh_defaults.h b/source/blender/makesdna/DNA_mesh_defaults.h
index 079bda42009..30fe387b05a 100644
--- a/source/blender/makesdna/DNA_mesh_defaults.h
+++ b/source/blender/makesdna/DNA_mesh_defaults.h
@@ -22,7 +22,7 @@
.remesh_voxel_adaptivity = 0.0f, \
.face_sets_color_seed = 0, \
.face_sets_color_default = 1, \
- .flag = ME_REMESH_FIX_POLES | ME_REMESH_REPROJECT_VOLUME, \
+ .flag = ME_REMESH_REPROJECT_VOLUME | ME_REMESH_REPROJECT_PAINT_MASK | ME_REMESH_REPROJECT_SCULPT_FACE_SETS | ME_REMESH_REPROJECT_VERTEX_COLORS, \
.editflag = ME_EDIT_MIRROR_VERTEX_GROUPS \
}
diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h
index d8a853681fd..0ff9ebb2337 100644
--- a/source/blender/makesdna/DNA_mesh_types.h
+++ b/source/blender/makesdna/DNA_mesh_types.h
@@ -50,6 +50,8 @@ typedef struct EditMeshData {
* #BKE_mesh_runtime_looptri_ensure, #BKE_mesh_runtime_looptri_len.
*/
struct MLoopTri_Store {
+ DNA_DEFINE_CXX_METHODS(MLoopTri_Store)
+
/* WARNING! swapping between array (ready-to-be-used data) and array_wip
* (where data is actually computed)
* shall always be protected by same lock as one used for looptris computing. */
@@ -60,6 +62,8 @@ struct MLoopTri_Store {
/** Runtime data, not saved in files. */
typedef struct Mesh_Runtime {
+ DNA_DEFINE_CXX_METHODS(Mesh_Runtime)
+
/* Evaluated mesh for objects which do not have effective modifiers.
* This mesh is used as a result of modifier stack evaluation.
* Since modifier stack evaluation is threaded on object level we need some synchronization. */
@@ -138,6 +142,8 @@ typedef struct Mesh_Runtime {
} Mesh_Runtime;
typedef struct Mesh {
+ DNA_DEFINE_CXX_METHODS(Mesh)
+
ID id;
/** Animation data (must be immediately after id for utilities to use it). */
struct AnimData *adt;
@@ -336,6 +342,8 @@ typedef struct Mesh {
/* deprecated by MTFace, only here for file reading */
#ifdef DNA_DEPRECATED_ALLOW
typedef struct TFace {
+ DNA_DEFINE_CXX_METHODS(TFace)
+
/** The faces image for the active UVLayer. */
void *tpage;
float uv[4][2];
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index 5f909ea325b..1b9192c75cf 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -318,23 +318,12 @@ typedef struct bNode {
short preview_xsize, preview_ysize;
/** Used at runtime when going through the tree. Initialize before use. */
short tmp_flag;
- /** Used at runtime to tag derivatives branches. EEVEE only. */
- char branch_tag;
+
+ char _pad0;
/** Used at runtime when iterating over node branches. */
char iter_flag;
/**
- * XXX: eevee only, id of screen space reflection layer,
- * needs to be a float to feed GPU_uniform.
- */
- float ssr_id;
- /**
- * XXX: eevee only, id of screen subsurface scatter layer,
- * needs to be a float to feed GPU_uniform.
- */
- float sss_id;
-
- /**
* Describes the desired interface of the node. This is run-time data only.
* The actual interface of the node may deviate from the declaration temporarily.
* It's possible to sync the actual state of the node to the desired state. Currently, this is
diff --git a/source/blender/makesdna/DNA_particle_types.h b/source/blender/makesdna/DNA_particle_types.h
index f3d342e4849..268e1412eef 100644
--- a/source/blender/makesdna/DNA_particle_types.h
+++ b/source/blender/makesdna/DNA_particle_types.h
@@ -109,7 +109,12 @@ typedef struct ParticleData {
/** Die-time is not necessarily time+lifetime as. */
float time, lifetime;
- /** Particles can die unnaturally (collision). */
+ /**
+ * Particles can die unnaturally (collision).
+ *
+ * \note Particles die on this frame, be sure to add 1 when clamping the lifetime of particles
+ * to inclusive ranges such as the scenes end frame. See: T68290.
+ */
float dietime;
/**
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index 9cc4d5ed55b..d6c1040110f 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -939,6 +939,7 @@ typedef struct PaintModeSettings {
/** Selected image when canvas_source=PAINT_CANVAS_SOURCE_IMAGE. */
Image *canvas_image;
+ ImageUser image_user;
} PaintModeSettings;
diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h
index c1eee109630..8560f8a454e 100644
--- a/source/blender/makesdna/DNA_screen_types.h
+++ b/source/blender/makesdna/DNA_screen_types.h
@@ -664,7 +664,7 @@ typedef enum eRegion_Type {
* context (surface, mirror view). Does not represent any real region. */
RGN_TYPE_XR = 13,
-#define RGN_TYPE_LEN (RGN_TYPE_XR + 1)
+#define RGN_TYPE_NUM (RGN_TYPE_XR + 1)
} eRegion_Type;
/* use for function args */
diff --git a/source/blender/makesdna/DNA_simulation_types.h b/source/blender/makesdna/DNA_simulation_types.h
index 4b2df1ddbfb..4bbbe9cd4e8 100644
--- a/source/blender/makesdna/DNA_simulation_types.h
+++ b/source/blender/makesdna/DNA_simulation_types.h
@@ -14,6 +14,8 @@ extern "C" {
#endif
typedef struct Simulation {
+ DNA_DEFINE_CXX_METHODS(Simulation)
+
ID id;
struct AnimData *adt; /* animation data (must be immediately after id) */
diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h
index bcf54ee47a0..838213dd2f3 100644
--- a/source/blender/makesdna/DNA_space_types.h
+++ b/source/blender/makesdna/DNA_space_types.h
@@ -1513,6 +1513,7 @@ typedef enum eSpaceNodeOverlay_Flag {
SN_OVERLAY_SHOW_WIRE_COLORS = (1 << 2),
SN_OVERLAY_SHOW_TIMINGS = (1 << 3),
SN_OVERLAY_SHOW_PATH = (1 << 4),
+ SN_OVERLAY_SHOW_NAMED_ATTRIBUTES = (1 << 5),
} eSpaceNodeOverlay_Flag;
typedef struct SpaceNode {
@@ -2057,7 +2058,7 @@ typedef enum eSpace_Type {
SPACE_STATUSBAR = 22,
SPACE_SPREADSHEET = 23
-#define SPACE_TYPE_LAST SPACE_SPREADSHEET
+#define SPACE_TYPE_NUM (SPACE_SPREADSHEET + 1)
} eSpace_Type;
/* use for function args */
diff --git a/source/blender/makesdna/DNA_texture_types.h b/source/blender/makesdna/DNA_texture_types.h
index dab3a1b331e..b725939dbab 100644
--- a/source/blender/makesdna/DNA_texture_types.h
+++ b/source/blender/makesdna/DNA_texture_types.h
@@ -25,6 +25,7 @@ struct PreviewImage;
struct Tex;
typedef struct MTex {
+ DNA_DEFINE_CXX_METHODS(MTex)
short texco, mapto, maptoneg, blendtype;
struct Object *object;
@@ -96,6 +97,8 @@ typedef struct ColorBand {
} ColorBand;
typedef struct PointDensity {
+ DNA_DEFINE_CXX_METHODS(PointDensity)
+
short flag;
short falloff_type;
@@ -143,6 +146,8 @@ typedef struct PointDensity {
} PointDensity;
typedef struct Tex {
+ DNA_DEFINE_CXX_METHODS(Tex)
+
ID id;
/** Animation data (must be immediately after id for utilities to use it). */
struct AnimData *adt;
diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h
index 0f6c32e4ddf..619f4c05875 100644
--- a/source/blender/makesdna/DNA_userdef_types.h
+++ b/source/blender/makesdna/DNA_userdef_types.h
@@ -649,9 +649,9 @@ typedef struct UserDef_Experimental {
char use_extended_asset_browser;
char use_override_templates;
char use_named_attribute_nodes;
- char use_select_nearest_on_first_click;
char enable_eevee_next;
char use_sculpt_texture_paint;
+ char _pad0[1];
/** `makesdna` does not allow empty structs. */
} UserDef_Experimental;
diff --git a/source/blender/makesdna/DNA_world_types.h b/source/blender/makesdna/DNA_world_types.h
index 88f2e0c9407..bd2ba1e3bf7 100644
--- a/source/blender/makesdna/DNA_world_types.h
+++ b/source/blender/makesdna/DNA_world_types.h
@@ -27,6 +27,8 @@ struct bNodeTree;
* World defines general modeling data such as a background fill,
* gravity, color model etc. It mixes rendering data and modeling data. */
typedef struct World {
+ DNA_DEFINE_CXX_METHODS(World)
+
ID id;
/** Animation data (must be immediately after id for utilities to use it). */
struct AnimData *adt;
diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c
index d3b7e380afa..e2ce1f23a33 100644
--- a/source/blender/makesrna/intern/rna_ID.c
+++ b/source/blender/makesrna/intern/rna_ID.c
@@ -1457,7 +1457,7 @@ static void rna_def_ID_properties(BlenderRNA *brna)
* when we only really want this so RNA_def_struct_name_property() is set to something useful */
prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
RNA_def_property_flag(prop, PROP_IDPROPERTY);
- /*RNA_def_property_clear_flag(prop, PROP_EDITABLE); */
+ // RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Name", "Unique name used in the code and scripting");
RNA_def_struct_name_property(srna, prop);
}
diff --git a/source/blender/makesrna/intern/rna_armature.c b/source/blender/makesrna/intern/rna_armature.c
index 11c4dad15e9..8d022338d7a 100644
--- a/source/blender/makesrna/intern/rna_armature.c
+++ b/source/blender/makesrna/intern/rna_armature.c
@@ -1383,7 +1383,7 @@ static void rna_def_armature_edit_bones(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_property_pointer_sdna(prop, NULL, "act_edbone");
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Active EditBone", "Armatures active edit bone");
- /*RNA_def_property_update(prop, 0, "rna_Armature_act_editbone_update"); */
+ // RNA_def_property_update(prop, 0, "rna_Armature_act_editbone_update");
RNA_def_property_pointer_funcs(prop, NULL, "rna_Armature_act_edit_bone_set", NULL, NULL);
/* TODO: redraw. */
diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c
index 062c827b9d0..25dd87b1e74 100644
--- a/source/blender/makesrna/intern/rna_brush.c
+++ b/source/blender/makesrna/intern/rna_brush.c
@@ -249,7 +249,6 @@ const EnumPropertyItem rna_enum_brush_curves_sculpt_tool_items[] = {
{CURVES_SCULPT_TOOL_SNAKE_HOOK, "SNAKE_HOOK", ICON_NONE, "Snake Hook", ""},
{CURVES_SCULPT_TOOL_ADD, "ADD", ICON_NONE, "Add", ""},
{CURVES_SCULPT_TOOL_GROW_SHRINK, "GROW_SHRINK", ICON_NONE, "Grow / Shrink", ""},
- {CURVES_SCULPT_TOOL_TEST1, "TEST1", ICON_NONE, "Test 1", ""},
{0, NULL, 0, NULL, NULL},
};
@@ -664,7 +663,7 @@ static void rna_Brush_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerR
{
Brush *br = (Brush *)ptr->data;
WM_main_add_notifier(NC_BRUSH | NA_EDITED, br);
- /*WM_main_add_notifier(NC_SPACE|ND_SPACE_VIEW3D, NULL); */
+ // WM_main_add_notifier(NC_SPACE | ND_SPACE_VIEW3D, NULL);
}
static void rna_Brush_material_update(bContext *UNUSED(C), PointerRNA *UNUSED(ptr))
diff --git a/source/blender/makesrna/intern/rna_camera.c b/source/blender/makesrna/intern/rna_camera.c
index 794f08d850e..f535cdcee96 100644
--- a/source/blender/makesrna/intern/rna_camera.c
+++ b/source/blender/makesrna/intern/rna_camera.c
@@ -564,7 +564,8 @@ void RNA_def_camera(BlenderRNA *brna)
RNA_def_property_float_sdna(prop, NULL, "lens");
RNA_def_property_range(prop, 1.0f, FLT_MAX);
RNA_def_property_ui_range(prop, 1.0f, 5000.0f, 100, 4);
- RNA_def_property_ui_text(prop, "Focal Length", "Perspective Camera focal length value in millimeters");
+ RNA_def_property_ui_text(
+ prop, "Focal Length", "Perspective Camera focal length value in millimeters");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Camera_update");
prop = RNA_def_property(srna, "sensor_width", PROP_FLOAT, PROP_DISTANCE_CAMERA);
diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c
index 22275df61f9..52af96e355d 100644
--- a/source/blender/makesrna/intern/rna_mesh.c
+++ b/source/blender/makesrna/intern/rna_mesh.c
@@ -3451,20 +3451,17 @@ static void rna_def_mesh(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_remesh_preserve_paint_mask", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_REMESH_REPROJECT_PAINT_MASK);
- RNA_def_property_boolean_default(prop, false);
RNA_def_property_ui_text(prop, "Preserve Paint Mask", "Keep the current mask on the new mesh");
RNA_def_property_update(prop, 0, "rna_Mesh_update_draw");
prop = RNA_def_property(srna, "use_remesh_preserve_sculpt_face_sets", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_REMESH_REPROJECT_SCULPT_FACE_SETS);
- RNA_def_property_boolean_default(prop, false);
RNA_def_property_ui_text(
prop, "Preserve Face Sets", "Keep the current Face Sets on the new mesh");
RNA_def_property_update(prop, 0, "rna_Mesh_update_draw");
prop = RNA_def_property(srna, "use_remesh_preserve_vertex_colors", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_REMESH_REPROJECT_VERTEX_COLORS);
- RNA_def_property_boolean_default(prop, false);
RNA_def_property_ui_text(
prop, "Preserve Vertex Colors", "Keep the current vertex colors on the new mesh");
RNA_def_property_update(prop, 0, "rna_Mesh_update_draw");
diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c
index 54031f5a416..16045e6cddf 100644
--- a/source/blender/makesrna/intern/rna_modifier.c
+++ b/source/blender/makesrna/intern/rna_modifier.c
@@ -5825,7 +5825,7 @@ static void rna_def_modifier_ocean(BlenderRNA *brna)
prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_DIRPATH);
RNA_def_property_string_sdna(prop, NULL, "cachepath");
RNA_def_property_ui_text(prop, "Cache Path", "Path to a folder to store external baked images");
- /*RNA_def_property_update(prop, 0, "rna_Modifier_update"); */
+ // RNA_def_property_update(prop, 0, "rna_Modifier_update");
/* XXX how to update? */
RNA_define_lib_overridable(false);
diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c
index 358f88c8f4d..1fb41bf792f 100644
--- a/source/blender/makesrna/intern/rna_object.c
+++ b/source/blender/makesrna/intern/rna_object.c
@@ -2572,11 +2572,11 @@ static void rna_def_object_modifiers(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_property_pointer_sdna(prop, NULL, "act_edbone");
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Active EditBone", "Armatures active edit bone");
- /*RNA_def_property_update(prop, 0, "rna_Armature_act_editbone_update"); */
+ // RNA_def_property_update(prop, 0, "rna_Armature_act_editbone_update");
RNA_def_property_pointer_funcs(prop, NULL, "rna_Armature_act_edit_bone_set", NULL, NULL);
/* TODO: redraw. */
-/* RNA_def_property_collection_active(prop, prop_act); */
+ // RNA_def_property_collection_active(prop, prop_act);
# endif
/* add modifier */
@@ -3387,7 +3387,7 @@ static void rna_def_object(BlenderRNA *brna)
prop, "rna_Object_matrix_basis_get", "rna_Object_matrix_basis_set", NULL);
RNA_def_property_update(prop, NC_OBJECT | ND_TRANSFORM, "rna_Object_internal_update");
- /*parent_inverse*/
+ /* Parent_inverse. */
prop = RNA_def_property(srna, "matrix_parent_inverse", PROP_FLOAT, PROP_MATRIX);
RNA_def_property_float_sdna(prop, NULL, "parentinv");
RNA_def_property_multi_array(prop, 2, rna_matrix_dimsize_4x4);
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index d2b8eb203aa..c40a2ed9260 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -7143,6 +7143,13 @@ static void rna_def_space_node_overlay(BlenderRNA *brna)
RNA_def_property_boolean_default(prop, true);
RNA_def_property_ui_text(prop, "Show Tree Path", "Display breadcrumbs for the editor's context");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE, NULL);
+
+ prop = RNA_def_property(srna, "show_named_attributes", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "overlay.flag", SN_OVERLAY_SHOW_NAMED_ATTRIBUTES);
+ RNA_def_property_boolean_default(prop, true);
+ RNA_def_property_ui_text(
+ prop, "Show Named Attributes", "Show when nodes are using named attributes");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE, NULL);
}
static void rna_def_space_node(BlenderRNA *brna)
diff --git a/source/blender/makesrna/intern/rna_texture.c b/source/blender/makesrna/intern/rna_texture.c
index 56d1b48811a..6f7ee966723 100644
--- a/source/blender/makesrna/intern/rna_texture.c
+++ b/source/blender/makesrna/intern/rna_texture.c
@@ -1556,7 +1556,7 @@ static void rna_def_texture(BlenderRNA *brna)
RNA_def_struct_refine_func(srna, "rna_Texture_refine");
prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
- /*RNA_def_property_clear_flag(prop, PROP_EDITABLE); */
+ // RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_enum_sdna(prop, NULL, "type");
RNA_def_property_enum_items(prop, rna_enum_texture_type_items);
RNA_def_property_enum_funcs(prop, NULL, "rna_Texture_type_set", NULL);
diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c
index cf622818a3d..8c86e44aebf 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -6440,12 +6440,6 @@ static void rna_def_userdef_experimental(BlenderRNA *brna)
"Named Attribute Nodes",
"Enable named attribute nodes in the geometry nodes add menu");
- prop = RNA_def_property(srna, "use_select_nearest_on_first_click", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "use_select_nearest_on_first_click", 1);
- RNA_def_property_ui_text(prop,
- "Object Select Nearest on First Click",
- "When enabled, always select the front-most object on the first click");
-
prop = RNA_def_property(srna, "enable_eevee_next", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "enable_eevee_next", 1);
RNA_def_property_ui_text(prop, "EEVEE Next", "Enable the new EEVEE codebase, requires restart");
diff --git a/source/blender/modifiers/intern/MOD_laplaciansmooth.c b/source/blender/modifiers/intern/MOD_laplaciansmooth.c
index 11abe9a4d0a..95283b1cd20 100644
--- a/source/blender/modifiers/intern/MOD_laplaciansmooth.c
+++ b/source/blender/modifiers/intern/MOD_laplaciansmooth.c
@@ -60,7 +60,7 @@ struct BLaplacianSystem {
const MEdge *medges;
LinearSolver *context;
- /*Data*/
+ /* Data. */
float min_area;
float vert_centroid[3];
};
diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.cc b/source/blender/modifiers/intern/MOD_meshsequencecache.cc
index 7dbd4c5b260..998fb0a94a3 100644
--- a/source/blender/modifiers/intern/MOD_meshsequencecache.cc
+++ b/source/blender/modifiers/intern/MOD_meshsequencecache.cc
@@ -226,14 +226,13 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
velocity_scale *= FPS;
}
- result = ABC_read_mesh(mcmd->reader,
- ctx->object,
- mesh,
- time,
- &err_str,
- mcmd->read_flag,
- mcmd->cache_file->velocity_name,
- velocity_scale);
+ ABCReadParams params = {};
+ params.time = time;
+ params.read_flags = mcmd->read_flag;
+ params.velocity_name = mcmd->cache_file->velocity_name;
+ params.velocity_scale = velocity_scale;
+
+ result = ABC_read_mesh(mcmd->reader, ctx->object, mesh, &params, &err_str);
# endif
break;
}
diff --git a/source/blender/modifiers/intern/MOD_screw.c b/source/blender/modifiers/intern/MOD_screw.c
index e8fb667c572..4ace6404388 100644
--- a/source/blender/modifiers/intern/MOD_screw.c
+++ b/source/blender/modifiers/intern/MOD_screw.c
@@ -515,7 +515,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
mv_new = mvert_new;
if (ob_axis != NULL) {
- /*mtx_tx is initialized early on */
+ /* `mtx_tx` is initialized early on. */
for (i = 0; i < totvert; i++, mv_new++, mv_orig++, vc++) {
vc->co[0] = mv_new->co[0] = mv_orig->co[0];
vc->co[1] = mv_new->co[1] = mv_orig->co[1];
@@ -883,7 +883,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
copy_v3_v3(mv_new->co, mv_new_base->co);
/* only need to set these if using non cleared memory */
- /*mv_new->mat_nr = mv_new->flag = 0;*/
+ // mv_new->mat_nr = mv_new->flag = 0;
if (ob_axis != NULL) {
sub_v3_v3(mv_new->co, mtx_tx[3]);
diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh
index 96a1904abdd..bac4d0165e9 100644
--- a/source/blender/nodes/NOD_geometry_exec.hh
+++ b/source/blender/nodes/NOD_geometry_exec.hh
@@ -40,6 +40,7 @@ using fn::FieldInput;
using fn::FieldOperation;
using fn::GField;
using fn::ValueOrField;
+using geometry_nodes_eval_log::NamedAttributeUsage;
using geometry_nodes_eval_log::NodeWarningType;
/**
@@ -342,6 +343,8 @@ class GeoNodeExecParams {
void set_default_remaining_outputs();
+ void used_named_attribute(std::string attribute_name, NamedAttributeUsage usage);
+
private:
/* Utilities for detecting common errors at when using this class. */
void check_input_access(StringRef identifier, const CPPType *requested_type = nullptr) const;
diff --git a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh
index 16332be5179..1ad859aa47b 100644
--- a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh
+++ b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh
@@ -170,6 +170,24 @@ struct ValueOfSockets {
destruct_ptr<ValueLog> value;
};
+enum class NamedAttributeUsage {
+ None = 0,
+ Read = 1 << 0,
+ Write = 1 << 1,
+ Remove = 1 << 2,
+};
+ENUM_OPERATORS(NamedAttributeUsage, NamedAttributeUsage::Remove);
+
+struct UsedNamedAttribute {
+ std::string name;
+ NamedAttributeUsage usage;
+};
+
+struct NodeWithUsedNamedAttribute {
+ DNode node;
+ UsedNamedAttribute attribute;
+};
+
class GeoLogger;
class ModifierLog;
@@ -186,6 +204,7 @@ class LocalGeoLogger {
Vector<NodeWithWarning> node_warnings_;
Vector<NodeWithExecutionTime> node_exec_times_;
Vector<NodeWithDebugMessage> node_debug_messages_;
+ Vector<NodeWithUsedNamedAttribute> used_named_attributes_;
friend ModifierLog;
@@ -199,6 +218,7 @@ class LocalGeoLogger {
void log_multi_value_socket(DSocket socket, Span<GPointer> values);
void log_node_warning(DNode node, NodeWarningType type, std::string message);
void log_execution_time(DNode node, std::chrono::microseconds exec_time);
+ void log_used_named_attribute(DNode node, std::string attribute_name, NamedAttributeUsage usage);
/**
* Log a message that will be displayed in the node editor next to the node.
* This should only be used for debugging purposes and not to display information to users.
@@ -278,6 +298,7 @@ class NodeLog {
Vector<SocketLog> output_logs_;
Vector<NodeWarning, 0> warnings_;
Vector<std::string, 0> debug_messages_;
+ Vector<UsedNamedAttribute, 0> used_named_attributes_;
std::chrono::microseconds exec_time_;
friend ModifierLog;
@@ -307,6 +328,11 @@ class NodeLog {
return debug_messages_;
}
+ Span<UsedNamedAttribute> used_named_attributes() const
+ {
+ return used_named_attributes_;
+ }
+
std::chrono::microseconds execution_time() const
{
return exec_time_;
diff --git a/source/blender/nodes/NOD_node_tree_ref.hh b/source/blender/nodes/NOD_node_tree_ref.hh
index 3ed09de8fab..61d1d11d859 100644
--- a/source/blender/nodes/NOD_node_tree_ref.hh
+++ b/source/blender/nodes/NOD_node_tree_ref.hh
@@ -65,7 +65,6 @@ class SocketRef : NonCopyable, NonMovable {
bool is_input_;
int id_;
int index_;
- PointerRNA rna_;
Vector<LinkRef *> directly_linked_links_;
/* These sockets are linked directly, i.e. with a single link in between. */
@@ -101,7 +100,7 @@ class SocketRef : NonCopyable, NonMovable {
const InputSocketRef &as_input() const;
const OutputSocketRef &as_output() const;
- PointerRNA *rna() const;
+ PointerRNA rna() const;
StringRefNull idname() const;
StringRefNull name() const;
@@ -152,7 +151,6 @@ class NodeRef : NonCopyable, NonMovable {
private:
NodeTreeRef *tree_;
bNode *bnode_;
- PointerRNA rna_;
int id_;
Vector<InputSocketRef *> inputs_;
Vector<OutputSocketRef *> outputs_;
@@ -183,7 +181,7 @@ class NodeRef : NonCopyable, NonMovable {
bNode *bnode() const;
bNodeTree *btree() const;
- PointerRNA *rna() const;
+ PointerRNA rna() const;
StringRefNull idname() const;
StringRefNull name() const;
StringRefNull label() const;
@@ -410,11 +408,6 @@ inline const OutputSocketRef &SocketRef::as_output() const
return static_cast<const OutputSocketRef &>(*this);
}
-inline PointerRNA *SocketRef::rna() const
-{
- return const_cast<PointerRNA *>(&rna_);
-}
-
inline StringRefNull SocketRef::idname() const
{
return bsocket_->idname;
@@ -571,11 +564,6 @@ inline bNodeTree *NodeRef::btree() const
return tree_->btree();
}
-inline PointerRNA *NodeRef::rna() const
-{
- return const_cast<PointerRNA *>(&rna_);
-}
-
inline StringRefNull NodeRef::idname() const
{
return bnode_->idname;
diff --git a/source/blender/nodes/NOD_shader.h b/source/blender/nodes/NOD_shader.h
index f1c08f6a589..4996f12e27d 100644
--- a/source/blender/nodes/NOD_shader.h
+++ b/source/blender/nodes/NOD_shader.h
@@ -144,10 +144,7 @@ struct bNode *ntreeShaderOutputNode(struct bNodeTree *ntree, int target);
/**
* This one needs to work on a local tree.
*/
-void ntreeGPUMaterialNodes(struct bNodeTree *localtree,
- struct GPUMaterial *mat,
- bool *has_surface_output,
- bool *has_volume_output);
+void ntreeGPUMaterialNodes(struct bNodeTree *localtree, struct GPUMaterial *mat);
#ifdef __cplusplus
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
index e7a8c61290b..903a5e7c1d7 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
-#include "BKE_spline.hh"
+#include "BKE_curves.hh"
#include "BKE_curve_to_mesh.hh"
@@ -27,17 +27,18 @@ static void geometry_set_curve_to_mesh(GeometrySet &geometry_set,
const GeometrySet &profile_set,
const bool fill_caps)
{
- const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
- *geometry_set.get_curves_for_read());
+ const Curves &curves = *geometry_set.get_curves_for_read();
+
const Curves *profile_curves = profile_set.get_curves_for_read();
if (profile_curves == nullptr) {
- Mesh *mesh = bke::curve_to_wire_mesh(*curve);
+ Mesh *mesh = bke::curve_to_wire_mesh(bke::CurvesGeometry::wrap(curves.geometry));
geometry_set.replace_mesh(mesh);
}
else {
- const std::unique_ptr<CurveEval> profile_curve = curves_to_curve_eval(*profile_curves);
- Mesh *mesh = bke::curve_to_mesh_sweep(*curve, *profile_curve, fill_caps);
+ Mesh *mesh = bke::curve_to_mesh_sweep(bke::CurvesGeometry::wrap(curves.geometry),
+ bke::CurvesGeometry::wrap(profile_curves->geometry),
+ fill_caps);
geometry_set.replace_mesh(mesh);
}
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc
index 1e170dd5350..1b26cfe31fe 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc
@@ -759,6 +759,7 @@ static void duplicate_edges(GeometrySet &geometry_set,
MEdge &new_edge = new_edges[edge_range[i_duplicate]];
new_edge.v1 = vert_range[i_duplicate * 2];
new_edge.v2 = vert_range[i_duplicate * 2] + 1;
+ new_edge.flag = ME_LOOSEEDGE;
}
}
});
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc
index f6e2be9119c..6cb9ca642ef 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc
@@ -81,11 +81,13 @@ static void node_geo_exec(GeoNodeExecParams params)
const std::string name = params.extract_input<std::string>("Name");
- if (!U.experimental.use_named_attribute_nodes) {
+ if (!U.experimental.use_named_attribute_nodes || name.empty()) {
params.set_default_remaining_outputs();
return;
}
+ params.used_named_attribute(name, NamedAttributeUsage::Read);
+
switch (data_type) {
case CD_PROP_FLOAT:
params.set_output("Attribute_Float", AttributeFieldInput::Create<float>(std::move(name)));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc
index 202241affeb..f46b70c91a9 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc
@@ -32,6 +32,8 @@ static void node_geo_exec(GeoNodeExecParams params)
return;
}
+ params.used_named_attribute(name, NamedAttributeUsage::Remove);
+
std::atomic<bool> attribute_exists = false;
std::atomic<bool> cannot_delete = false;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc
index c48129acbdc..296b40c6c49 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_material.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc
@@ -5,6 +5,7 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "DNA_curves_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_pointcloud_types.h"
@@ -63,6 +64,7 @@ static void node_geo_exec(GeoNodeExecParams params)
/* Only add the warnings once, even if there are many unique instances. */
bool point_selection_warning = false;
bool volume_selection_warning = false;
+ bool curves_selection_warning = false;
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
if (geometry_set.has_mesh()) {
@@ -89,6 +91,12 @@ static void node_geo_exec(GeoNodeExecParams params)
point_selection_warning = true;
}
}
+ if (Curves *curves = geometry_set.get_curves_for_write()) {
+ BKE_id_material_eval_assign(&curves->id, 1, material);
+ if (selection_field.node().depends_on_input()) {
+ curves_selection_warning = true;
+ }
+ }
});
if (volume_selection_warning) {
@@ -101,6 +109,11 @@ static void node_geo_exec(GeoNodeExecParams params)
NodeWarningType::Info,
TIP_("Point clouds only support a single material; selection input can not be a field"));
}
+ if (curves_selection_warning) {
+ params.error_message_add(
+ NodeWarningType::Info,
+ TIP_("Curves only support a single material; selection input can not be a field"));
+ }
params.set_output("Geometry", std::move(geometry_set));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc
index 1d1446ce1bd..92614d1a31d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc
@@ -127,11 +127,13 @@ static void node_geo_exec(GeoNodeExecParams params)
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
std::string name = params.extract_input<std::string>("Name");
- if (!U.experimental.use_named_attribute_nodes) {
+ if (!U.experimental.use_named_attribute_nodes || name.empty()) {
params.set_output("Geometry", std::move(geometry_set));
return;
}
+ params.used_named_attribute(name, NamedAttributeUsage::Write);
+
const NodeGeometryStoreNamedAttribute &storage = node_storage(params.node());
const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
const AttributeDomain domain = static_cast<AttributeDomain>(storage.domain);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc
index bc34a1a6f2c..658de02fdab 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc
@@ -186,7 +186,7 @@ static TextLayout get_text_layout(GeoNodeExecParams &params)
params.extract_input<float>("Text Box Height");
VFont *vfont = (VFont *)params.node().id;
- Curve cu = {{nullptr}};
+ Curve cu = dna::shallow_zero_initialize();
cu.type = OB_FONT;
/* Set defaults */
cu.resolu = 12;
@@ -278,7 +278,7 @@ static Map<int, int> create_curve_instances(GeoNodeExecParams &params,
if (handles.contains(layout.char_codes[i])) {
continue;
}
- Curve cu = {{nullptr}};
+ Curve cu = dna::shallow_zero_initialize();
cu.type = OB_FONT;
cu.resolu = 12;
cu.vfont = vfont;
diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc
index 13f38c3352e..378bac894e8 100644
--- a/source/blender/nodes/intern/geometry_nodes_eval_log.cc
+++ b/source/blender/nodes/intern/geometry_nodes_eval_log.cc
@@ -62,6 +62,13 @@ ModifierLog::ModifierLog(GeoLogger &logger)
NodeLog &node_log = this->lookup_or_add_node_log(log_by_tree_context, debug_message.node);
node_log.debug_messages_.append(debug_message.message);
}
+
+ for (NodeWithUsedNamedAttribute &node_with_attribute_name :
+ local_logger.used_named_attributes_) {
+ NodeLog &node_log = this->lookup_or_add_node_log(log_by_tree_context,
+ node_with_attribute_name.node);
+ node_log.used_named_attributes_.append(std::move(node_with_attribute_name.attribute));
+ }
}
}
@@ -486,6 +493,13 @@ void LocalGeoLogger::log_execution_time(DNode node, std::chrono::microseconds ex
node_exec_times_.append({node, exec_time});
}
+void LocalGeoLogger::log_used_named_attribute(DNode node,
+ std::string attribute_name,
+ NamedAttributeUsage usage)
+{
+ used_named_attributes_.append({node, {std::move(attribute_name), usage}});
+}
+
void LocalGeoLogger::log_debug_message(DNode node, std::string message)
{
node_debug_messages_.append({node, std::move(message)});
diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc
index d63a6d11eda..cea3084a418 100644
--- a/source/blender/nodes/intern/node_geometry_exec.cc
+++ b/source/blender/nodes/intern/node_geometry_exec.cc
@@ -23,6 +23,16 @@ void GeoNodeExecParams::error_message_add(const NodeWarningType type, std::strin
local_logger.log_node_warning(provider_->dnode, type, std::move(message));
}
+void GeoNodeExecParams::used_named_attribute(std::string attribute_name,
+ const NamedAttributeUsage usage)
+{
+ if (provider_->logger == nullptr) {
+ return;
+ }
+ LocalGeoLogger &local_logger = provider_->logger->local();
+ local_logger.log_used_named_attribute(provider_->dnode, std::move(attribute_name), usage);
+}
+
void GeoNodeExecParams::check_input_geometry_set(StringRef identifier,
const GeometrySet &geometry_set) const
{
diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc
index 01a31ab852d..64a8690a869 100644
--- a/source/blender/nodes/intern/node_tree_ref.cc
+++ b/source/blender/nodes/intern/node_tree_ref.cc
@@ -21,7 +21,6 @@ NodeTreeRef::NodeTreeRef(bNodeTree *btree) : btree_(btree)
node.tree_ = this;
node.bnode_ = bnode;
node.id_ = nodes_by_id_.append_and_get_index(&node);
- RNA_pointer_create(&btree->id, &RNA_Node, bnode, &node.rna_);
LISTBASE_FOREACH (bNodeSocket *, bsocket, &bnode->inputs) {
InputSocketRef &socket = *allocator_.construct<InputSocketRef>().release();
@@ -30,7 +29,6 @@ NodeTreeRef::NodeTreeRef(bNodeTree *btree) : btree_(btree)
socket.is_input_ = true;
socket.bsocket_ = bsocket;
socket.id_ = sockets_by_id_.append_and_get_index(&socket);
- RNA_pointer_create(&btree->id, &RNA_NodeSocket, bsocket, &socket.rna_);
}
LISTBASE_FOREACH (bNodeSocket *, bsocket, &bnode->outputs) {
@@ -40,7 +38,6 @@ NodeTreeRef::NodeTreeRef(bNodeTree *btree) : btree_(btree)
socket.is_input_ = false;
socket.bsocket_ = bsocket;
socket.id_ = sockets_by_id_.append_and_get_index(&socket);
- RNA_pointer_create(&btree->id, &RNA_NodeSocket, bsocket, &socket.rna_);
}
LISTBASE_FOREACH (bNodeLink *, blink, &bnode->internal_links) {
@@ -664,4 +661,18 @@ const NodeTreeRef &get_tree_ref_from_map(NodeTreeRefMap &node_tree_refs, bNodeTr
[&]() { return std::make_unique<NodeTreeRef>(&btree); });
}
+PointerRNA NodeRef::rna() const
+{
+ PointerRNA rna;
+ RNA_pointer_create(&tree_->btree()->id, &RNA_Node, bnode_, &rna);
+ return rna;
+}
+
+PointerRNA SocketRef::rna() const
+{
+ PointerRNA rna;
+ RNA_pointer_create(&this->tree().btree()->id, &RNA_NodeSocket, bsocket_, &rna);
+ return rna;
+}
+
} // namespace blender::nodes
diff --git a/source/blender/nodes/shader/node_shader_tree.cc b/source/blender/nodes/shader/node_shader_tree.cc
index 03baebfd28b..d917106807c 100644
--- a/source/blender/nodes/shader/node_shader_tree.cc
+++ b/source/blender/nodes/shader/node_shader_tree.cc
@@ -16,11 +16,12 @@
#include "DNA_workspace_types.h"
#include "DNA_world_types.h"
-#include "BLI_alloca.h"
+#include "BLI_array.hh"
#include "BLI_linklist.h"
#include "BLI_listbase.h"
#include "BLI_threads.h"
#include "BLI_utildefines.h"
+#include "BLI_vector.hh"
#include "BLT_translation.h"
@@ -47,11 +48,8 @@
#include "node_shader_util.hh"
#include "node_util.h"
-struct nTreeTags {
- float ssr_id, sss_id;
-};
-
-static void ntree_shader_tag_nodes(bNodeTree *ntree, bNode *output_node, nTreeTags *tags);
+using blender::Array;
+using blender::Vector;
static bool shader_tree_poll(const bContext *C, bNodeTreeType *UNUSED(treetype))
{
@@ -250,6 +248,18 @@ static bNodeSocket *ntree_shader_node_find_output(bNode *node, const char *ident
return ntree_shader_node_find_socket(&node->outputs, identifier);
}
+/* Find input socket at a specific position. */
+static bNodeSocket *ntree_shader_node_input_get(bNode *node, int n)
+{
+ return reinterpret_cast<bNodeSocket *>(BLI_findlink(&node->inputs, n));
+}
+
+/* Find output socket at a specific position. */
+static bNodeSocket *ntree_shader_node_output_get(bNode *node, int n)
+{
+ return reinterpret_cast<bNodeSocket *>(BLI_findlink(&node->outputs, n));
+}
+
/* Return true on success. */
static bool ntree_shader_expand_socket_default(bNodeTree *localtree,
bNode *node,
@@ -522,125 +532,21 @@ static void ntree_shader_groups_flatten(bNodeTree *localtree)
BKE_ntree_update_main_tree(G.main, localtree, nullptr);
}
-/* Check whether shader has a displacement.
- *
- * Will also return a node and its socket which is connected to a displacement
- * output. Additionally, link which is attached to the displacement output is
- * also returned.
- */
-static bool ntree_shader_has_displacement(bNodeTree *ntree,
- bNode *output_node,
- bNode **r_node,
- bNodeSocket **r_socket,
- bNodeLink **r_link)
-{
- if (output_node == nullptr) {
- /* We can't have displacement without output node, apparently. */
- return false;
- }
- /* Make sure sockets links pointers are correct. */
- BKE_ntree_update_main_tree(G.main, ntree, nullptr);
- bNodeSocket *displacement = ntree_shader_node_find_input(output_node, "Displacement");
-
- if (displacement == nullptr) {
- /* Non-cycles node is used as an output. */
- return false;
- }
-
- if ((displacement->link != nullptr) && !(displacement->link->flag & NODE_LINK_MUTED)) {
- *r_node = displacement->link->fromnode;
- *r_socket = displacement->link->fromsock;
- *r_link = displacement->link;
- return true;
- }
- return false;
-}
-
-static void ntree_shader_relink_node_normal(bNodeTree *ntree,
- bNode *node,
- bNode *node_from,
- bNodeSocket *socket_from)
-{
- /* TODO(sergey): Can we do something smarter here than just a name-based
- * matching?
- */
- LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
- if (STREQ(sock->identifier, "Normal") && sock->link == nullptr) {
- /* It's a normal input and nothing is connected to it. */
- nodeAddLink(ntree, node_from, socket_from, node, sock);
- }
- else if (sock->link) {
- bNodeLink *link = sock->link;
- if (ELEM(link->fromnode->type, SH_NODE_NEW_GEOMETRY, SH_NODE_TEX_COORD) &&
- STREQ(link->fromsock->identifier, "Normal")) {
- /* Linked to a geometry node normal output. */
- nodeAddLink(ntree, node_from, socket_from, node, sock);
- }
- }
- }
-}
-
-/* Use specified node and socket as an input for unconnected normal sockets. */
-static void ntree_shader_link_builtin_normal(bNodeTree *ntree,
- bNode *node_from,
- bNodeSocket *socket_from)
-{
- LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
- if (node == node_from) {
- /* Don't connect node itself! */
- continue;
- }
- if (node->tmp_flag == -2) {
- /* This node is used inside the displacement tree. Skip to avoid cycles. */
- continue;
- }
- ntree_shader_relink_node_normal(ntree, node, node_from, socket_from);
- }
-}
-
-static void ntree_shader_bypass_bump_link(bNodeTree *ntree, bNode *bump_node, bNodeLink *bump_link)
-{
- /* Bypass bump nodes. This replicates cycles "implicit" behavior. */
- bNodeSocket *bump_normal_input = ntree_shader_node_find_input(bump_node, "Normal");
- bNode *fromnode;
- bNodeSocket *fromsock;
- /* Default to builtin normals if there is no link. */
- if (bump_normal_input->link) {
- fromsock = bump_normal_input->link->fromsock;
- fromnode = bump_normal_input->link->fromnode;
- }
- else {
- fromnode = nodeAddStaticNode(nullptr, ntree, SH_NODE_NEW_GEOMETRY);
- fromsock = ntree_shader_node_find_output(fromnode, "Normal");
- }
- /* Bypass the bump node by creating a link between the previous and next node. */
- nodeAddLink(ntree, fromnode, fromsock, bump_link->tonode, bump_link->tosock);
- nodeRemLink(ntree, bump_link);
-}
-
-static void ntree_shader_bypass_tagged_bump_nodes(bNodeTree *ntree)
-{
- /* Bypass bump links inside copied nodes */
- LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
- bNode *node = link->fromnode;
- /* If node is a copy. */
- if (node->tmp_flag == -2 && node->type == SH_NODE_BUMP) {
- ntree_shader_bypass_bump_link(ntree, node, link);
- }
- }
- BKE_ntree_update_main_tree(G.main, ntree, nullptr);
-}
+struct branchIterData {
+ bool (*node_filter)(const bNode *node);
+ int node_count;
+};
static bool ntree_branch_count_and_tag_nodes(bNode *fromnode, bNode *tonode, void *userdata)
{
- int *node_count = (int *)userdata;
- if (fromnode->tmp_flag == -1) {
- fromnode->tmp_flag = *node_count;
- (*node_count)++;
+ branchIterData *iter = (branchIterData *)userdata;
+ if (fromnode->tmp_flag == -1 && (iter->node_filter == nullptr || iter->node_filter(fromnode))) {
+ fromnode->tmp_flag = iter->node_count;
+ iter->node_count++;
}
- if (tonode->tmp_flag == -1) {
- tonode->tmp_flag = *node_count;
- (*node_count)++;
+ if (tonode->tmp_flag == -1 && (iter->node_filter == nullptr || iter->node_filter(tonode))) {
+ tonode->tmp_flag = iter->node_count;
+ iter->node_count++;
}
return true;
}
@@ -650,6 +556,7 @@ static bool ntree_branch_count_and_tag_nodes(bNode *fromnode, bNode *tonode, voi
* Returns input node copy. */
static bNode *ntree_shader_copy_branch(bNodeTree *ntree,
bNode *start_node,
+ bool (*node_filter)(const bNode *node),
void (*callback)(bNode *node, int user_data),
int user_data)
{
@@ -659,10 +566,12 @@ static bNode *ntree_shader_copy_branch(bNodeTree *ntree,
}
/* Count and tag all nodes inside the displacement branch of the tree. */
start_node->tmp_flag = 0;
- int node_count = 1;
- nodeChainIterBackwards(ntree, start_node, ntree_branch_count_and_tag_nodes, &node_count, 1);
+ branchIterData iter_data;
+ iter_data.node_filter = node_filter;
+ iter_data.node_count = 1;
+ nodeChainIterBackwards(ntree, start_node, ntree_branch_count_and_tag_nodes, &iter_data, 1);
/* Make a full copy of the branch */
- bNode **nodes_copy = static_cast<bNode **>(MEM_mallocN(sizeof(bNode *) * node_count, __func__));
+ Array<bNode *> nodes_copy(iter_data.node_count);
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->tmp_flag >= 0) {
int id = node->tmp_flag;
@@ -690,225 +599,447 @@ static bNode *ntree_shader_copy_branch(bNodeTree *ntree,
}
/* Per node callback. */
if (callback) {
- for (int i = 0; i < node_count; i++) {
+ for (int i = 0; i < iter_data.node_count; i++) {
callback(nodes_copy[i], user_data);
}
}
bNode *start_node_copy = nodes_copy[start_node->tmp_flag];
- MEM_freeN(nodes_copy);
return start_node_copy;
}
-static void ntree_shader_copy_branch_displacement(bNodeTree *ntree,
- bNode *displacement_node,
- bNodeSocket *displacement_socket,
- bNodeLink *displacement_link)
+/* Generate emission node to convert regular data to closure sockets.
+ * Returns validity of the tree.
+ */
+static bool ntree_shader_implicit_closure_cast(bNodeTree *ntree)
{
- /* Replace displacement socket/node/link. */
- bNode *tonode = displacement_link->tonode;
- bNodeSocket *tosock = displacement_link->tosock;
- displacement_node = ntree_shader_copy_branch(ntree, displacement_node, nullptr, 0);
- displacement_socket = ntree_shader_node_find_output(displacement_node,
- displacement_socket->identifier);
- nodeRemLink(ntree, displacement_link);
- nodeAddLink(ntree, displacement_node, displacement_socket, tonode, tosock);
-
- BKE_ntree_update_main_tree(G.main, ntree, nullptr);
+ bool modified = false;
+ LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
+ if ((link->fromsock->type != SOCK_SHADER) && (link->tosock->type == SOCK_SHADER)) {
+ bNode *emission_node = nodeAddStaticNode(NULL, ntree, SH_NODE_EMISSION);
+ bNodeSocket *in_sock = ntree_shader_node_find_input(emission_node, "Color");
+ bNodeSocket *out_sock = ntree_shader_node_find_output(emission_node, "Emission");
+ nodeAddLink(ntree, link->fromnode, link->fromsock, emission_node, in_sock);
+ nodeAddLink(ntree, emission_node, out_sock, link->tonode, link->tosock);
+ nodeRemLink(ntree, link);
+ modified = true;
+ }
+ else if ((link->fromsock->type == SOCK_SHADER) && (link->tosock->type != SOCK_SHADER)) {
+ /* Meh. Not directly visible to the user. But better than nothing. */
+ fprintf(stderr, "Shader Nodetree Error: Invalid implicit socket conversion\n");
+ BKE_ntree_update_main_tree(G.main, ntree, nullptr);
+ return false;
+ }
+ }
+ if (modified) {
+ BKE_ntree_update_main_tree(G.main, ntree, nullptr);
+ }
+ return true;
}
-/* Re-link displacement output to unconnected normal sockets via bump node.
- * This way material with have proper displacement in the viewport.
- */
-static void ntree_shader_relink_displacement(bNodeTree *ntree, bNode *output_node)
-{
- bNode *displacement_node;
- bNodeSocket *displacement_socket;
- bNodeLink *displacement_link;
- if (!ntree_shader_has_displacement(
- ntree, output_node, &displacement_node, &displacement_socket, &displacement_link)) {
- /* There is no displacement output connected, nothing to re-link. */
- return;
- }
-
- /* Copy the whole displacement branch to avoid cyclic dependency
- * and issue when bypassing bump nodes. */
- ntree_shader_copy_branch_displacement(
- ntree, displacement_node, displacement_socket, displacement_link);
- /* Bypass bump nodes inside the copied branch to mimic cycles behavior. */
- ntree_shader_bypass_tagged_bump_nodes(ntree);
-
- /* Displacement Node may have changed because of branch copy and bump bypass. */
- ntree_shader_has_displacement(
- ntree, output_node, &displacement_node, &displacement_socket, &displacement_link);
-
- /* We have to disconnect displacement output socket, otherwise we'll have
- * cycles in the Cycles material :)
- */
- nodeRemLink(ntree, displacement_link);
-
- /* Convert displacement vector to bump height. */
- bNode *dot_node = nodeAddStaticNode(nullptr, ntree, SH_NODE_VECTOR_MATH);
- bNode *geo_node = nodeAddStaticNode(nullptr, ntree, SH_NODE_NEW_GEOMETRY);
- bNodeSocket *normal_socket = ntree_shader_node_find_output(geo_node, "Normal");
- bNodeSocket *dot_input1 = static_cast<bNodeSocket *>(dot_node->inputs.first);
- bNodeSocket *dot_input2 = static_cast<bNodeSocket *>(dot_input1->next);
- dot_node->custom1 = NODE_VECTOR_MATH_DOT_PRODUCT;
-
- nodeAddLink(ntree, displacement_node, displacement_socket, dot_node, dot_input1);
- nodeAddLink(ntree, geo_node, normal_socket, dot_node, dot_input2);
- displacement_node = dot_node;
- displacement_socket = ntree_shader_node_find_output(dot_node, "Value");
-
- /* We can't connect displacement to normal directly, use bump node for that
- * and hope that it gives good enough approximation.
- */
- bNode *bump_node = nodeAddStaticNode(nullptr, ntree, SH_NODE_BUMP);
- bNodeSocket *bump_input_socket = ntree_shader_node_find_input(bump_node, "Height");
- bNodeSocket *bump_output_socket = ntree_shader_node_find_output(bump_node, "Normal");
- BLI_assert(bump_input_socket != nullptr);
- BLI_assert(bump_output_socket != nullptr);
- /* Connect bump node to where displacement output was originally
- * connected to.
- */
- nodeAddLink(ntree, displacement_node, displacement_socket, bump_node, bump_input_socket);
-
- /* Tag as part of the new displacement tree. */
- dot_node->tmp_flag = -2;
- geo_node->tmp_flag = -2;
- bump_node->tmp_flag = -2;
-
- BKE_ntree_update_main_tree(G.main, ntree, nullptr);
-
- /* Connect all free-standing Normal inputs and relink geometry/coordinate nodes. */
- ntree_shader_link_builtin_normal(ntree, bump_node, bump_output_socket);
- /* We modified the tree, it needs to be updated now. */
- BKE_ntree_update_main_tree(G.main, ntree, nullptr);
+/* Socket already has a link to it. Add weights together. */
+static void ntree_weight_tree_merge_weight(bNodeTree *ntree,
+ bNode *UNUSED(fromnode),
+ bNodeSocket *fromsock,
+ bNode **tonode,
+ bNodeSocket **tosock)
+{
+ bNode *addnode = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH);
+ addnode->custom1 = NODE_MATH_ADD;
+ addnode->tmp_flag = -2; /* Copy */
+ bNodeSocket *addsock_out = ntree_shader_node_output_get(addnode, 0);
+ bNodeSocket *addsock_in0 = ntree_shader_node_input_get(addnode, 0);
+ bNodeSocket *addsock_in1 = ntree_shader_node_input_get(addnode, 1);
+ bNodeLink *oldlink = fromsock->link;
+ nodeAddLink(ntree, oldlink->fromnode, oldlink->fromsock, addnode, addsock_in0);
+ nodeAddLink(ntree, *tonode, *tosock, addnode, addsock_in1);
+ nodeRemLink(ntree, oldlink);
+ *tonode = addnode;
+ *tosock = addsock_out;
}
-static void node_tag_branch_as_derivative(bNode *node, int dx)
+static bool ntree_weight_tree_tag_nodes(bNode *fromnode, bNode *tonode, void *userdata)
{
- if (dx) {
- node->branch_tag = 1;
+ int *node_count = (int *)userdata;
+ bool to_node_from_weight_tree = ELEM(tonode->type,
+ SH_NODE_ADD_SHADER,
+ SH_NODE_MIX_SHADER,
+ SH_NODE_OUTPUT_WORLD,
+ SH_NODE_OUTPUT_MATERIAL,
+ SH_NODE_SHADERTORGB);
+ if (tonode->tmp_flag == -1 && to_node_from_weight_tree) {
+ tonode->tmp_flag = *node_count;
+ *node_count += (tonode->type == SH_NODE_MIX_SHADER) ? 4 : 1;
}
- else {
- node->branch_tag = 2;
+ if (fromnode->tmp_flag == -1 && ELEM(fromnode->type, SH_NODE_ADD_SHADER, SH_NODE_MIX_SHADER)) {
+ fromnode->tmp_flag = *node_count;
+ *node_count += (fromnode->type == SH_NODE_MIX_SHADER) ? 4 : 1;
}
+ return to_node_from_weight_tree;
}
-static bool ntree_shader_bump_branches(bNode *fromnode, bNode *UNUSED(tonode), void *userdata)
+/* Invert evaluation order of the weight tree (add & mix closure nodes) to feed the closure nodes
+ * with their respective weights. */
+static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node)
{
- bNodeTree *ntree = (bNodeTree *)userdata;
+ bNodeLink *displace_link = NULL;
+ bNodeSocket *displace_output = ntree_shader_node_find_input(output_node, "Displacement");
+ if (displace_output && displace_output->link) {
+ /* Remove any displacement link to avoid tagging it later on. */
+ displace_link = displace_output->link;
+ displace_output->link = NULL;
+ }
+ bNodeLink *thickness_link = NULL;
+ bNodeSocket *thickness_output = ntree_shader_node_find_input(output_node, "Thickness");
+ if (thickness_output && thickness_output->link) {
+ /* Remove any thickness link to avoid tagging it later on. */
+ thickness_link = thickness_output->link;
+ thickness_output->link = NULL;
+ }
+ /* Init tmp flag. */
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ node->tmp_flag = -1;
+ }
+ /* Tag nodes from the weight tree. Only tag output node and mix/add shader nodes. */
+ output_node->tmp_flag = 0;
+ int node_count = 1;
+ nodeChainIterBackwards(ntree, output_node, ntree_weight_tree_tag_nodes, &node_count, 0);
+ /* Make a mirror copy of the weight tree. */
+ Array<bNode *> nodes_copy(node_count);
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ if (node->tmp_flag >= 0) {
+ int id = node->tmp_flag;
- if (fromnode->type == SH_NODE_BUMP) {
- bNodeSocket *height_dx_sock, *height_dy_sock, *bump_socket, *bump_dx_socket, *bump_dy_socket;
- bNode *bump = fromnode;
- bump_socket = ntree_shader_node_find_input(bump, "Height");
- bump_dx_socket = ntree_shader_node_find_input(bump, "Height_dx");
- bump_dy_socket = ntree_shader_node_find_input(bump, "Height_dy");
- if (bump_dx_socket->link) {
- /* Avoid reconnecting the same bump twice. */
+ switch (node->type) {
+ case SH_NODE_SHADERTORGB:
+ case SH_NODE_OUTPUT_WORLD:
+ case SH_NODE_OUTPUT_MATERIAL: {
+ /* Start the tree with full weight. */
+ nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_VALUE);
+ nodes_copy[id]->tmp_flag = -2; /* Copy */
+ ((bNodeSocketValueFloat *)ntree_shader_node_output_get(nodes_copy[id], 0)->default_value)
+ ->value = 1.0f;
+ break;
+ }
+ case SH_NODE_ADD_SHADER: {
+ /* Simple passthrough node. Each original inputs will get the same weight. */
+ /* TODO(fclem) Better use some kind of reroute node? */
+ nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH);
+ nodes_copy[id]->custom1 = NODE_MATH_ADD;
+ nodes_copy[id]->tmp_flag = -2; /* Copy */
+ ((bNodeSocketValueFloat *)ntree_shader_node_input_get(nodes_copy[id], 0)->default_value)
+ ->value = 0.0f;
+ break;
+ }
+ case SH_NODE_MIX_SHADER: {
+ /* We need multiple nodes to emulate the mix node in reverse. */
+ bNode *fromnode, *tonode;
+ bNodeSocket *fromsock, *tosock;
+ int id_start = id;
+ /* output = (factor * input_weight) */
+ nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH);
+ nodes_copy[id]->custom1 = NODE_MATH_MULTIPLY;
+ nodes_copy[id]->tmp_flag = -2; /* Copy */
+ id++;
+ /* output = ((1.0 - factor) * input_weight) <=> (input_weight - factor * input_weight) */
+ nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH);
+ nodes_copy[id]->custom1 = NODE_MATH_SUBTRACT;
+ nodes_copy[id]->tmp_flag = -2; /* Copy */
+ id++;
+ /* Node sanitizes the input mix factor by clamping it. */
+ nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH);
+ nodes_copy[id]->custom1 = NODE_MATH_ADD;
+ nodes_copy[id]->custom2 = SHD_MATH_CLAMP;
+ nodes_copy[id]->tmp_flag = -2; /* Copy */
+ ((bNodeSocketValueFloat *)ntree_shader_node_input_get(nodes_copy[id], 0)->default_value)
+ ->value = 0.0f;
+ /* Copy default value if no link present. */
+ bNodeSocket *fac_sock = ntree_shader_node_find_input(node, "Fac");
+ if (!fac_sock->link) {
+ float default_value = ((bNodeSocketValueFloat *)fac_sock->default_value)->value;
+ bNodeSocket *dst_sock = ntree_shader_node_input_get(nodes_copy[id], 1);
+ ((bNodeSocketValueFloat *)dst_sock->default_value)->value = default_value;
+ }
+ id++;
+ /* Reroute the weight input to the 3 processing nodes. Simplify linking later-on. */
+ /* TODO(fclem) Better use some kind of reroute node? */
+ nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH);
+ nodes_copy[id]->custom1 = NODE_MATH_ADD;
+ nodes_copy[id]->tmp_flag = -2; /* Copy */
+ ((bNodeSocketValueFloat *)ntree_shader_node_input_get(nodes_copy[id], 0)->default_value)
+ ->value = 0.0f;
+ id++;
+ /* Link between nodes for the substraction. */
+ fromnode = nodes_copy[id_start];
+ tonode = nodes_copy[id_start + 1];
+ fromsock = ntree_shader_node_output_get(fromnode, 0);
+ tosock = ntree_shader_node_input_get(tonode, 1);
+ nodeAddLink(ntree, fromnode, fromsock, tonode, tosock);
+ /* Link mix input to first node. */
+ fromnode = nodes_copy[id_start + 2];
+ tonode = nodes_copy[id_start];
+ fromsock = ntree_shader_node_output_get(fromnode, 0);
+ tosock = ntree_shader_node_input_get(tonode, 1);
+ nodeAddLink(ntree, fromnode, fromsock, tonode, tosock);
+ /* Link weight input to both multiply nodes. */
+ fromnode = nodes_copy[id_start + 3];
+ fromsock = ntree_shader_node_output_get(fromnode, 0);
+ tonode = nodes_copy[id_start];
+ tosock = ntree_shader_node_input_get(tonode, 0);
+ nodeAddLink(ntree, fromnode, fromsock, tonode, tosock);
+ tonode = nodes_copy[id_start + 1];
+ tosock = ntree_shader_node_input_get(tonode, 0);
+ nodeAddLink(ntree, fromnode, fromsock, tonode, tosock);
+ break;
+ }
+ default:
+ BLI_assert(0);
+ break;
+ }
}
- else if (bump_socket && bump_socket->link) {
- bNodeLink *link = bump_socket->link;
- bNode *height = link->fromnode;
- bNode *height_dx = ntree_shader_copy_branch(ntree, height, node_tag_branch_as_derivative, 1);
- bNode *height_dy = ntree_shader_copy_branch(ntree, height, node_tag_branch_as_derivative, 0);
- height_dx_sock = ntree_shader_node_find_output(height_dx, link->fromsock->identifier);
- height_dy_sock = ntree_shader_node_find_output(height_dy, link->fromsock->identifier);
- nodeAddLink(ntree, height_dx, height_dx_sock, bump, bump_dx_socket);
- nodeAddLink(ntree, height_dy, height_dy_sock, bump, bump_dy_socket);
- /* We could end iter here, but other bump node could be plugged into other input sockets. */
+ }
+ /* Recreate links between copied nodes. */
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ if (node->tmp_flag >= 0) {
+ /* Naming can be confusing here. We use original nodelink name for from/to prefix.
+ * The final link is in reversed order. */
+ int socket_index;
+ LISTBASE_FOREACH_INDEX (bNodeSocket *, sock, &node->inputs, socket_index) {
+ bNodeSocket *tosock;
+ bNode *tonode;
+
+ switch (node->type) {
+ case SH_NODE_SHADERTORGB:
+ case SH_NODE_OUTPUT_WORLD:
+ case SH_NODE_OUTPUT_MATERIAL:
+ case SH_NODE_ADD_SHADER: {
+ tonode = nodes_copy[node->tmp_flag];
+ tosock = ntree_shader_node_output_get(tonode, 0);
+ break;
+ }
+ case SH_NODE_MIX_SHADER: {
+ if (socket_index == 0) {
+ /* Mix Factor. */
+ tonode = nodes_copy[node->tmp_flag + 2];
+ tosock = ntree_shader_node_input_get(tonode, 1);
+ }
+ else if (socket_index == 1) {
+ /* Shader 1. */
+ tonode = nodes_copy[node->tmp_flag + 1];
+ tosock = ntree_shader_node_output_get(tonode, 0);
+ }
+ else {
+ /* Shader 2. */
+ tonode = nodes_copy[node->tmp_flag];
+ tosock = ntree_shader_node_output_get(tonode, 0);
+ }
+ break;
+ }
+ default:
+ BLI_assert(0);
+ break;
+ }
+
+ if (sock->link) {
+ bNodeSocket *fromsock;
+ bNode *fromnode = sock->link->fromnode;
+
+ switch (fromnode->type) {
+ case SH_NODE_ADD_SHADER: {
+ fromnode = nodes_copy[fromnode->tmp_flag];
+ fromsock = ntree_shader_node_input_get(fromnode, 1);
+ if (fromsock->link) {
+ ntree_weight_tree_merge_weight(ntree, fromnode, fromsock, &tonode, &tosock);
+ }
+ break;
+ }
+ case SH_NODE_MIX_SHADER: {
+ fromnode = nodes_copy[fromnode->tmp_flag + 3];
+ fromsock = ntree_shader_node_input_get(fromnode, 1);
+ if (fromsock->link) {
+ ntree_weight_tree_merge_weight(ntree, fromnode, fromsock, &tonode, &tosock);
+ }
+ break;
+ }
+ case SH_NODE_BACKGROUND:
+ case SH_NODE_BSDF_ANISOTROPIC:
+ case SH_NODE_BSDF_DIFFUSE:
+ case SH_NODE_BSDF_GLASS:
+ case SH_NODE_BSDF_GLOSSY:
+ case SH_NODE_BSDF_HAIR_PRINCIPLED:
+ case SH_NODE_BSDF_HAIR:
+ case SH_NODE_BSDF_PRINCIPLED:
+ case SH_NODE_BSDF_REFRACTION:
+ case SH_NODE_BSDF_TOON:
+ case SH_NODE_BSDF_TRANSLUCENT:
+ case SH_NODE_BSDF_TRANSPARENT:
+ case SH_NODE_BSDF_VELVET:
+ case SH_NODE_EEVEE_SPECULAR:
+ case SH_NODE_EMISSION:
+ case SH_NODE_HOLDOUT:
+ case SH_NODE_SUBSURFACE_SCATTERING:
+ case SH_NODE_VOLUME_ABSORPTION:
+ case SH_NODE_VOLUME_PRINCIPLED:
+ case SH_NODE_VOLUME_SCATTER:
+ fromsock = ntree_shader_node_find_input(fromnode, "Weight");
+ if (fromsock->link) {
+ ntree_weight_tree_merge_weight(ntree, fromnode, fromsock, &tonode, &tosock);
+ }
+ break;
+ default:
+ fromsock = sock->link->fromsock;
+ break;
+ }
+
+ /* Manually add the link to the socket to avoid calling
+ * BKE_ntree_update_main_tree(G.main, oop, nullptr. */
+ fromsock->link = nodeAddLink(ntree, fromnode, fromsock, tonode, tosock);
+ BLI_assert(fromsock->link);
+ }
+ }
}
}
- return true;
+ /* Restore displacement & thickness link. */
+ if (displace_link) {
+ nodeAddLink(
+ ntree, displace_link->fromnode, displace_link->fromsock, output_node, displace_output);
+ }
+ if (thickness_link) {
+ nodeAddLink(
+ ntree, thickness_link->fromnode, thickness_link->fromsock, output_node, thickness_output);
+ }
+ BKE_ntree_update_main_tree(G.main, ntree, nullptr);
}
-static bool ntree_tag_bsdf_cb(bNode *fromnode, bNode *UNUSED(tonode), void *userdata)
+static bool closure_node_filter(const bNode *node)
{
- switch (fromnode->type) {
+ switch (node->type) {
+ case SH_NODE_ADD_SHADER:
+ case SH_NODE_MIX_SHADER:
+ case SH_NODE_BACKGROUND:
case SH_NODE_BSDF_ANISOTROPIC:
- case SH_NODE_EEVEE_SPECULAR:
- case SH_NODE_BSDF_GLOSSY:
+ case SH_NODE_BSDF_DIFFUSE:
case SH_NODE_BSDF_GLASS:
- fromnode->ssr_id = ((nTreeTags *)userdata)->ssr_id;
- ((nTreeTags *)userdata)->ssr_id += 1;
- break;
- case SH_NODE_SUBSURFACE_SCATTERING:
- fromnode->sss_id = ((nTreeTags *)userdata)->sss_id;
- ((nTreeTags *)userdata)->sss_id += 1;
- break;
+ case SH_NODE_BSDF_GLOSSY:
+ case SH_NODE_BSDF_HAIR_PRINCIPLED:
+ case SH_NODE_BSDF_HAIR:
case SH_NODE_BSDF_PRINCIPLED:
- fromnode->ssr_id = ((nTreeTags *)userdata)->ssr_id;
- fromnode->sss_id = ((nTreeTags *)userdata)->sss_id;
- ((nTreeTags *)userdata)->sss_id += 1;
- ((nTreeTags *)userdata)->ssr_id += 1;
- break;
+ case SH_NODE_BSDF_REFRACTION:
+ case SH_NODE_BSDF_TOON:
+ case SH_NODE_BSDF_TRANSLUCENT:
+ case SH_NODE_BSDF_TRANSPARENT:
+ case SH_NODE_BSDF_VELVET:
+ case SH_NODE_EEVEE_SPECULAR:
+ case SH_NODE_EMISSION:
+ case SH_NODE_HOLDOUT:
+ case SH_NODE_SUBSURFACE_SCATTERING:
+ case SH_NODE_VOLUME_ABSORPTION:
+ case SH_NODE_VOLUME_PRINCIPLED:
+ case SH_NODE_VOLUME_SCATTER:
+ return true;
default:
- /* We could return false here but since we
- * allow the use of Closure as RGBA, we can have
- * BSDF nodes linked to other BSDF nodes. */
- break;
+ return false;
}
+}
+static bool shader_to_rgba_node_gather(bNode *UNUSED(fromnode), bNode *tonode, void *userdata)
+{
+ Vector<bNode *> &shader_to_rgba_nodes = *(Vector<bNode *> *)userdata;
+ if (tonode->tmp_flag == -1 && tonode->type == SH_NODE_SHADERTORGB) {
+ tonode->tmp_flag = 0;
+ shader_to_rgba_nodes.append(tonode);
+ }
return true;
}
-/* EEVEE: Scan the ntree to set the Screen Space Reflection
- * layer id of every specular node AND the Subsurface Scattering id of every SSS node.
- */
-void ntree_shader_tag_nodes(bNodeTree *ntree, bNode *output_node, nTreeTags *tags)
+/* Shader to rgba needs their associated closure duplicated and the weight tree generated for. */
+static void ntree_shader_shader_to_rgba_branch(bNodeTree *ntree, bNode *output_node)
{
- if (output_node == nullptr) {
- return;
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ node->tmp_flag = -1;
}
- /* Make sure sockets links pointers are correct. */
- BKE_ntree_update_main_tree(G.main, ntree, nullptr);
+ /* First gather the shader_to_rgba nodes linked to the ouput. This is separate to avoid
+ * conflicting usage of the node->tmp_flag. */
+ Vector<bNode *> shader_to_rgba_nodes;
+ nodeChainIterBackwards(ntree, output_node, shader_to_rgba_node_gather, &shader_to_rgba_nodes, 0);
+
+ for (bNode *shader_to_rgba : shader_to_rgba_nodes) {
+ bNodeSocket *closure_input = ntree_shader_node_input_get(shader_to_rgba, 0);
+ if (closure_input->link == nullptr) {
+ continue;
+ }
+ bNode *start_node = closure_input->link->fromnode;
+ bNode *start_node_copy = ntree_shader_copy_branch(
+ ntree, start_node, closure_node_filter, nullptr, 0);
+ /* Replace node copy link. This assumes that every node possibly connected to the closure input
+ * has only one ouput. */
+ bNodeSocket *closure_output = ntree_shader_node_output_get(start_node_copy, 0);
+ nodeRemLink(ntree, closure_input->link);
+ nodeAddLink(ntree, start_node_copy, closure_output, shader_to_rgba, closure_input);
+ BKE_ntree_update_main_tree(G.main, ntree, nullptr);
+
+ ntree_shader_weight_tree_invert(ntree, shader_to_rgba);
+ }
+}
- nodeChainIterBackwards(ntree, output_node, ntree_tag_bsdf_cb, tags, 0);
+static bool ntree_branch_node_tag(bNode *fromnode, bNode *tonode, void *UNUSED(userdata))
+{
+ fromnode->tmp_flag = 1;
+ tonode->tmp_flag = 1;
+ return true;
}
-void ntreeGPUMaterialNodes(bNodeTree *localtree,
- GPUMaterial *mat,
- bool *has_surface_output,
- bool *has_volume_output)
+/* Avoid adding more node execution when multiple outputs are present. */
+/* NOTE(@fclem): This is also a workaround for the old EEVEE SSS implementation where only the
+ * first executed SSS node gets a SSS profile. */
+static void ntree_shader_pruned_unused(bNodeTree *ntree, bNode *output_node)
{
- bNodeTreeExec *exec;
+ bool changed = false;
- bNode *output = ntreeShaderOutputNode(localtree, SHD_OUTPUT_EEVEE);
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ node->tmp_flag = 0;
+ }
- ntree_shader_groups_remove_muted_links(localtree);
- ntree_shader_groups_expand_inputs(localtree);
+ /* Avoid deleting the output node if it is the only node in the tree. */
+ output_node->tmp_flag = 1;
- ntree_shader_groups_flatten(localtree);
+ nodeChainIterBackwards(ntree, output_node, ntree_branch_node_tag, nullptr, 0);
- if (output == nullptr) {
- /* Search again, now including flattened nodes. */
- output = ntreeShaderOutputNode(localtree, SHD_OUTPUT_EEVEE);
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ if (node->type == SH_NODE_OUTPUT_AOV) {
+ nodeChainIterBackwards(ntree, node, ntree_branch_node_tag, nullptr, 0);
+ }
}
- /* Perform all needed modifications on the tree in order to support
- * displacement/bump mapping.
- */
- ntree_shader_relink_displacement(localtree, output);
-
- /* Duplicate bump height branches for manual derivatives.
- */
- nodeChainIterBackwards(localtree, output, ntree_shader_bump_branches, localtree, 0);
- LISTBASE_FOREACH (bNode *, node, &localtree->nodes) {
- if (node->type == SH_NODE_OUTPUT_AOV) {
- nodeChainIterBackwards(localtree, node, ntree_shader_bump_branches, localtree, 0);
- nTreeTags tags = {};
- tags.ssr_id = 1.0;
- tags.sss_id = 1.0;
- ntree_shader_tag_nodes(localtree, node, &tags);
+ LISTBASE_FOREACH_MUTABLE (bNode *, node, &ntree->nodes) {
+ if (node->tmp_flag == 0) {
+ ntreeFreeLocalNode(ntree, node);
+ changed = true;
}
}
- /* TODO(fclem): consider moving this to the gpu shader tree evaluation. */
- nTreeTags tags = {};
- tags.ssr_id = 1.0;
- tags.sss_id = 1.0;
- ntree_shader_tag_nodes(localtree, output, &tags);
+ if (changed) {
+ BKE_ntree_update_main_tree(G.main, ntree, nullptr);
+ }
+}
+
+void ntreeGPUMaterialNodes(bNodeTree *localtree, GPUMaterial *mat)
+{
+ bNodeTreeExec *exec;
+
+ ntree_shader_groups_remove_muted_links(localtree);
+ ntree_shader_groups_expand_inputs(localtree);
+ ntree_shader_groups_flatten(localtree);
+
+ bNode *output = ntreeShaderOutputNode(localtree, SHD_OUTPUT_EEVEE);
+
+ /* Tree is valid if it contains no undefined implicit socket type cast. */
+ bool valid_tree = ntree_shader_implicit_closure_cast(localtree);
+
+ if (valid_tree && output != NULL) {
+ ntree_shader_pruned_unused(localtree, output);
+ ntree_shader_shader_to_rgba_branch(localtree, output);
+ ntree_shader_weight_tree_invert(localtree, output);
+ }
exec = ntreeShaderBeginExecTree(localtree);
ntreeExecGPUNodes(exec, mat, output);
@@ -918,23 +1049,6 @@ void ntreeGPUMaterialNodes(bNodeTree *localtree,
}
}
ntreeShaderEndExecTree(exec);
-
- /* EEVEE: Find which material domain was used (volume, surface ...). */
- *has_surface_output = false;
- *has_volume_output = false;
-
- if (output != nullptr) {
- bNodeSocket *surface_sock = ntree_shader_node_find_input(output, "Surface");
- bNodeSocket *volume_sock = ntree_shader_node_find_input(output, "Volume");
-
- if (surface_sock != nullptr) {
- *has_surface_output = (nodeCountSocketLinks(localtree, surface_sock) > 0);
- }
-
- if (volume_sock != nullptr) {
- *has_volume_output = (nodeCountSocketLinks(localtree, volume_sock) > 0);
- }
- }
}
bNodeTreeExec *ntreeShaderBeginExecTree_internal(bNodeExecContext *context,
diff --git a/source/blender/nodes/shader/node_shader_util.cc b/source/blender/nodes/shader/node_shader_util.cc
index 728e2760f9a..c47b69e6b69 100644
--- a/source/blender/nodes/shader/node_shader_util.cc
+++ b/source/blender/nodes/shader/node_shader_util.cc
@@ -284,26 +284,15 @@ void ntreeExecGPUNodes(bNodeTreeExec *exec, GPUMaterial *mat, bNode *output_node
}
}
-void node_shader_gpu_bump_tex_coord(GPUMaterial *mat, bNode *node, GPUNodeLink **link)
+void node_shader_gpu_bump_tex_coord(GPUMaterial *mat, bNode *UNUSED(node), GPUNodeLink **link)
{
- if (node->branch_tag == 1) {
- /* Add one time the value for derivative to the input vector. */
- GPU_link(mat, "dfdx_v3", *link, link);
- }
- else if (node->branch_tag == 2) {
- /* Add one time the value for derivative to the input vector. */
- GPU_link(mat, "dfdy_v3", *link, link);
- }
- else {
- /* nothing to do, reference center value. */
- }
+ GPU_link(mat, "differentiate_texco", *link, link);
}
void node_shader_gpu_default_tex_coord(GPUMaterial *mat, bNode *node, GPUNodeLink **link)
{
if (!*link) {
*link = GPU_attribute(mat, CD_ORCO, "");
- GPU_link(mat, "generated_texco", GPU_builtin(GPU_VIEW_POSITION), *link, link);
node_shader_gpu_bump_tex_coord(mat, node, link);
}
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_ambient_occlusion.cc b/source/blender/nodes/shader/nodes/node_shader_ambient_occlusion.cc
index a24b4379e69..1e8df2e985d 100644
--- a/source/blender/nodes/shader/nodes/node_shader_ambient_occlusion.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_ambient_occlusion.cc
@@ -36,7 +36,7 @@ static int node_shader_gpu_ambient_occlusion(GPUMaterial *mat,
GPU_link(mat, "world_normals_get", &in[2].link);
}
- GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE);
+ GPU_material_flag_set(mat, GPU_MATFLAG_AO);
float inverted = (node->custom2 & SHD_AO_INSIDE) ? 1.0f : 0.0f;
float f_samples = divide_ceil_u(node->custom1, 4);
diff --git a/source/blender/nodes/shader/nodes/node_shader_background.cc b/source/blender/nodes/shader/nodes/node_shader_background.cc
index f5d3b6811f1..ea5c1f541ea 100644
--- a/source/blender/nodes/shader/nodes/node_shader_background.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_background.cc
@@ -9,6 +9,7 @@ static void node_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Color>(N_("Color")).default_value({0.8f, 0.8f, 0.8f, 1.0f});
b.add_input<decl::Float>(N_("Strength")).default_value(1.0f).min(0.0f).max(1000000.0f);
+ b.add_input<decl::Float>(N_("Weight")).unavailable();
b.add_output<decl::Shader>(N_("Background"));
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_bevel.cc b/source/blender/nodes/shader/nodes/node_shader_bevel.cc
index 8bce38baf73..4ae60af9974 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bevel.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_bevel.cc
@@ -32,11 +32,7 @@ static int gpu_shader_bevel(GPUMaterial *mat,
GPUNodeStack *out)
{
if (!in[1].link) {
- GPU_link(mat,
- "direction_transform_m4v3",
- GPU_builtin(GPU_VIEW_NORMAL),
- GPU_builtin(GPU_INVERSE_VIEW_MATRIX),
- &in[1].link);
+ GPU_link(mat, "world_normals_get", &in[1].link);
}
return GPU_stack_link(mat, node, "node_bevel", in, out);
diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_anisotropic.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_anisotropic.cc
index 13b4a034a51..761a833f377 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bsdf_anisotropic.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_anisotropic.cc
@@ -24,6 +24,7 @@ static void node_declare(NodeDeclarationBuilder &b)
.subtype(PROP_FACTOR);
b.add_input<decl::Vector>(N_("Normal")).hide_value();
b.add_input<decl::Vector>(N_("Tangent")).hide_value();
+ b.add_input<decl::Float>(N_("Weight")).unavailable();
b.add_output<decl::Shader>(N_("BSDF"));
}
@@ -51,13 +52,8 @@ static int node_shader_gpu_bsdf_anisotropic(GPUMaterial *mat,
float use_multi_scatter = (node->custom1 == SHD_GLOSSY_MULTI_GGX) ? 1.0f : 0.0f;
- return GPU_stack_link(mat,
- node,
- "node_bsdf_anisotropic",
- in,
- out,
- GPU_constant(&use_multi_scatter),
- GPU_constant(&node->ssr_id));
+ return GPU_stack_link(
+ mat, node, "node_bsdf_anisotropic", in, out, GPU_constant(&use_multi_scatter));
}
} // namespace blender::nodes::node_shader_bsdf_anisotropic_cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_diffuse.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_diffuse.cc
index 3f025b2ba39..5975e04450e 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bsdf_diffuse.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_diffuse.cc
@@ -14,6 +14,7 @@ static void node_declare(NodeDeclarationBuilder &b)
.max(1.0f)
.subtype(PROP_FACTOR);
b.add_input<decl::Vector>(N_("Normal")).hide_value();
+ b.add_input<decl::Float>(N_("Weight")).unavailable();
b.add_output<decl::Shader>(N_("BSDF"));
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_glass.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_glass.cc
index a1e5b705438..95869f13b7e 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bsdf_glass.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_glass.cc
@@ -15,6 +15,7 @@ static void node_declare(NodeDeclarationBuilder &b)
.subtype(PROP_FACTOR);
b.add_input<decl::Float>(N_("IOR")).default_value(1.45f).min(0.0f).max(1000.0f);
b.add_input<decl::Vector>(N_("Normal")).hide_value();
+ b.add_input<decl::Float>(N_("Weight")).unavailable();
b.add_output<decl::Shader>(N_("BSDF"));
}
@@ -37,17 +38,11 @@ static int node_shader_gpu_bsdf_glass(GPUMaterial *mat,
GPU_link(mat, "set_value_zero", &in[1].link);
}
- GPU_material_flag_set(mat, (eGPUMatFlag)(GPU_MATFLAG_GLOSSY | GPU_MATFLAG_REFRACT));
+ GPU_material_flag_set(mat, GPU_MATFLAG_GLOSSY | GPU_MATFLAG_REFRACT);
float use_multi_scatter = (node->custom1 == SHD_GLOSSY_MULTI_GGX) ? 1.0f : 0.0f;
- return GPU_stack_link(mat,
- node,
- "node_bsdf_glass",
- in,
- out,
- GPU_constant(&use_multi_scatter),
- GPU_constant(&node->ssr_id));
+ return GPU_stack_link(mat, node, "node_bsdf_glass", in, out, GPU_constant(&use_multi_scatter));
}
} // namespace blender::nodes::node_shader_bsdf_glass_cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_glossy.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_glossy.cc
index 9f0e172a5fe..07062a9730e 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bsdf_glossy.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_glossy.cc
@@ -14,6 +14,7 @@ static void node_declare(NodeDeclarationBuilder &b)
.max(1.0f)
.subtype(PROP_FACTOR);
b.add_input<decl::Vector>(N_("Normal")).hide_value();
+ b.add_input<decl::Float>(N_("Weight")).unavailable();
b.add_output<decl::Shader>(N_("BSDF"));
}
@@ -40,13 +41,7 @@ static int node_shader_gpu_bsdf_glossy(GPUMaterial *mat,
float use_multi_scatter = (node->custom1 == SHD_GLOSSY_MULTI_GGX) ? 1.0f : 0.0f;
- return GPU_stack_link(mat,
- node,
- "node_bsdf_glossy",
- in,
- out,
- GPU_constant(&use_multi_scatter),
- GPU_constant(&node->ssr_id));
+ return GPU_stack_link(mat, node, "node_bsdf_glossy", in, out, GPU_constant(&use_multi_scatter));
}
} // namespace blender::nodes::node_shader_bsdf_glossy_cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_hair.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_hair.cc
index ef87f380bb9..1a1ba13e886 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bsdf_hair.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_hair.cc
@@ -27,6 +27,7 @@ static void node_declare(NodeDeclarationBuilder &b)
.max(1.0f)
.subtype(PROP_FACTOR);
b.add_input<decl::Vector>(N_("Tangent")).hide_value();
+ b.add_input<decl::Float>(N_("Weight")).unavailable();
b.add_output<decl::Shader>(N_("BSDF"));
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.cc
index edae03bbd31..6495dcfffba 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.cc
@@ -60,6 +60,7 @@ static void node_declare(NodeDeclarationBuilder &b)
.max(1.0f)
.subtype(PROP_FACTOR);
b.add_input<decl::Float>(N_("Random")).hide_value();
+ b.add_input<decl::Float>(N_("Weight")).unavailable();
b.add_output<decl::Shader>(N_("BSDF"));
}
@@ -108,6 +109,15 @@ static void node_shader_update_hair_principled(bNodeTree *ntree, bNode *node)
}
}
+static int node_shader_gpu_hair_principled(GPUMaterial *mat,
+ bNode *node,
+ bNodeExecData *UNUSED(execdata),
+ GPUNodeStack *in,
+ GPUNodeStack *out)
+{
+ return GPU_stack_link(mat, node, "node_bsdf_hair_principled", in, out);
+}
+
} // namespace blender::nodes::node_shader_bsdf_hair_principled_cc
/* node type definition */
@@ -124,6 +134,7 @@ void register_node_type_sh_bsdf_hair_principled()
node_type_size_preset(&ntype, NODE_SIZE_LARGE);
node_type_init(&ntype, file_ns::node_shader_init_hair_principled);
node_type_update(&ntype, file_ns::node_shader_update_hair_principled);
+ node_type_gpu(&ntype, file_ns::node_shader_gpu_hair_principled);
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc
index ba98a797f2a..a63c7aede04 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc
@@ -103,6 +103,7 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_input<decl::Vector>(N_("Normal")).hide_value();
b.add_input<decl::Vector>(N_("Clearcoat Normal")).hide_value();
b.add_input<decl::Vector>(N_("Tangent")).hide_value();
+ b.add_input<decl::Float>(N_("Weight")).unavailable();
b.add_output<decl::Shader>(N_("BSDF"));
}
@@ -128,8 +129,6 @@ static int node_shader_gpu_bsdf_principled(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
- GPUNodeLink *sss_scale;
-
/* Normals */
if (!in[22].link) {
GPU_link(mat, "world_normals_get", &in[22].link);
@@ -145,36 +144,17 @@ static int node_shader_gpu_bsdf_principled(GPUMaterial *mat,
if (!in[24].link) {
GPUNodeLink *orco = GPU_attribute(CD_ORCO, "");
GPU_link(mat, "tangent_orco_z", orco, &in[24].link);
- GPU_link(mat,
- "node_tangent",
- GPU_builtin(GPU_WORLD_NORMAL),
- in[24].link,
- GPU_builtin(GPU_OBJECT_MATRIX),
- &in[24].link);
+ GPU_link(mat, "node_tangent", in[24].link, &in[24].link);
}
#endif
bool use_diffuse = socket_not_one(6) && socket_not_one(17);
- bool use_subsurf = socket_not_zero(1) && use_diffuse && node->sss_id > 0;
+ bool use_subsurf = socket_not_zero(1) && use_diffuse;
bool use_refract = socket_not_one(6) && socket_not_zero(17);
+ bool use_transparency = socket_not_one(21);
bool use_clear = socket_not_zero(14);
- /* SSS Profile */
- if (use_subsurf) {
- bNodeSocket *socket = (bNodeSocket *)BLI_findlink(&node->original->inputs, 2);
- bNodeSocketValueRGBA *socket_data = (bNodeSocketValueRGBA *)socket->default_value;
- /* For some reason it seems that the socket value is in ARGB format. */
- GPU_material_sss_profile_create(mat, &socket_data->value[1]);
- }
-
- if (in[2].link) {
- sss_scale = in[2].link;
- }
- else {
- GPU_link(mat, "set_rgb_one", &sss_scale);
- }
-
- uint flag = GPU_MATFLAG_GLOSSY;
+ eGPUMaterialFlag flag = GPU_MATFLAG_GLOSSY;
if (use_diffuse) {
flag |= GPU_MATFLAG_DIFFUSE;
}
@@ -182,28 +162,37 @@ static int node_shader_gpu_bsdf_principled(GPUMaterial *mat,
flag |= GPU_MATFLAG_REFRACT;
}
if (use_subsurf) {
- flag |= GPU_MATFLAG_SSS;
+ flag |= GPU_MATFLAG_SUBSURFACE;
+ }
+ if (use_transparency) {
+ flag |= GPU_MATFLAG_TRANSPARENT;
+ }
+
+ if (use_subsurf) {
+ bNodeSocket *socket = (bNodeSocket *)BLI_findlink(&node->original->inputs, 2);
+ bNodeSocketValueRGBA *socket_data = (bNodeSocketValueRGBA *)socket->default_value;
+ /* For some reason it seems that the socket value is in ARGB format. */
+ use_subsurf = GPU_material_sss_profile_create(mat, &socket_data->value[1]);
}
- float f_use_diffuse = use_diffuse ? 1.0f : 0.0f;
- float f_use_clearcoat = use_clear ? 1.0f : 0.0f;
- float f_use_refraction = use_refract ? 1.0f : 0.0f;
float use_multi_scatter = (node->custom1 == SHD_GLOSSY_MULTI_GGX) ? 1.0f : 0.0f;
+ float use_sss = (use_subsurf) ? 1.0f : 0.0f;
+ float use_diffuse_f = (use_diffuse) ? 1.0f : 0.0f;
+ float use_clear_f = (use_clear) ? 1.0f : 0.0f;
+ float use_refract_f = (use_refract) ? 1.0f : 0.0f;
- GPU_material_flag_set(mat, (eGPUMatFlag)flag);
+ GPU_material_flag_set(mat, flag);
return GPU_stack_link(mat,
node,
"node_bsdf_principled",
in,
out,
- GPU_constant(&f_use_diffuse),
- GPU_constant(&f_use_clearcoat),
- GPU_constant(&f_use_refraction),
+ GPU_constant(&use_diffuse_f),
+ GPU_constant(&use_clear_f),
+ GPU_constant(&use_refract_f),
GPU_constant(&use_multi_scatter),
- GPU_constant(&node->ssr_id),
- GPU_constant(&node->sss_id),
- sss_scale);
+ GPU_uniform(&use_sss));
}
static void node_shader_update_principled(bNodeTree *ntree, bNode *node)
diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_refraction.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_refraction.cc
index 73f58477b67..e814eb223e5 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bsdf_refraction.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_refraction.cc
@@ -15,6 +15,7 @@ static void node_declare(NodeDeclarationBuilder &b)
.subtype(PROP_FACTOR);
b.add_input<decl::Float>(N_("IOR")).default_value(1.45f).min(0.0f).max(1000.0f);
b.add_input<decl::Vector>(N_("Normal")).hide_value();
+ b.add_input<decl::Float>(N_("Weight")).unavailable();
b.add_output<decl::Shader>(N_("BSDF"));
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_toon.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_toon.cc
index 9cb7c4c3bbb..c1c092e89c7 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bsdf_toon.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_toon.cc
@@ -22,6 +22,7 @@ static void node_declare(NodeDeclarationBuilder &b)
.max(1.0f)
.subtype(PROP_FACTOR);
b.add_input<decl::Vector>(N_("Normal")).hide_value();
+ b.add_input<decl::Float>(N_("Weight")).unavailable();
b.add_output<decl::Shader>(N_("BSDF"));
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_translucent.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_translucent.cc
index 19e61d8140d..fd0dd9f93de 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bsdf_translucent.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_translucent.cc
@@ -9,6 +9,7 @@ static void node_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Color>(N_("Color")).default_value({0.8f, 0.8f, 0.8f, 1.0f});
b.add_input<decl::Vector>(N_("Normal")).hide_value();
+ b.add_input<decl::Float>(N_("Weight")).unavailable();
b.add_output<decl::Shader>(N_("BSDF"));
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_transparent.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_transparent.cc
index 57fd7e3ef3b..291b3fdb2be 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bsdf_transparent.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_transparent.cc
@@ -8,6 +8,7 @@ namespace blender::nodes::node_shader_bsdf_transparent_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Color>(N_("Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Weight")).unavailable();
b.add_output<decl::Shader>(N_("BSDF"));
}
@@ -17,6 +18,9 @@ static int node_shader_gpu_bsdf_transparent(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
+ if (in[0].link || !is_zero_v3(in[0].vec)) {
+ GPU_material_flag_set(mat, GPU_MATFLAG_TRANSPARENT);
+ }
return GPU_stack_link(mat, node, "node_bsdf_transparent", in, out);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_velvet.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_velvet.cc
index 75bdb5b6f54..c86d70aecbf 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bsdf_velvet.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_velvet.cc
@@ -14,6 +14,7 @@ static void node_declare(NodeDeclarationBuilder &b)
.max(1.0f)
.subtype(PROP_FACTOR);
b.add_input<decl::Vector>(N_("Normal")).hide_value();
+ b.add_input<decl::Float>(N_("Weight")).unavailable();
b.add_output<decl::Shader>(N_("BSDF"));
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_bump.cc b/source/blender/nodes/shader/nodes/node_shader_bump.cc
index 5dd27e64ce3..ad2c56d96b5 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bump.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_bump.cc
@@ -27,8 +27,6 @@ static void node_declare(NodeDeclarationBuilder &b)
.min(-1000.0f)
.max(1000.0f)
.hide_value();
- b.add_input<decl::Float>(N_("Height_dx")).default_value(1.0f).unavailable();
- b.add_input<decl::Float>(N_("Height_dy")).default_value(1.0f).unavailable();
b.add_input<decl::Vector>(N_("Normal")).min(-1.0f).max(1.0f).hide_value();
b.add_output<decl::Vector>(N_("Normal"));
}
@@ -46,23 +44,26 @@ static int gpu_shader_bump(GPUMaterial *mat,
{
/* If there is no Height input, the node becomes a no-op. */
if (!in[2].link) {
- if (!in[5].link) {
+ if (!in[3].link) {
return GPU_link(mat, "world_normals_get", &out[0].link);
}
else {
/* Actually running the bump code would normalize, but Cycles handles it as total no-op. */
- return GPU_link(mat, "vector_copy", in[5].link, &out[0].link);
+ return GPU_link(mat, "vector_copy", in[3].link, &out[0].link);
}
}
- if (!in[5].link) {
- GPU_link(mat, "world_normals_get", &in[5].link);
+ if (!in[3].link) {
+ GPU_link(mat, "world_normals_get", &in[3].link);
}
+ const char *height_function = GPU_material_split_sub_function(mat, GPU_FLOAT, &in[2].link);
+
+ GPUNodeLink *dheight = GPU_differentiate_float_function(height_function);
+
float invert = (node->custom1) ? -1.0 : 1.0;
- return GPU_stack_link(
- mat, node, "node_bump", in, out, GPU_builtin(GPU_VIEW_POSITION), GPU_constant(&invert));
+ return GPU_stack_link(mat, node, "node_bump", in, out, dheight, GPU_constant(&invert));
}
} // namespace blender::nodes::node_shader_bump_cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_camera.cc b/source/blender/nodes/shader/nodes/node_shader_camera.cc
index 9d2858f56f7..99c82582456 100644
--- a/source/blender/nodes/shader/nodes/node_shader_camera.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_camera.cc
@@ -22,11 +22,7 @@ static int gpu_shader_camera(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
- GPUNodeLink *viewvec;
-
- viewvec = GPU_builtin(GPU_VIEW_POSITION);
- GPU_link(mat, "invert_z", viewvec, &viewvec);
- return GPU_stack_link(mat, node, "camera", in, out, viewvec);
+ return GPU_stack_link(mat, node, "camera", in, out);
}
} // namespace blender::nodes::node_shader_camera_cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_displacement.cc b/source/blender/nodes/shader/nodes/node_shader_displacement.cc
index 88d1fac13a4..6591396adda 100644
--- a/source/blender/nodes/shader/nodes/node_shader_displacement.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_displacement.cc
@@ -33,16 +33,11 @@ static int gpu_shader_displacement(GPUMaterial *mat,
GPUNodeStack *out)
{
if (!in[3].link) {
- GPU_link(mat,
- "direction_transform_m4v3",
- GPU_builtin(GPU_VIEW_NORMAL),
- GPU_builtin(GPU_INVERSE_VIEW_MATRIX),
- &in[3].link);
+ GPU_link(mat, "world_normals_get", &in[3].link);
}
if (node->custom1 == SHD_SPACE_OBJECT) {
- return GPU_stack_link(
- mat, node, "node_displacement_object", in, out, GPU_builtin(GPU_OBJECT_MATRIX));
+ return GPU_stack_link(mat, node, "node_displacement_object", in, out);
}
return GPU_stack_link(mat, node, "node_displacement_world", in, out);
diff --git a/source/blender/nodes/shader/nodes/node_shader_eevee_specular.cc b/source/blender/nodes/shader/nodes/node_shader_eevee_specular.cc
index c4aed523f61..d68b0c0c37c 100644
--- a/source/blender/nodes/shader/nodes/node_shader_eevee_specular.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_eevee_specular.cc
@@ -33,9 +33,12 @@ static void node_declare(NodeDeclarationBuilder &b)
.subtype(PROP_FACTOR);
b.add_input<decl::Vector>(N_("Clear Coat Normal")).hide_value();
b.add_input<decl::Float>(N_("Ambient Occlusion")).hide_value();
+ b.add_input<decl::Float>(N_("Weight")).unavailable();
b.add_output<decl::Shader>(N_("BSDF"));
}
+#define socket_not_zero(sock) (in[sock].link || (clamp_f(in[sock].vec[0], 0.0f, 1.0f) > 1e-5f))
+
static int node_shader_gpu_eevee_specular(GPUMaterial *mat,
bNode *node,
bNodeExecData *UNUSED(execdata),
@@ -59,9 +62,11 @@ static int node_shader_gpu_eevee_specular(GPUMaterial *mat,
GPU_link(mat, "set_value", GPU_constant(&one), &in[9].link);
}
- GPU_material_flag_set(mat, static_cast<eGPUMatFlag>(GPU_MATFLAG_DIFFUSE | GPU_MATFLAG_GLOSSY));
+ GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE | GPU_MATFLAG_GLOSSY);
+
+ float use_clear = (socket_not_zero(6)) ? 1.0f : 0.0f;
- return GPU_stack_link(mat, node, "node_eevee_specular", in, out, GPU_constant(&node->ssr_id));
+ return GPU_stack_link(mat, node, "node_eevee_specular", in, out, GPU_constant(&use_clear));
}
} // namespace blender::nodes::node_shader_eevee_specular_cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_emission.cc b/source/blender/nodes/shader/nodes/node_shader_emission.cc
index d4d88857cd2..be98f096ce5 100644
--- a/source/blender/nodes/shader/nodes/node_shader_emission.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_emission.cc
@@ -9,6 +9,7 @@ static void node_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Color>(N_("Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
b.add_input<decl::Float>(N_("Strength")).default_value(1.0f).min(0.0f).max(1000000.0f);
+ b.add_input<decl::Float>(N_("Weight")).unavailable();
b.add_output<decl::Shader>(N_("Emission"));
}
@@ -18,7 +19,8 @@ static int node_shader_gpu_emission(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
- return GPU_stack_link(mat, node, "node_emission", in, out, GPU_builtin(GPU_VIEW_NORMAL));
+ GPU_material_flag_set(mat, GPU_MATFLAG_EMISSION);
+ return GPU_stack_link(mat, node, "node_emission", in, out);
}
} // namespace blender::nodes::node_shader_emission_cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_fresnel.cc b/source/blender/nodes/shader/nodes/node_shader_fresnel.cc
index 47b2c6b97d4..7b771d7dafd 100644
--- a/source/blender/nodes/shader/nodes/node_shader_fresnel.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_fresnel.cc
@@ -19,14 +19,10 @@ static int node_shader_gpu_fresnel(GPUMaterial *mat,
GPUNodeStack *out)
{
if (!in[1].link) {
- in[1].link = GPU_builtin(GPU_VIEW_NORMAL);
- }
- else {
- GPU_link(
- mat, "direction_transform_m4v3", in[1].link, GPU_builtin(GPU_VIEW_MATRIX), &in[1].link);
+ GPU_link(mat, "world_normals_get", &in[1].link);
}
- return GPU_stack_link(mat, node, "node_fresnel", in, out, GPU_builtin(GPU_VIEW_POSITION));
+ return GPU_stack_link(mat, node, "node_fresnel", in, out);
}
} // namespace blender::nodes::node_shader_fresnel_cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_geometry.cc b/source/blender/nodes/shader/nodes/node_shader_geometry.cc
index 5bd984ddfc4..47df932f9d4 100644
--- a/source/blender/nodes/shader/nodes/node_shader_geometry.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_geometry.cc
@@ -24,29 +24,17 @@ static int node_shader_gpu_geometry(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
- /* HACK: Don't request GPU_BARYCENTRIC_TEXCO if not used because it will
+ /* HACK: Don't request GPU_MATFLAG_BARYCENTRIC if not used because it will
* trigger the use of geometry shader (and the performance penalty it implies). */
- const float val[4] = {0.0f, 0.0f, 0.0f, 0.0f};
- GPUNodeLink *bary_link = (!out[5].hasoutput) ? GPU_constant(val) :
- GPU_builtin(GPU_BARYCENTRIC_TEXCO);
if (out[5].hasoutput) {
GPU_material_flag_set(mat, GPU_MATFLAG_BARYCENTRIC);
}
/* Opti: don't request orco if not needed. */
+ const float val[4] = {0.0f, 0.0f, 0.0f, 0.0f};
GPUNodeLink *orco_link = (!out[2].hasoutput) ? GPU_constant(val) :
GPU_attribute(mat, CD_ORCO, "");
- const bool success = GPU_stack_link(mat,
- node,
- "node_geometry",
- in,
- out,
- GPU_builtin(GPU_VIEW_POSITION),
- GPU_builtin(GPU_WORLD_NORMAL),
- orco_link,
- GPU_builtin(GPU_OBJECT_MATRIX),
- GPU_builtin(GPU_INVERSE_VIEW_MATRIX),
- bary_link);
+ const bool success = GPU_stack_link(mat, node, "node_geometry", in, out, orco_link);
int i;
LISTBASE_FOREACH_INDEX (bNodeSocket *, sock, &node->outputs, i) {
@@ -55,7 +43,7 @@ static int node_shader_gpu_geometry(GPUMaterial *mat,
* This is the case for interpolated, non linear functions.
* The resulting vector can still be a bit wrong but not as much.
* (see T70644) */
- if (node->branch_tag != 0 && ELEM(i, 1, 2, 4)) {
+ if (ELEM(i, 1, 2, 4)) {
GPU_link(mat,
"vector_math_normalize",
out[i].link,
diff --git a/source/blender/nodes/shader/nodes/node_shader_holdout.cc b/source/blender/nodes/shader/nodes/node_shader_holdout.cc
index b1a9ce75e1f..6a21fab28db 100644
--- a/source/blender/nodes/shader/nodes/node_shader_holdout.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_holdout.cc
@@ -7,6 +7,7 @@ namespace blender::nodes::node_shader_holdout_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
+ b.add_input<decl::Float>(N_("Weight")).unavailable();
b.add_output<decl::Shader>(N_("Holdout"));
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_layer_weight.cc b/source/blender/nodes/shader/nodes/node_shader_layer_weight.cc
index b5097e71657..69825e472fb 100644
--- a/source/blender/nodes/shader/nodes/node_shader_layer_weight.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_layer_weight.cc
@@ -20,14 +20,10 @@ static int node_shader_gpu_layer_weight(GPUMaterial *mat,
GPUNodeStack *out)
{
if (!in[1].link) {
- in[1].link = GPU_builtin(GPU_VIEW_NORMAL);
- }
- else {
- GPU_link(
- mat, "direction_transform_m4v3", in[1].link, GPU_builtin(GPU_VIEW_MATRIX), &in[1].link);
+ GPU_link(mat, "world_normals_get", &in[1].link);
}
- return GPU_stack_link(mat, node, "node_layer_weight", in, out, GPU_builtin(GPU_VIEW_POSITION));
+ return GPU_stack_link(mat, node, "node_layer_weight", in, out);
}
} // namespace blender::nodes::node_shader_layer_weight_cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_normal_map.cc b/source/blender/nodes/shader/nodes/node_shader_normal_map.cc
index f8a5e342ae3..d51d8def945 100644
--- a/source/blender/nodes/shader/nodes/node_shader_normal_map.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_normal_map.cc
@@ -83,18 +83,16 @@ static int gpu_shader_normal_map(GPUMaterial *mat,
GPU_link(mat, color_to_normal_fnc_name, newnormal, &newnormal);
switch (nm->space) {
case SHD_SPACE_TANGENT:
+ GPU_material_flag_set(mat, GPU_MATFLAG_OBJECT_INFO);
GPU_link(mat,
"node_normal_map",
- GPU_builtin(GPU_OBJECT_INFO),
GPU_attribute(mat, CD_TANGENT, nm->uv_map),
- GPU_builtin(GPU_WORLD_NORMAL),
newnormal,
&newnormal);
break;
case SHD_SPACE_OBJECT:
case SHD_SPACE_BLENDER_OBJECT:
- GPU_link(
- mat, "direction_transform_m4v3", newnormal, GPU_builtin(GPU_OBJECT_MATRIX), &newnormal);
+ GPU_link(mat, "normal_transform_object_to_world", newnormal, &newnormal);
break;
case SHD_SPACE_WORLD:
case SHD_SPACE_BLENDER_WORLD:
@@ -102,8 +100,7 @@ static int gpu_shader_normal_map(GPUMaterial *mat,
break;
}
- GPUNodeLink *oldnormal = GPU_builtin(GPU_WORLD_NORMAL);
- GPU_link(mat, "node_normal_map_mix", strength, newnormal, oldnormal, &out[0].link);
+ GPU_link(mat, "node_normal_map_mix", strength, newnormal, &out[0].link);
return true;
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_object_info.cc b/source/blender/nodes/shader/nodes/node_shader_object_info.cc
index 03c1a018d37..8985ab6d0e9 100644
--- a/source/blender/nodes/shader/nodes/node_shader_object_info.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_object_info.cc
@@ -23,15 +23,8 @@ static int node_shader_gpu_object_info(GPUMaterial *mat,
{
Material *ma = GPU_material_get_material(mat);
float index = ma ? ma->index : 0.0f;
- return GPU_stack_link(mat,
- node,
- "node_object_info",
- in,
- out,
- GPU_builtin(GPU_OBJECT_MATRIX),
- GPU_builtin(GPU_OBJECT_COLOR),
- GPU_builtin(GPU_OBJECT_INFO),
- GPU_constant(&index));
+ GPU_material_flag_set(mat, GPU_MATFLAG_OBJECT_INFO);
+ return GPU_stack_link(mat, node, "node_object_info", in, out, GPU_constant(&index));
}
} // namespace blender::nodes::node_shader_object_info_cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_output_aov.cc b/source/blender/nodes/shader/nodes/node_shader_output_aov.cc
index c0a116675ab..78dabc5c1c2 100644
--- a/source/blender/nodes/shader/nodes/node_shader_output_aov.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_output_aov.cc
@@ -35,12 +35,16 @@ static int node_shader_gpu_output_aov(GPUMaterial *mat,
{
GPUNodeLink *outlink;
NodeShaderOutputAOV *aov = (NodeShaderOutputAOV *)node->storage;
- /* Keep in sync with `renderpass_lib.glsl#render_pass_aov_hash` and
- * `EEVEE_renderpasses_aov_hash`. */
- unsigned int hash = BLI_hash_string(aov->name) << 1;
- GPU_stack_link(mat, node, "node_output_aov", in, out, &outlink);
- GPU_material_add_output_link_aov(mat, outlink, hash);
+ uint hash = BLI_hash_string(aov->name);
+ /* WORKAROUND: We don't support int/uint constants for now. So make sure the aliasing works.
+ * We cast back to uint in GLSL. */
+ BLI_STATIC_ASSERT(sizeof(float) == sizeof(uint),
+ "GPUCodegen: AOV hash needs float and uint to be the same size.");
+ GPUNodeLink *hash_link = GPU_constant((float *)&hash);
+ GPU_material_flag_set(mat, GPU_MATFLAG_AOV);
+ GPU_stack_link(mat, node, "node_output_aov", in, out, hash_link, &outlink);
+ GPU_material_add_output_link_aov(mat, outlink, hash);
return true;
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_output_material.cc b/source/blender/nodes/shader/nodes/node_shader_output_material.cc
index 3329ab7f8a0..54475d547da 100644
--- a/source/blender/nodes/shader/nodes/node_shader_output_material.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_output_material.cc
@@ -12,39 +12,33 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_input<decl::Shader>(N_("Surface"));
b.add_input<decl::Shader>(N_("Volume"));
b.add_input<decl::Vector>(N_("Displacement")).hide_value();
+ b.add_input<decl::Float>(N_("Thickness")).hide_value();
}
static int node_shader_gpu_output_material(GPUMaterial *mat,
- bNode *node,
+ bNode *UNUSED(node),
bNodeExecData *UNUSED(execdata),
GPUNodeStack *in,
- GPUNodeStack *out)
+ GPUNodeStack *UNUSED(out))
{
- GPUNodeLink *outlink, *alpha_threshold_link, *shadow_threshold_link;
- Material *ma = GPU_material_get_material(mat);
-
- static float no_alpha_threshold = -1.0f;
- if (ma) {
- alpha_threshold_link = GPU_uniform((ma->blend_method == MA_BM_CLIP) ? &ma->alpha_threshold :
- &no_alpha_threshold);
- shadow_threshold_link = GPU_uniform((ma->blend_shadow == MA_BS_CLIP) ? &ma->alpha_threshold :
- &no_alpha_threshold);
+ GPUNodeLink *outlink_surface, *outlink_volume, *outlink_displacement, *outlink_thickness;
+ /* Passthrough node in order to do the right socket conversions (important for displacement). */
+ if (in[0].link) {
+ GPU_link(mat, "node_output_material_surface", in[0].link, &outlink_surface);
+ GPU_material_output_surface(mat, outlink_surface);
}
- else {
- alpha_threshold_link = GPU_uniform(&no_alpha_threshold);
- shadow_threshold_link = GPU_uniform(&no_alpha_threshold);
+ if (in[1].link) {
+ GPU_link(mat, "node_output_material_volume", in[1].link, &outlink_volume);
+ GPU_material_output_volume(mat, outlink_volume);
+ }
+ if (in[2].link) {
+ GPU_link(mat, "node_output_material_displacement", in[2].link, &outlink_displacement);
+ GPU_material_output_displacement(mat, outlink_displacement);
+ }
+ if (in[3].link) {
+ GPU_link(mat, "node_output_material_thickness", in[3].link, &outlink_thickness);
+ GPU_material_output_thickness(mat, outlink_thickness);
}
-
- GPU_stack_link(mat,
- node,
- "node_output_material",
- in,
- out,
- alpha_threshold_link,
- shadow_threshold_link,
- &outlink);
- GPU_material_output_link(mat, outlink);
-
return true;
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_output_world.cc b/source/blender/nodes/shader/nodes/node_shader_output_world.cc
index 77146876d9d..b0cf4c80bc5 100644
--- a/source/blender/nodes/shader/nodes/node_shader_output_world.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_output_world.cc
@@ -12,16 +12,20 @@ static void node_declare(NodeDeclarationBuilder &b)
}
static int node_shader_gpu_output_world(GPUMaterial *mat,
- bNode *node,
+ bNode *UNUSED(node),
bNodeExecData *UNUSED(execdata),
GPUNodeStack *in,
- GPUNodeStack *out)
+ GPUNodeStack *UNUSED(out))
{
- GPUNodeLink *outlink;
-
- GPU_stack_link(mat, node, "node_output_world", in, out, &outlink);
- GPU_material_output_link(mat, outlink);
-
+ GPUNodeLink *outlink_surface, *outlink_volume;
+ if (in[0].link) {
+ GPU_link(mat, "node_output_world_surface", in[0].link, &outlink_surface);
+ GPU_material_output_surface(mat, outlink_surface);
+ }
+ if (in[1].link) {
+ GPU_link(mat, "node_output_world_volume", in[1].link, &outlink_volume);
+ GPU_material_output_volume(mat, outlink_volume);
+ }
return true;
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_particle_info.cc b/source/blender/nodes/shader/nodes/node_shader_particle_info.cc
index 66e859a8c69..71adbd5e5c4 100644
--- a/source/blender/nodes/shader/nodes/node_shader_particle_info.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_particle_info.cc
@@ -28,16 +28,9 @@ static int gpu_shader_particle_info(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
-
- return GPU_stack_link(mat,
- node,
- "particle_info",
- in,
- out,
- GPU_builtin(GPU_PARTICLE_SCALAR_PROPS),
- GPU_builtin(GPU_PARTICLE_LOCATION),
- GPU_builtin(GPU_PARTICLE_VELOCITY),
- GPU_builtin(GPU_PARTICLE_ANG_VELOCITY));
+ GPU_material_flag_set(mat, GPU_MATFLAG_OBJECT_INFO);
+ /* TODO(fclem) Pass particle data in obinfo. */
+ return GPU_stack_link(mat, node, "particle_info", in, out);
}
} // namespace blender::nodes::node_shader_particle_info_cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_shader_to_rgb.cc b/source/blender/nodes/shader/nodes/node_shader_shader_to_rgb.cc
index 11dfa03ea40..0404158a803 100644
--- a/source/blender/nodes/shader/nodes/node_shader_shader_to_rgb.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_shader_to_rgb.cc
@@ -18,9 +18,7 @@ static int node_shader_gpu_shadertorgb(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
- /* Because node_shader_to_rgba is using fallback_cubemap()
- * we need to tag material as glossy. */
- GPU_material_flag_set(mat, GPU_MATFLAG_GLOSSY);
+ GPU_material_flag_set(mat, GPU_MATFLAG_SHADER_TO_RGBA);
return GPU_stack_link(mat, node, "node_shader_to_rgba", in, out);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.cc b/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.cc
index 17173ef020a..e3ff4c28604 100644
--- a/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.cc
@@ -25,6 +25,7 @@ static void node_declare(NodeDeclarationBuilder &b)
.max(1.0f)
.subtype(PROP_FACTOR);
b.add_input<decl::Vector>(N_("Normal")).hide_value();
+ b.add_input<decl::Float>(N_("Weight")).unavailable();
b.add_output<decl::Shader>(N_("BSSRDF"));
}
@@ -49,19 +50,16 @@ static int node_shader_gpu_subsurface_scattering(GPUMaterial *mat,
GPU_link(mat, "world_normals_get", &in[5].link);
}
- if (node->sss_id > 0) {
- bNodeSocket *socket = (bNodeSocket *)BLI_findlink(&node->original->inputs, 2);
- bNodeSocketValueRGBA *socket_data = (bNodeSocketValueRGBA *)socket->default_value;
- /* For some reason it seems that the socket value is in ARGB format. */
- GPU_material_sss_profile_create(mat, &socket_data->value[1]);
+ bNodeSocket *socket = (bNodeSocket *)BLI_findlink(&node->original->inputs, 2);
+ bNodeSocketValueRGBA *socket_data = (bNodeSocketValueRGBA *)socket->default_value;
+ /* For some reason it seems that the socket value is in ARGB format. */
+ bool use_subsurf = GPU_material_sss_profile_create(mat, &socket_data->value[1]);
- /* sss_id is 0 only the node is not connected to any output.
- * In this case flagging the material would trigger a bug (see T68736). */
- GPU_material_flag_set(mat, (eGPUMatFlag)(GPU_MATFLAG_DIFFUSE | GPU_MATFLAG_SSS));
- }
+ float use_sss = (use_subsurf) ? 1.0f : 0.0f;
+
+ GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE | GPU_MATFLAG_SUBSURFACE);
- return GPU_stack_link(
- mat, node, "node_subsurface_scattering", in, out, GPU_constant(&node->sss_id));
+ return GPU_stack_link(mat, node, "node_subsurface_scattering", in, out, GPU_uniform(&use_sss));
}
static void node_shader_update_subsurface_scattering(bNodeTree *ntree, bNode *node)
diff --git a/source/blender/nodes/shader/nodes/node_shader_tangent.cc b/source/blender/nodes/shader/nodes/node_shader_tangent.cc
index 25489632594..8e27ebed21b 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tangent.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tangent.cc
@@ -73,14 +73,7 @@ static int node_shader_gpu_tangent(GPUMaterial *mat,
GPU_link(mat, "tangent_orco_z", orco, &orco);
}
- return GPU_stack_link(mat,
- node,
- "node_tangent",
- in,
- out,
- GPU_builtin(GPU_WORLD_NORMAL),
- orco,
- GPU_builtin(GPU_OBJECT_MATRIX));
+ return GPU_stack_link(mat, node, "node_tangent", in, out, orco);
}
} // namespace blender::nodes::node_shader_tangent_cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc b/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc
index ac986787d65..ae683386bac 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc
@@ -35,24 +35,18 @@ static int node_shader_gpu_tex_coord(GPUMaterial *mat,
{
Object *ob = (Object *)node->id;
- GPUNodeLink *inv_obmat = (ob != nullptr) ? GPU_uniform(&ob->imat[0][0]) :
- GPU_builtin(GPU_INVERSE_OBJECT_MATRIX);
+ /* Use special matrix to let the shader branch to using the render object's matrix. */
+ float dummy_matrix[4][4];
+ dummy_matrix[3][3] = 0.0f;
+ GPUNodeLink *inv_obmat = (ob != NULL) ? GPU_uniform(&ob->imat[0][0]) :
+ GPU_uniform(&dummy_matrix[0][0]);
/* Opti: don't request orco if not needed. */
- const float default_coords[4] = {0.0f, 0.0f, 0.0f, 0.0f};
- GPUNodeLink *orco = (!out[0].hasoutput) ? GPU_constant(default_coords) :
- GPU_attribute(mat, CD_ORCO, "");
+ float4 zero(0.0f);
+ GPUNodeLink *orco = (!out[0].hasoutput) ? GPU_constant(zero) : GPU_attribute(mat, CD_ORCO, "");
GPUNodeLink *mtface = GPU_attribute(mat, CD_MTFACE, "");
- GPUNodeLink *viewpos = GPU_builtin(GPU_VIEW_POSITION);
- GPUNodeLink *worldnor = GPU_builtin(GPU_WORLD_NORMAL);
- GPUNodeLink *texcofacs = GPU_builtin(GPU_CAMERA_TEXCO_FACTORS);
- if (out[0].hasoutput) {
- GPU_link(mat, "generated_from_orco", orco, &orco);
- }
-
- GPU_stack_link(
- mat, node, "node_tex_coord", in, out, viewpos, worldnor, inv_obmat, texcofacs, orco, mtface);
+ GPU_stack_link(mat, node, "node_tex_coord", in, out, inv_obmat, orco, mtface);
int i;
LISTBASE_FOREACH_INDEX (bNodeSocket *, sock, &node->outputs, i) {
@@ -61,7 +55,7 @@ static int node_shader_gpu_tex_coord(GPUMaterial *mat,
* This is the case for interpolated, non linear functions.
* The resulting vector can still be a bit wrong but not as much.
* (see T70644) */
- if (node->branch_tag != 0 && ELEM(i, 1, 6)) {
+ if (ELEM(i, 1, 6)) {
GPU_link(mat,
"vector_math_normalize",
out[i].link,
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc b/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc
index 41456091e1d..dbff092234b 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc
@@ -44,15 +44,13 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat,
GPUNodeLink *outalpha;
- if (!ima) {
+ /* HACK(fclem): For lookdev mode: do not compile an empty environment and just create an empty
+ * texture entry point. We manually bind to it after DRW_shgroup_add_material_resources(). */
+ if (!ima && !GPU_material_flag_get(mat, GPU_MATFLAG_LOOKDEV_HACK)) {
return GPU_stack_link(mat, node, "node_tex_environment_empty", in, out);
}
- if (!in[0].link) {
- GPU_link(mat, "node_tex_environment_texco", GPU_builtin(GPU_VIEW_POSITION), &in[0].link);
- node_shader_gpu_bump_tex_coord(mat, node, &in[0].link);
- }
-
+ node_shader_gpu_default_tex_coord(mat, node, &in[0].link);
node_shader_gpu_tex_mapping(mat, node, in, out);
/* Compute texture coordinate. */
@@ -92,7 +90,7 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat,
/* Sample texture with correct interpolation. */
GPU_link(mat, gpu_fn, in[0].link, GPU_image(mat, ima, iuser, sampler), &out[0].link, &outalpha);
- if (out[0].hasoutput) {
+ if (out[0].hasoutput && ima) {
if (ELEM(ima->alpha_mode, IMA_ALPHA_IGNORE, IMA_ALPHA_CHANNEL_PACKED) ||
IMB_colormanagement_space_name_is_data(ima->colorspace_settings.name)) {
/* Don't let alpha affect color output in these cases. */
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_image.cc b/source/blender/nodes/shader/nodes/node_shader_tex_image.cc
index 19b5a50cd6b..d97f7d0375d 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_image.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_image.cc
@@ -88,13 +88,11 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat,
}
case SHD_PROJ_BOX: {
gpu_node_name = use_cubic ? "tex_box_sample_cubic" : "tex_box_sample_linear";
- GPUNodeLink *wnor, *col1, *col2, *col3;
- GPUNodeLink *vnor = GPU_builtin(GPU_WORLD_NORMAL);
- GPUNodeLink *ob_mat = GPU_builtin(GPU_OBJECT_MATRIX);
+ GPUNodeLink *vnor, *wnor, *col1, *col2, *col3;
GPUNodeLink *blend = GPU_uniform(&tex->projection_blend);
GPUNodeLink *gpu_image = GPU_image(mat, ima, iuser, sampler_state);
- /* equivalent to normal_world_to_object */
- GPU_link(mat, "normal_transform_transposed_m4v3", vnor, ob_mat, &wnor);
+ GPU_link(mat, "world_normals_get", &vnor);
+ GPU_link(mat, "normal_transform_world_to_object", vnor, &wnor);
GPU_link(mat, gpu_node_name, in[0].link, wnor, gpu_image, &col1, &col2, &col3);
GPU_link(mat, "tex_box_blend", wnor, col1, col2, col3, blend, &out[0].link, &out[1].link);
break;
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_pointdensity.cc b/source/blender/nodes/shader/nodes/node_shader_tex_pointdensity.cc
index f628d1ec77b..fcd1d4973ff 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_pointdensity.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_pointdensity.cc
@@ -65,7 +65,7 @@ static void node_shader_buts_tex_pointdensity(uiLayout *layout,
static void node_shader_init_tex_pointdensity(bNodeTree *UNUSED(ntree), bNode *node)
{
- NodeShaderTexPointDensity *point_density = MEM_cnew<NodeShaderTexPointDensity>("new pd node");
+ NodeShaderTexPointDensity *point_density = MEM_new<NodeShaderTexPointDensity>("new pd node");
point_density->resolution = 100;
point_density->radius = 0.3f;
point_density->space = SHD_POINTDENSITY_SPACE_OBJECT;
@@ -79,7 +79,7 @@ static void node_shader_free_tex_pointdensity(bNode *node)
PointDensity *pd = &point_density->pd;
RE_point_density_free(pd);
BKE_texture_pointdensity_free_data(pd);
- memset(pd, 0, sizeof(*pd));
+ *pd = dna::shallow_zero_initialize();
MEM_freeN(point_density);
}
@@ -90,7 +90,7 @@ static void node_shader_copy_tex_pointdensity(bNodeTree *UNUSED(dest_ntree),
dest_node->storage = MEM_dupallocN(src_node->storage);
NodeShaderTexPointDensity *point_density = (NodeShaderTexPointDensity *)dest_node->storage;
PointDensity *pd = &point_density->pd;
- memset(pd, 0, sizeof(*pd));
+ *pd = dna::shallow_zero_initialize();
}
} // namespace blender::nodes::node_shader_tex_pointdensity_cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_displacement.cc b/source/blender/nodes/shader/nodes/node_shader_vector_displacement.cc
index 04cf0b72f32..9996e91177a 100644
--- a/source/blender/nodes/shader/nodes/node_shader_vector_displacement.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_vector_displacement.cc
@@ -24,23 +24,20 @@ static int gpu_shader_vector_displacement(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
- if (node->custom1 == SHD_SPACE_TANGENT) {
- return GPU_stack_link(mat,
- node,
- "node_vector_displacement_tangent",
- in,
- out,
- GPU_attribute(mat, CD_TANGENT, ""),
- GPU_builtin(GPU_WORLD_NORMAL),
- GPU_builtin(GPU_OBJECT_MATRIX),
- GPU_builtin(GPU_VIEW_MATRIX));
+ switch (node->custom1) {
+ case SHD_SPACE_TANGENT:
+ return GPU_stack_link(mat,
+ node,
+ "node_vector_displacement_tangent",
+ in,
+ out,
+ GPU_attribute(mat, CD_TANGENT, ""));
+ case SHD_SPACE_OBJECT:
+ return GPU_stack_link(mat, node, "node_vector_displacement_object", in, out);
+ case SHD_SPACE_WORLD:
+ default:
+ return GPU_stack_link(mat, node, "node_vector_displacement_world", in, out);
}
- if (node->custom1 == SHD_SPACE_OBJECT) {
- return GPU_stack_link(
- mat, node, "node_vector_displacement_object", in, out, GPU_builtin(GPU_OBJECT_MATRIX));
- }
-
- return GPU_stack_link(mat, node, "node_vector_displacement_world", in, out);
}
} // namespace blender::nodes::node_shader_vector_displacement_cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc
index a041492fb13..b35f686e331 100644
--- a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc
@@ -69,9 +69,9 @@ static int gpu_shader_vector_rotate(GPUMaterial *mat,
return 0;
}
-static float3 sh_node_vector_rotate_around_axis(const float3 vector,
- const float3 center,
- const float3 axis,
+static float3 sh_node_vector_rotate_around_axis(const float3 &vector,
+ const float3 &center,
+ const float3 &axis,
const float angle)
{
float3 result = vector - center;
@@ -81,9 +81,9 @@ static float3 sh_node_vector_rotate_around_axis(const float3 vector,
return result + center;
}
-static float3 sh_node_vector_rotate_euler(const float3 vector,
- const float3 center,
- const float3 rotation,
+static float3 sh_node_vector_rotate_euler(const float3 &vector,
+ const float3 &center,
+ const float3 &rotation,
const bool invert)
{
float mat[3][3];
@@ -105,13 +105,15 @@ static const fn::MultiFunction *get_multi_function(bNode &node)
case NODE_VECTOR_ROTATE_TYPE_AXIS: {
if (invert) {
static fn::CustomMF_SI_SI_SI_SI_SO<float3, float3, float3, float, float3> fn{
- "Rotate Axis", [](float3 in, float3 center, float3 axis, float angle) {
+ "Rotate Axis",
+ [](const float3 &in, const float3 &center, const float3 &axis, float angle) {
return sh_node_vector_rotate_around_axis(in, center, axis, -angle);
}};
return &fn;
}
static fn::CustomMF_SI_SI_SI_SI_SO<float3, float3, float3, float, float3> fn{
- "Rotate Axis", [](float3 in, float3 center, float3 axis, float angle) {
+ "Rotate Axis",
+ [](const float3 &in, const float3 &center, const float3 &axis, float angle) {
return sh_node_vector_rotate_around_axis(in, center, axis, angle);
}};
return &fn;
@@ -120,13 +122,13 @@ static const fn::MultiFunction *get_multi_function(bNode &node)
float3 axis = float3(1.0f, 0.0f, 0.0f);
if (invert) {
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{
- "Rotate X-Axis", [=](float3 in, float3 center, float angle) {
+ "Rotate X-Axis", [=](const float3 &in, const float3 &center, float angle) {
return sh_node_vector_rotate_around_axis(in, center, axis, -angle);
}};
return &fn;
}
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{
- "Rotate X-Axis", [=](float3 in, float3 center, float angle) {
+ "Rotate X-Axis", [=](const float3 &in, const float3 &center, float angle) {
return sh_node_vector_rotate_around_axis(in, center, axis, angle);
}};
return &fn;
@@ -135,13 +137,13 @@ static const fn::MultiFunction *get_multi_function(bNode &node)
float3 axis = float3(0.0f, 1.0f, 0.0f);
if (invert) {
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{
- "Rotate Y-Axis", [=](float3 in, float3 center, float angle) {
+ "Rotate Y-Axis", [=](const float3 &in, const float3 &center, float angle) {
return sh_node_vector_rotate_around_axis(in, center, axis, -angle);
}};
return &fn;
}
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{
- "Rotate Y-Axis", [=](float3 in, float3 center, float angle) {
+ "Rotate Y-Axis", [=](const float3 &in, const float3 &center, float angle) {
return sh_node_vector_rotate_around_axis(in, center, axis, angle);
}};
return &fn;
@@ -150,13 +152,13 @@ static const fn::MultiFunction *get_multi_function(bNode &node)
float3 axis = float3(0.0f, 0.0f, 1.0f);
if (invert) {
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{
- "Rotate Z-Axis", [=](float3 in, float3 center, float angle) {
+ "Rotate Z-Axis", [=](const float3 &in, const float3 &center, float angle) {
return sh_node_vector_rotate_around_axis(in, center, axis, -angle);
}};
return &fn;
}
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{
- "Rotate Z-Axis", [=](float3 in, float3 center, float angle) {
+ "Rotate Z-Axis", [=](const float3 &in, const float3 &center, float angle) {
return sh_node_vector_rotate_around_axis(in, center, axis, angle);
}};
return &fn;
@@ -164,13 +166,13 @@ static const fn::MultiFunction *get_multi_function(bNode &node)
case NODE_VECTOR_ROTATE_TYPE_EULER_XYZ: {
if (invert) {
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{
- "Rotate Euler", [](float3 in, float3 center, float3 rotation) {
+ "Rotate Euler", [](const float3 &in, const float3 &center, const float3 &rotation) {
return sh_node_vector_rotate_euler(in, center, rotation, true);
}};
return &fn;
}
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{
- "Rotate Euler", [](float3 in, float3 center, float3 rotation) {
+ "Rotate Euler", [](const float3 &in, const float3 &center, const float3 &rotation) {
return sh_node_vector_rotate_euler(in, center, rotation, false);
}};
return &fn;
diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_transform.cc b/source/blender/nodes/shader/nodes/node_shader_vector_transform.cc
index d610e1309a7..de588f9005f 100644
--- a/source/blender/nodes/shader/nodes/node_shader_vector_transform.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_vector_transform.cc
@@ -43,7 +43,7 @@ static void node_shader_init_vect_transform(bNodeTree *UNUSED(ntree), bNode *nod
node->storage = vect;
}
-static GPUNodeLink *get_gpulink_matrix_from_to(short from, short to)
+static const char *get_gpufn_name_from_to(short from, short to, bool is_direction)
{
switch (from) {
case SHD_VECT_TRANSFORM_SPACE_OBJECT:
@@ -51,9 +51,11 @@ static GPUNodeLink *get_gpulink_matrix_from_to(short from, short to)
case SHD_VECT_TRANSFORM_SPACE_OBJECT:
return nullptr;
case SHD_VECT_TRANSFORM_SPACE_WORLD:
- return GPU_builtin(GPU_OBJECT_MATRIX);
+ return is_direction ? "direction_transform_object_to_world" :
+ "point_transform_object_to_world";
case SHD_VECT_TRANSFORM_SPACE_CAMERA:
- return GPU_builtin(GPU_LOC_TO_VIEW_MATRIX);
+ return is_direction ? "direction_transform_object_to_view" :
+ "point_transform_object_to_view";
}
break;
case SHD_VECT_TRANSFORM_SPACE_WORLD:
@@ -61,9 +63,11 @@ static GPUNodeLink *get_gpulink_matrix_from_to(short from, short to)
case SHD_VECT_TRANSFORM_SPACE_WORLD:
return nullptr;
case SHD_VECT_TRANSFORM_SPACE_CAMERA:
- return GPU_builtin(GPU_VIEW_MATRIX);
+ return is_direction ? "direction_transform_world_to_view" :
+ "point_transform_world_to_view";
case SHD_VECT_TRANSFORM_SPACE_OBJECT:
- return GPU_builtin(GPU_INVERSE_OBJECT_MATRIX);
+ return is_direction ? "direction_transform_world_to_object" :
+ "point_transform_world_to_object";
}
break;
case SHD_VECT_TRANSFORM_SPACE_CAMERA:
@@ -71,14 +75,17 @@ static GPUNodeLink *get_gpulink_matrix_from_to(short from, short to)
case SHD_VECT_TRANSFORM_SPACE_CAMERA:
return nullptr;
case SHD_VECT_TRANSFORM_SPACE_WORLD:
- return GPU_builtin(GPU_INVERSE_VIEW_MATRIX);
+ return is_direction ? "direction_transform_view_to_world" :
+ "point_transform_view_to_world";
case SHD_VECT_TRANSFORM_SPACE_OBJECT:
- return GPU_builtin(GPU_INVERSE_LOC_TO_VIEW_MATRIX);
+ return is_direction ? "direction_transform_view_to_object" :
+ "point_transform_view_to_object";
}
break;
}
- return nullptr;
+ return NULL;
}
+
static int gpu_shader_vect_transform(GPUMaterial *mat,
bNode *node,
bNodeExecData *UNUSED(execdata),
@@ -86,11 +93,6 @@ static int gpu_shader_vect_transform(GPUMaterial *mat,
GPUNodeStack *out)
{
struct GPUNodeLink *inputlink;
- struct GPUNodeLink *fromto;
-
- const char *vtransform = "direction_transform_m4v3";
- const char *ptransform = "point_transform_m4v3";
- const char *func_name = nullptr;
NodeShaderVectTransform *nodeprop = (NodeShaderVectTransform *)node->storage;
@@ -101,17 +103,20 @@ static int gpu_shader_vect_transform(GPUMaterial *mat,
inputlink = GPU_constant(in[0].vec);
}
- fromto = get_gpulink_matrix_from_to(nodeprop->convert_from, nodeprop->convert_to);
+ const bool is_direction = (nodeprop->type != SHD_VECT_TRANSFORM_TYPE_POINT);
+ const char *func_name = get_gpufn_name_from_to(
+ nodeprop->convert_from, nodeprop->convert_to, is_direction);
- func_name = (nodeprop->type == SHD_VECT_TRANSFORM_TYPE_POINT) ? ptransform : vtransform;
- if (fromto) {
+ if (func_name) {
/* For cycles we have inverted Z */
/* TODO: pass here the correct matrices */
if (nodeprop->convert_from == SHD_VECT_TRANSFORM_SPACE_CAMERA &&
nodeprop->convert_to != SHD_VECT_TRANSFORM_SPACE_CAMERA) {
GPU_link(mat, "invert_z", inputlink, &inputlink);
}
- GPU_link(mat, func_name, inputlink, fromto, &out[0].link);
+
+ GPU_link(mat, func_name, inputlink, &out[0].link);
+
if (nodeprop->convert_to == SHD_VECT_TRANSFORM_SPACE_CAMERA &&
nodeprop->convert_from != SHD_VECT_TRANSFORM_SPACE_CAMERA) {
GPU_link(mat, "invert_z", out[0].link, &out[0].link);
diff --git a/source/blender/nodes/shader/nodes/node_shader_volume_absorption.cc b/source/blender/nodes/shader/nodes/node_shader_volume_absorption.cc
index e0fef6b0d19..930fa6e5fb9 100644
--- a/source/blender/nodes/shader/nodes/node_shader_volume_absorption.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_volume_absorption.cc
@@ -9,6 +9,7 @@ static void node_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Color>(N_("Color")).default_value({0.8f, 0.8f, 0.8f, 1.0f});
b.add_input<decl::Float>(N_("Density")).default_value(1.0f).min(0.0f).max(1000.0f);
+ b.add_input<decl::Float>(N_("Weight")).unavailable();
b.add_output<decl::Shader>(N_("Volume"));
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_volume_principled.cc b/source/blender/nodes/shader/nodes/node_shader_volume_principled.cc
index 980782f571c..d414b4b2ef7 100644
--- a/source/blender/nodes/shader/nodes/node_shader_volume_principled.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_volume_principled.cc
@@ -27,6 +27,7 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_input<decl::Color>(N_("Blackbody Tint")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
b.add_input<decl::Float>(N_("Temperature")).default_value(1000.0f).min(0.0f).max(6500.0f);
b.add_input<decl::String>(N_("Temperature Attribute"));
+ b.add_input<decl::Float>(N_("Weight")).unavailable();
b.add_output<decl::Shader>(N_("Volume"));
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_volume_scatter.cc b/source/blender/nodes/shader/nodes/node_shader_volume_scatter.cc
index 6408efb769d..0c6859ad1fb 100644
--- a/source/blender/nodes/shader/nodes/node_shader_volume_scatter.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_volume_scatter.cc
@@ -14,6 +14,7 @@ static void node_declare(NodeDeclarationBuilder &b)
.min(-1.0f)
.max(1.0f)
.subtype(PROP_FACTOR);
+ b.add_input<decl::Float>(N_("Weight")).unavailable();
b.add_output<decl::Shader>(N_("Volume"));
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_wireframe.cc b/source/blender/nodes/shader/nodes/node_shader_wireframe.cc
index 1204c3a678f..6a1acda3353 100644
--- a/source/blender/nodes/shader/nodes/node_shader_wireframe.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_wireframe.cc
@@ -28,17 +28,11 @@ static int node_shader_gpu_wireframe(GPUMaterial *mat,
GPU_material_flag_set(mat, GPU_MATFLAG_BARYCENTRIC);
/* node->custom1 is use_pixel_size */
if (node->custom1) {
- return GPU_stack_link(
- mat, node, "node_wireframe_screenspace", in, out, GPU_builtin(GPU_BARYCENTRIC_TEXCO));
+ return GPU_stack_link(mat, node, "node_wireframe_screenspace", in, out);
+ }
+ else {
+ return GPU_stack_link(mat, node, "node_wireframe", in, out);
}
-
- return GPU_stack_link(mat,
- node,
- "node_wireframe",
- in,
- out,
- GPU_builtin(GPU_BARYCENTRIC_TEXCO),
- GPU_builtin(GPU_BARYCENTRIC_DIST));
}
} // namespace blender::nodes::node_shader_wireframe_cc
diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c
index 46f89dd4103..baa2b0deb71 100644
--- a/source/blender/python/bmesh/bmesh_py_types.c
+++ b/source/blender/python/bmesh/bmesh_py_types.c
@@ -1061,8 +1061,9 @@ PyDoc_STRVAR(bpy_bmesh_from_object_doc,
" :arg cage: Get the mesh as a deformed cage.\n"
" :type cage: boolean\n"
" :arg face_normals: Calculate face normals.\n"
+ " :type face_normals: boolean\n"
" :arg vertex_normals: Calculate vertex normals.\n"
- " :type face_normals: boolean\n");
+ " :type vertex_normals: boolean\n");
static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject *kw)
{
static const char *kwlist[] = {
@@ -1083,7 +1084,7 @@ static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject
if (!PyArg_ParseTupleAndKeywords(args,
kw,
- "OO|$O&O&:from_object",
+ "OO|$O&O&O&:from_object",
(char **)kwlist,
&py_object,
&py_depsgraph,
@@ -1190,7 +1191,7 @@ static PyObject *bpy_bmesh_from_mesh(BPy_BMesh *self, PyObject *args, PyObject *
if (!PyArg_ParseTupleAndKeywords(args,
kw,
- "O|$O&O&i:from_mesh",
+ "O|$O&O&O&i:from_mesh",
(char **)kwlist,
&py_mesh,
PyC_ParseBool,
@@ -3634,8 +3635,8 @@ void BPy_BM_init_types(void)
BPy_BMLoopSeq_Type.tp_methods = bpy_bmloopseq_methods;
BPy_BMIter_Type.tp_methods = NULL;
- /*BPy_BMElem_Check() uses bpy_bm_elem_hash() to check types.
- * if this changes update the macro */
+ /* #BPy_BMElem_Check() uses #bpy_bm_elem_hash() to check types.
+ * if this changes update the macro. */
BPy_BMesh_Type.tp_hash = bpy_bm_hash;
BPy_BMVert_Type.tp_hash = bpy_bm_elem_hash;
BPy_BMEdge_Type.tp_hash = bpy_bm_elem_hash;
diff --git a/source/blender/python/bmesh/bmesh_py_types_customdata.c b/source/blender/python/bmesh/bmesh_py_types_customdata.c
index 1686f99df99..e591dfa2929 100644
--- a/source/blender/python/bmesh/bmesh_py_types_customdata.c
+++ b/source/blender/python/bmesh/bmesh_py_types_customdata.c
@@ -90,7 +90,7 @@ PyDoc_STRVAR(
PyDoc_STRVAR(bpy_bmlayeraccess_collection__bevel_weight_doc,
"Bevel weight float in [0 - 1].\n\n:type: :class:`BMLayerCollection`");
PyDoc_STRVAR(bpy_bmlayeraccess_collection__crease_doc,
- "Edge crease for subdivision surface - float in [0 - 1].\n\n:type: "
+ "Crease for subdivision surface - float in [0 - 1].\n\n:type: "
":class:`BMLayerCollection`");
PyDoc_STRVAR(
bpy_bmlayeraccess_collection__uv_doc,
@@ -210,6 +210,11 @@ static PyGetSetDef bpy_bmlayeraccess_vert_getseters[] = {
(setter)NULL,
bpy_bmlayeraccess_collection__bevel_weight_doc,
(void *)CD_BWEIGHT},
+ {"crease",
+ (getter)bpy_bmlayeraccess_collection_get,
+ (setter)NULL,
+ bpy_bmlayeraccess_collection__crease_doc,
+ (void *)CD_CREASE},
{"skin",
(getter)bpy_bmlayeraccess_collection_get,
(setter)NULL,
diff --git a/source/blender/python/generic/py_capi_utils.h b/source/blender/python/generic/py_capi_utils.h
index 0f94b264159..ecb6db2b82c 100644
--- a/source/blender/python/generic/py_capi_utils.h
+++ b/source/blender/python/generic/py_capi_utils.h
@@ -8,6 +8,10 @@
#ifndef __PY_CAPI_UTILS_H__
#define __PY_CAPI_UTILS_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#include "BLI_sys_types.h"
#include "BLI_utildefines_variadic.h"
@@ -273,3 +277,7 @@ bool PyC_StructFmt_type_is_byte(char format);
bool PyC_StructFmt_type_is_bool(char format);
#endif /* __PY_CAPI_UTILS_H__ */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/python/gpu/CMakeLists.txt b/source/blender/python/gpu/CMakeLists.txt
index 644bbd18173..e726cb7883d 100644
--- a/source/blender/python/gpu/CMakeLists.txt
+++ b/source/blender/python/gpu/CMakeLists.txt
@@ -29,6 +29,7 @@ set(SRC
gpu_py_offscreen.c
gpu_py_platform.c
gpu_py_select.c
+ gpu_py_shader_create_info.cc
gpu_py_shader.c
gpu_py_state.c
gpu_py_texture.c
diff --git a/source/blender/python/gpu/gpu_py_buffer.c b/source/blender/python/gpu/gpu_py_buffer.c
index e0c20b64c63..020535d002a 100644
--- a/source/blender/python/gpu/gpu_py_buffer.c
+++ b/source/blender/python/gpu/gpu_py_buffer.c
@@ -277,7 +277,10 @@ static int pygpu_buffer__tp_traverse(BPyGPUBuffer *self, visitproc visit, void *
static int pygpu_buffer__tp_clear(BPyGPUBuffer *self)
{
- Py_CLEAR(self->parent);
+ if (self->parent) {
+ Py_CLEAR(self->parent);
+ self->buf.as_void = NULL;
+ }
return 0;
}
@@ -287,7 +290,7 @@ static void pygpu_buffer__tp_dealloc(BPyGPUBuffer *self)
PyObject_GC_UnTrack(self);
Py_CLEAR(self->parent);
}
- else {
+ else if (self->buf.as_void) {
MEM_freeN(self->buf.as_void);
}
@@ -394,9 +397,16 @@ static PyObject *pygpu_buffer__tp_new(PyTypeObject *UNUSED(type), PyObject *args
return NULL;
}
- if (pygpu_buffer_dimensions_tot_len_compare(shape, shape_len, pybuffer.shape, pybuffer.ndim)) {
+ Py_ssize_t *pybuffer_shape = pybuffer.shape;
+ Py_ssize_t pybuffer_ndim = pybuffer.ndim;
+ if (!pybuffer_shape) {
+ pybuffer_shape = &pybuffer.len;
+ pybuffer_ndim = 1;
+ }
+
+ if (pygpu_buffer_dimensions_tot_len_compare(shape, shape_len, pybuffer_shape, pybuffer_ndim)) {
buffer = pygpu_buffer_make_from_data(
- init, pygpu_dataformat.value_found, pybuffer.ndim, shape, pybuffer.buf);
+ init, pygpu_dataformat.value_found, shape_len, shape, pybuffer.buf);
}
PyBuffer_Release(&pybuffer);
@@ -594,23 +604,31 @@ static void pygpu_buffer_strides_calc(const eGPUDataFormat format,
}
/* Here is the buffer interface function */
-static int pygpu_buffer__bf_getbuffer(BPyGPUBuffer *self, Py_buffer *view, int UNUSED(flags))
+static int pygpu_buffer__bf_getbuffer(BPyGPUBuffer *self, Py_buffer *view, int flags)
{
if (view == NULL) {
PyErr_SetString(PyExc_ValueError, "NULL view in getbuffer");
return -1;
}
+ memset(view, 0, sizeof(*view));
+
view->obj = (PyObject *)self;
view->buf = (void *)self->buf.as_void;
view->len = bpygpu_Buffer_size(self);
view->readonly = 0;
view->itemsize = GPU_texture_dataformat_size(self->format);
- view->format = (char *)pygpu_buffer_formatstr(self->format);
- view->ndim = self->shape_len;
- view->shape = self->shape;
- view->strides = MEM_mallocN(view->ndim * sizeof(*view->strides), "BPyGPUBuffer strides");
- pygpu_buffer_strides_calc(self->format, view->ndim, view->shape, view->strides);
+ if (flags & PyBUF_FORMAT) {
+ view->format = (char *)pygpu_buffer_formatstr(self->format);
+ }
+ if (flags & PyBUF_ND) {
+ view->ndim = self->shape_len;
+ view->shape = self->shape;
+ }
+ if (flags & PyBUF_STRIDES) {
+ view->strides = MEM_mallocN(view->ndim * sizeof(*view->strides), "BPyGPUBuffer strides");
+ pygpu_buffer_strides_calc(self->format, view->ndim, view->shape, view->strides);
+ }
view->suboffsets = NULL;
view->internal = NULL;
diff --git a/source/blender/python/gpu/gpu_py_element.c b/source/blender/python/gpu/gpu_py_element.c
index f2836576659..04975fcef96 100644
--- a/source/blender/python/gpu/gpu_py_element.c
+++ b/source/blender/python/gpu/gpu_py_element.c
@@ -70,12 +70,14 @@ static PyObject *pygpu_IndexBuf__tp_new(PyTypeObject *UNUSED(type), PyObject *ar
if (pybuffer.ndim != 1 && pybuffer.shape[1] != verts_per_prim) {
PyErr_Format(PyExc_ValueError, "Each primitive must exactly %d indices", verts_per_prim);
+ PyBuffer_Release(&pybuffer);
return NULL;
}
if (pybuffer.itemsize != 4 ||
PyC_StructFmt_type_is_float_any(PyC_StructFmt_type_from_str(pybuffer.format))) {
PyErr_Format(PyExc_ValueError, "Each index must be an 4-bytes integer value");
+ PyBuffer_Release(&pybuffer);
return NULL;
}
diff --git a/source/blender/python/gpu/gpu_py_shader.c b/source/blender/python/gpu/gpu_py_shader.c
index c74b3e173d1..9fe4bdcbaa0 100644
--- a/source/blender/python/gpu/gpu_py_shader.c
+++ b/source/blender/python/gpu/gpu_py_shader.c
@@ -537,16 +537,15 @@ static PyObject *pygpu_shader_uniform_block(BPyGPUShader *self, PyObject *args)
return NULL;
}
- int slot = GPU_shader_get_uniform_block(self->shader, name);
- if (slot == -1) {
+ int binding = GPU_shader_get_uniform_block_binding(self->shader, name);
+ if (binding == -1) {
PyErr_SetString(
PyExc_BufferError,
- "GPUShader.uniform_buffer: uniform block not found, make sure the name is correct");
+ "GPUShader.uniform_block: uniform block not found, make sure the name is correct");
return NULL;
}
- GPU_uniformbuf_bind(py_ubo->ubo, slot);
- GPU_shader_uniform_1i(self->shader, name, slot);
+ GPU_uniformbuf_bind(py_ubo->ubo, binding);
Py_RETURN_NONE;
}
@@ -831,6 +830,38 @@ static PyObject *pygpu_shader_code_from_builtin(BPyGPUShader *UNUSED(self), PyOb
return r_dict;
}
+PyDoc_STRVAR(pygpu_shader_create_from_info_doc,
+ ".. function:: create_from_info(shader_info)\n"
+ "\n"
+ " Create shader from a GPUShaderCreateInfo.\n"
+ "\n"
+ " :param shader_info: GPUShaderCreateInfo\n"
+ " :type shader_info: :class:`bpy.types.GPUShaderCreateInfo`\n"
+ " :return: Shader object corresponding to the given name.\n"
+ " :rtype: :class:`bpy.types.GPUShader`\n");
+static PyObject *pygpu_shader_create_from_info(BPyGPUShader *UNUSED(self),
+ BPyGPUShaderCreateInfo *o)
+{
+ if (!BPyGPUShaderCreateInfo_Check(o)) {
+ PyErr_Format(PyExc_TypeError, "Expected a GPUShaderCreateInfo, got %s", Py_TYPE(o)->tp_name);
+ return NULL;
+ }
+
+ char error[128];
+ if (!GPU_shader_create_info_check_error(o->info, error)) {
+ PyErr_SetString(PyExc_Exception, error);
+ return NULL;
+ }
+
+ GPUShader *shader = GPU_shader_create_from_info(o->info);
+ if (!shader) {
+ PyErr_SetString(PyExc_Exception, "Shader Compile Error, see console for more details");
+ return NULL;
+ }
+
+ return BPyGPUShader_CreatePyObject(shader, false);
+}
+
static struct PyMethodDef pygpu_shader_module__tp_methods[] = {
{"unbind", (PyCFunction)pygpu_shader_unbind, METH_NOARGS, pygpu_shader_unbind_doc},
{"from_builtin",
@@ -841,6 +872,10 @@ static struct PyMethodDef pygpu_shader_module__tp_methods[] = {
(PyCFunction)pygpu_shader_code_from_builtin,
METH_O,
pygpu_shader_code_from_builtin_doc},
+ {"create_from_info",
+ (PyCFunction)pygpu_shader_create_from_info,
+ METH_O,
+ pygpu_shader_create_from_info_doc},
{NULL, NULL, 0, NULL},
};
diff --git a/source/blender/python/gpu/gpu_py_shader.h b/source/blender/python/gpu/gpu_py_shader.h
index eb4810efeac..b5944c4b3a0 100644
--- a/source/blender/python/gpu/gpu_py_shader.h
+++ b/source/blender/python/gpu/gpu_py_shader.h
@@ -6,6 +6,12 @@
#pragma once
+/* Make sure that there is always a reference count for PyObjects of type String as the strings are
+ * passed by reference in the #GPUStageInterfaceInfo and #GPUShaderCreateInfo APIs. */
+#define USE_GPU_PY_REFERENCES
+
+/* gpu_py_shader.c */
+
extern PyTypeObject BPyGPUShader_Type;
#define BPyGPUShader_Check(v) (Py_TYPE(v) == &BPyGPUShader_Type)
@@ -18,3 +24,44 @@ typedef struct BPyGPUShader {
PyObject *BPyGPUShader_CreatePyObject(struct GPUShader *shader, bool is_builtin);
PyObject *bpygpu_shader_init(void);
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* gpu_py_shader_create_info.cc */
+
+extern PyTypeObject BPyGPUShaderCreateInfo_Type;
+extern PyTypeObject BPyGPUStageInterfaceInfo_Type;
+
+#define BPyGPUShaderCreateInfo_Check(v) (Py_TYPE(v) == &BPyGPUShaderCreateInfo_Type)
+#define BPyGPUStageInterfaceInfo_Check(v) (Py_TYPE(v) == &BPyGPUStageInterfaceInfo_Type)
+
+typedef struct BPyGPUStageInterfaceInfo {
+ PyObject_VAR_HEAD
+ struct GPUStageInterfaceInfo *interface;
+#ifdef USE_GPU_PY_REFERENCES
+ /* Just to keep a user to prevent freeing buf's we're using. */
+ PyObject *references;
+#endif
+} BPyGPUStageInterfaceInfo;
+
+typedef struct BPyGPUShaderCreateInfo {
+ PyObject_VAR_HEAD
+ struct GPUShaderCreateInfo *info;
+#ifdef USE_GPU_PY_REFERENCES
+ /* Just to keep a user to prevent freeing buf's we're using. */
+ PyObject *vertex_source;
+ PyObject *fragment_source;
+ PyObject *typedef_source;
+ PyObject *references;
+#endif
+ size_t constants_total_size;
+} BPyGPUShaderCreateInfo;
+
+PyObject *BPyGPUStageInterfaceInfo_CreatePyObject(struct GPUStageInterfaceInfo *interface);
+PyObject *BPyGPUShaderCreateInfo_CreatePyObject(struct GPUShaderCreateInfo *info);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/python/gpu/gpu_py_shader_create_info.cc b/source/blender/python/gpu/gpu_py_shader_create_info.cc
new file mode 100644
index 00000000000..3b043c605fa
--- /dev/null
+++ b/source/blender/python/gpu/gpu_py_shader_create_info.cc
@@ -0,0 +1,1130 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup bpygpu
+ *
+ * - Use `bpygpu_` for local API.
+ * - Use `BPyGPU` for public API.
+ */
+
+#include <Python.h>
+
+#include "BLI_utildefines.h"
+
+#include "GPU_shader.h"
+#include "intern/gpu_shader_create_info.hh"
+
+#include "../generic/py_capi_utils.h"
+#include "../generic/python_utildefines.h"
+
+#include "gpu_py_shader.h" /* own include */
+
+//#define USE_PYGPU_SHADER_INFO_IMAGE_METHOD
+
+using blender::gpu::shader::DualBlend;
+using blender::gpu::shader::Frequency;
+using blender::gpu::shader::ImageType;
+using blender::gpu::shader::ShaderCreateInfo;
+using blender::gpu::shader::StageInterfaceInfo;
+using blender::gpu::shader::Type;
+
+#ifdef USE_PYGPU_SHADER_INFO_IMAGE_METHOD
+using blender::gpu::shader::Qualifier;
+
+# define PYDOC_QUALIFIERS \
+ " - ``NO_RESTRICT``\n" \
+ " - ``READ``\n" \
+ " - ``WRITE``\n"
+static const struct PyC_FlagSet pygpu_qualifiers[] = {
+ {(int)Qualifier::NO_RESTRICT, "NO_RESTRICT"},
+ {(int)Qualifier::READ, "READ"},
+ {(int)Qualifier::WRITE, "WRITE"},
+ {0, nullptr},
+};
+#endif
+
+#define PYDOC_TYPE_LIST \
+ " - ``FLOAT``\n" \
+ " - ``VEC2``\n" \
+ " - ``VEC3``\n" \
+ " - ``VEC4``\n" \
+ " - ``MAT3``\n" \
+ " - ``MAT4``\n" \
+ " - ``UINT``\n" \
+ " - ``UVEC2``\n" \
+ " - ``UVEC3``\n" \
+ " - ``UVEC4``\n" \
+ " - ``INT``\n" \
+ " - ``IVEC2``\n" \
+ " - ``IVEC3``\n" \
+ " - ``IVEC4``\n" \
+ " - ``BOOL``\n"
+static const struct PyC_StringEnumItems pygpu_attrtype_items[] = {
+ {(int)Type::FLOAT, "FLOAT"},
+ {(int)Type::VEC2, "VEC2"},
+ {(int)Type::VEC3, "VEC3"},
+ {(int)Type::VEC4, "VEC4"},
+ {(int)Type::MAT3, "MAT3"},
+ {(int)Type::MAT4, "MAT4"},
+ {(int)Type::UINT, "UINT"},
+ {(int)Type::UVEC2, "UVEC2"},
+ {(int)Type::UVEC3, "UVEC3"},
+ {(int)Type::UVEC4, "UVEC4"},
+ {(int)Type::INT, "INT"},
+ {(int)Type::IVEC2, "IVEC2"},
+ {(int)Type::IVEC3, "IVEC3"},
+ {(int)Type::IVEC4, "IVEC4"},
+ {(int)Type::BOOL, "BOOL"},
+ {0, nullptr},
+};
+
+#define PYDOC_IMAGE_TYPES \
+ " - ``FLOAT_BUFFER``\n" \
+ " - ``FLOAT_1D``\n" \
+ " - ``FLOAT_1D_ARRAY``\n" \
+ " - ``FLOAT_2D``\n" \
+ " - ``FLOAT_2D_ARRAY``\n" \
+ " - ``FLOAT_3D``\n" \
+ " - ``FLOAT_CUBE``\n" \
+ " - ``FLOAT_CUBE_ARRAY``\n" \
+ " - ``INT_BUFFER``\n" \
+ " - ``INT_1D``\n" \
+ " - ``INT_1D_ARRAY``\n" \
+ " - ``INT_2D``\n" \
+ " - ``INT_2D_ARRAY``\n" \
+ " - ``INT_3D``\n" \
+ " - ``INT_CUBE``\n" \
+ " - ``INT_CUBE_ARRAY``\n" \
+ " - ``UINT_BUFFER``\n" \
+ " - ``UINT_1D``\n" \
+ " - ``UINT_1D_ARRAY``\n" \
+ " - ``UINT_2D``\n" \
+ " - ``UINT_2D_ARRAY``\n" \
+ " - ``UINT_3D``\n" \
+ " - ``UINT_CUBE``\n" \
+ " - ``UINT_CUBE_ARRAY``\n" \
+ " - ``SHADOW_2D``\n" \
+ " - ``SHADOW_2D_ARRAY``\n" \
+ " - ``SHADOW_CUBE``\n" \
+ " - ``SHADOW_CUBE_ARRAY``\n" \
+ " - ``DEPTH_2D``\n" \
+ " - ``DEPTH_2D_ARRAY``\n" \
+ " - ``DEPTH_CUBE``\n" \
+ " - ``DEPTH_CUBE_ARRAY``\n"
+static const struct PyC_StringEnumItems pygpu_imagetype_items[] = {
+ {(int)ImageType::FLOAT_BUFFER, "FLOAT_BUFFER"},
+ {(int)ImageType::FLOAT_1D, "FLOAT_1D"},
+ {(int)ImageType::FLOAT_1D_ARRAY, "FLOAT_1D_ARRAY"},
+ {(int)ImageType::FLOAT_2D, "FLOAT_2D"},
+ {(int)ImageType::FLOAT_2D_ARRAY, "FLOAT"},
+ {(int)ImageType::FLOAT_3D, "FLOAT_2D_ARRAY"},
+ {(int)ImageType::FLOAT_CUBE, "FLOAT_CUBE"},
+ {(int)ImageType::FLOAT_CUBE_ARRAY, "FLOAT_CUBE_ARRAY"},
+ {(int)ImageType::INT_BUFFER, "INT_BUFFER"},
+ {(int)ImageType::INT_1D, "INT_1D"},
+ {(int)ImageType::INT_1D_ARRAY, "INT_1D_ARRAY"},
+ {(int)ImageType::INT_2D, "INT_2D"},
+ {(int)ImageType::INT_2D_ARRAY, "INT_2D_ARRAY"},
+ {(int)ImageType::INT_3D, "INT_3D"},
+ {(int)ImageType::INT_CUBE, "INT_CUBE"},
+ {(int)ImageType::INT_CUBE_ARRAY, "INT_CUBE_ARRAY"},
+ {(int)ImageType::UINT_BUFFER, "UINT_BUFFER"},
+ {(int)ImageType::UINT_1D, "UINT_1D"},
+ {(int)ImageType::UINT_1D_ARRAY, "UINT_1D_ARRAY"},
+ {(int)ImageType::UINT_2D, "UINT_2D"},
+ {(int)ImageType::UINT_2D_ARRAY, "UINT_2D_ARRAY"},
+ {(int)ImageType::UINT_3D, "UINT_3D"},
+ {(int)ImageType::UINT_CUBE, "UINT_CUBE"},
+ {(int)ImageType::UINT_CUBE_ARRAY, "UINT_CUBE_ARRAY"},
+ {(int)ImageType::SHADOW_2D, "SHADOW_2D"},
+ {(int)ImageType::SHADOW_2D_ARRAY, "SHADOW_2D_ARRAY"},
+ {(int)ImageType::SHADOW_CUBE, "SHADOW_CUBE"},
+ {(int)ImageType::SHADOW_CUBE_ARRAY, "SHADOW_CUBE_ARRAY"},
+ {(int)ImageType::DEPTH_2D, "DEPTH_2D"},
+ {(int)ImageType::DEPTH_2D_ARRAY, "DEPTH_2D_ARRAY"},
+ {(int)ImageType::DEPTH_CUBE, "DEPTH_CUBE"},
+ {(int)ImageType::DEPTH_CUBE_ARRAY, "DEPTH_CUBE_ARRAY"},
+ {0, nullptr},
+};
+
+static const struct PyC_StringEnumItems pygpu_dualblend_items[] = {
+ {(int)DualBlend::NONE, "NONE"},
+ {(int)DualBlend::SRC_0, "SRC_0"},
+ {(int)DualBlend::SRC_1, "SRC_1"},
+ {0, nullptr},
+};
+
+/* -------------------------------------------------------------------- */
+/** \name GPUStageInterfaceInfo Methods
+ * \{ */
+
+static bool pygpu_interface_info_get_args(BPyGPUStageInterfaceInfo *self,
+ PyObject *args,
+ const char *format,
+ Type *r_type,
+ const char **r_name)
+{
+ struct PyC_StringEnum pygpu_type = {pygpu_attrtype_items};
+ PyObject *py_name;
+
+ if (!PyArg_ParseTuple(args, format, PyC_ParseStringEnum, &pygpu_type, &py_name)) {
+ return false;
+ }
+
+ const char *name = PyUnicode_AsUTF8(py_name);
+ if (name == nullptr) {
+ return false;
+ }
+
+#ifdef USE_GPU_PY_REFERENCES
+ PyList_Append(self->references, (PyObject *)py_name);
+#endif
+
+ *r_type = (Type)pygpu_type.value_found;
+ *r_name = name;
+ return true;
+}
+
+PyDoc_STRVAR(pygpu_interface_info_smooth_doc,
+ ".. method:: smooth(type, name)\n"
+ "\n"
+ " Add an attribute with qualifier of type `smooth` to the interface block.\n"
+ "\n"
+ " :param type: One of these types:\n"
+ "\n" PYDOC_TYPE_LIST
+ "\n"
+ " :type type: str\n"
+ " :param name: name of the attribute.\n"
+ " :type name: str\n");
+static PyObject *pygpu_interface_info_smooth(BPyGPUStageInterfaceInfo *self, PyObject *args)
+{
+ Type type;
+ const char *name;
+ if (!pygpu_interface_info_get_args(self, args, "O&O:smooth", &type, &name)) {
+ return nullptr;
+ }
+
+ StageInterfaceInfo *interface = reinterpret_cast<StageInterfaceInfo *>(self->interface);
+ interface->smooth(type, name);
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(pygpu_interface_info_flat_doc,
+ ".. method:: flat(type, name)\n"
+ "\n"
+ " Add an attribute with qualifier of type `flat` to the interface block.\n"
+ "\n"
+ " :param type: One of these types:\n"
+ "\n" PYDOC_TYPE_LIST
+ "\n"
+ " :type type: str\n"
+ " :param name: name of the attribute.\n"
+ " :type name: str\n");
+static PyObject *pygpu_interface_info_flat(BPyGPUStageInterfaceInfo *self, PyObject *args)
+{
+ Type type;
+ const char *name;
+ if (!pygpu_interface_info_get_args(self, args, "O&O:flat", &type, &name)) {
+ return nullptr;
+ }
+
+ StageInterfaceInfo *interface = reinterpret_cast<StageInterfaceInfo *>(self->interface);
+ interface->flat(type, name);
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(
+ pygpu_interface_info_no_perspective_doc,
+ ".. method:: no_perspective(type, name)\n"
+ "\n"
+ " Add an attribute with qualifier of type `no_perspective` to the interface block.\n"
+ "\n"
+ " :param type: One of these types:\n"
+ "\n" PYDOC_TYPE_LIST
+ "\n"
+ " :type type: str\n"
+ " :param name: name of the attribute.\n"
+ " :type name: str\n");
+static PyObject *pygpu_interface_info_no_perspective(BPyGPUStageInterfaceInfo *self,
+ PyObject *args)
+{
+ Type type;
+ const char *name;
+ if (!pygpu_interface_info_get_args(self, args, "O&O:no_perspective", &type, &name)) {
+ return nullptr;
+ }
+
+ StageInterfaceInfo *interface = reinterpret_cast<StageInterfaceInfo *>(self->interface);
+ interface->no_perspective(type, name);
+ Py_RETURN_NONE;
+}
+
+static struct PyMethodDef pygpu_interface_info__tp_methods[] = {
+ {"smooth",
+ (PyCFunction)pygpu_interface_info_smooth,
+ METH_VARARGS,
+ pygpu_interface_info_smooth_doc},
+ {"flat", (PyCFunction)pygpu_interface_info_flat, METH_VARARGS, pygpu_interface_info_flat_doc},
+ {"no_perspective",
+ (PyCFunction)pygpu_interface_info_no_perspective,
+ METH_VARARGS,
+ pygpu_interface_info_no_perspective_doc},
+ {nullptr, nullptr, 0, nullptr},
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name GPUStageInterfaceInfo Getters and Setters
+ * \{ */
+
+PyDoc_STRVAR(pygpu_interface_info_name_doc,
+ "Name of the interface block.\n"
+ "\n"
+ ":type: str");
+static PyObject *pygpu_interface_info_name_get(BPyGPUStageInterfaceInfo *self,
+ void *UNUSED(closure))
+{
+ StageInterfaceInfo *interface = reinterpret_cast<StageInterfaceInfo *>(self->interface);
+ return PyUnicode_FromString(interface->name.c_str());
+}
+
+static PyGetSetDef pygpu_interface_info__tp_getseters[] = {
+ {"name",
+ (getter)pygpu_interface_info_name_get,
+ (setter) nullptr,
+ pygpu_interface_info_name_doc,
+ nullptr},
+ {nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name GPUStageInterfaceInfo Type
+ * \{ */
+
+static PyObject *pygpu_interface_info__tp_new(PyTypeObject *UNUSED(type),
+ PyObject *args,
+ PyObject *kwds)
+{
+ if (kwds) {
+ PyErr_SetString(PyExc_TypeError, "no keywords are expected");
+ return nullptr;
+ }
+
+ const char *name;
+ if (!PyArg_ParseTuple(args, "s:GPUStageInterfaceInfo.__new__*", &name)) {
+ return nullptr;
+ }
+
+ StageInterfaceInfo *interface = new StageInterfaceInfo(name, "");
+ GPUStageInterfaceInfo *interface_info = reinterpret_cast<GPUStageInterfaceInfo *>(interface);
+
+ auto *self = BPyGPUStageInterfaceInfo_CreatePyObject(interface_info);
+
+#ifdef USE_GPU_PY_REFERENCES
+ PyObject *py_name = PyTuple_GET_ITEM(args, 0);
+ PyList_Append(((BPyGPUStageInterfaceInfo *)self)->references, py_name);
+#endif
+
+ return self;
+}
+
+#ifdef USE_GPU_PY_REFERENCES
+
+static int pygpu_interface_info__tp_traverse(PyObject *self, visitproc visit, void *arg)
+{
+ BPyGPUStageInterfaceInfo *py_interface = reinterpret_cast<BPyGPUStageInterfaceInfo *>(self);
+ Py_VISIT(py_interface->references);
+ return 0;
+}
+
+static int pygpu_interface_info__tp_clear(PyObject *self)
+{
+ BPyGPUStageInterfaceInfo *py_interface = reinterpret_cast<BPyGPUStageInterfaceInfo *>(self);
+ Py_CLEAR(py_interface->references);
+ return 0;
+}
+
+#endif
+
+static void pygpu_interface_info__tp_dealloc(PyObject *self)
+{
+ BPyGPUStageInterfaceInfo *py_interface = reinterpret_cast<BPyGPUStageInterfaceInfo *>(self);
+ StageInterfaceInfo *interface = reinterpret_cast<StageInterfaceInfo *>(py_interface->interface);
+ delete interface;
+
+#ifdef USE_GPU_PY_REFERENCES
+ PyObject_GC_UnTrack(self);
+ if (py_interface->references) {
+ pygpu_interface_info__tp_clear(self);
+ Py_CLEAR(py_interface->references);
+ }
+#endif
+
+ Py_TYPE(self)->tp_free((PyObject *)self);
+}
+
+PyDoc_STRVAR(pygpu_interface_info__tp_doc,
+ ".. class:: GPUStageInterfaceInfo(name)\n"
+ "\n"
+ " List of varyings between shader stages.\n\n"
+ "\n"
+ " :param name: Name of the interface block.\n"
+ " :type value: str\n");
+constexpr PyTypeObject pygpu_interface_info_type()
+{
+ PyTypeObject pytype = {PyVarObject_HEAD_INIT(nullptr, 0)};
+ pytype.tp_name = "GPUStageInterfaceInfo";
+ pytype.tp_basicsize = sizeof(BPyGPUStageInterfaceInfo);
+ pytype.tp_dealloc = pygpu_interface_info__tp_dealloc;
+ pytype.tp_doc = pygpu_interface_info__tp_doc;
+#ifdef USE_GPU_PY_REFERENCES
+ pytype.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC;
+ pytype.tp_traverse = pygpu_interface_info__tp_traverse;
+ pytype.tp_clear = pygpu_interface_info__tp_clear;
+#else
+ pytype.tp_flags = Py_TPFLAGS_DEFAULT,
+#endif
+ pytype.tp_methods = pygpu_interface_info__tp_methods;
+ pytype.tp_getset = pygpu_interface_info__tp_getseters;
+ pytype.tp_new = pygpu_interface_info__tp_new;
+ return pytype;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name GPUShaderCreateInfo Methods
+ * \{ */
+
+PyDoc_STRVAR(pygpu_shader_info_vertex_in_doc,
+ ".. method:: vertex_in(slot, type, name)\n"
+ "\n"
+ " Add a vertex shader input attribute.\n"
+ "\n"
+ " :param slot: The attribute index.\n"
+ " :type slot: int\n"
+ " :param type: One of these types:\n"
+ "\n" PYDOC_TYPE_LIST
+ "\n"
+ " :type type: str\n"
+ " :param name: name of the attribute.\n"
+ " :type name: str\n");
+static PyObject *pygpu_shader_info_vertex_in(BPyGPUShaderCreateInfo *self, PyObject *args)
+{
+ int slot;
+ struct PyC_StringEnum pygpu_type = {pygpu_attrtype_items};
+ const char *param;
+
+ if (!PyArg_ParseTuple(args, "iO&s:vertex_in", &slot, PyC_ParseStringEnum, &pygpu_type, &param)) {
+ return nullptr;
+ }
+
+#ifdef USE_GPU_PY_REFERENCES
+ PyObject *py_name = PyTuple_GET_ITEM(args, 2);
+ PyList_Append(self->references, py_name);
+#endif
+
+ ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(self->info);
+ info->vertex_in(slot, (Type)pygpu_type.value_found, param);
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(pygpu_shader_info_vertex_out_doc,
+ ".. method:: vertex_out(interface)\n"
+ "\n"
+ " Add a vertex shader output interface block.\n"
+ "\n"
+ " :param interface: Object describing the block.\n"
+ " :type interface: :class:`gpu.types.GPUStageInterfaceInfo`\n");
+static PyObject *pygpu_shader_info_vertex_out(BPyGPUShaderCreateInfo *self,
+ BPyGPUStageInterfaceInfo *o)
+{
+ if (!BPyGPUStageInterfaceInfo_Check(o)) {
+ PyErr_Format(PyExc_TypeError, "Expected a GPUStageInterfaceInfo, got %s", Py_TYPE(o)->tp_name);
+ return nullptr;
+ }
+
+#ifdef USE_GPU_PY_REFERENCES
+ PyList_Append(self->references, (PyObject *)o);
+#endif
+
+ ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(self->info);
+ StageInterfaceInfo *interface = reinterpret_cast<StageInterfaceInfo *>(o->interface);
+ info->vertex_out(*interface);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(pygpu_shader_info_fragment_out_doc,
+ ".. method:: fragment_out(slot, type, name, blend='NONE')\n"
+ "\n"
+ " Specify a fragment output corresponding to a framebuffer target slot.\n"
+ "\n"
+ " :param slot: The attribute index.\n"
+ " :type slot: int\n"
+ " :param type: One of these types:\n"
+ "\n" PYDOC_TYPE_LIST
+ "\n"
+ " :type type: str\n"
+ " :param name: Name of the attribute.\n"
+ " :type name: str\n"
+ " :param blend: Dual Source Blending Index. It can be 'NONE', 'SRC_0' or 'SRC_1'.\n"
+ " :type blend: str\n");
+static PyObject *pygpu_shader_info_fragment_out(BPyGPUShaderCreateInfo *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ int slot;
+ struct PyC_StringEnum pygpu_type = {pygpu_attrtype_items};
+ const char *name;
+ struct PyC_StringEnum blend_type = {pygpu_dualblend_items, (int)DualBlend::NONE};
+
+ static const char *_keywords[] = {"slot", "type", "name", "blend", nullptr};
+ static _PyArg_Parser _parser = {
+ "i" /* `slot` */
+ "O&" /* `type` */
+ "s" /* `name` */
+ "|$" /* Optional keyword only arguments. */
+ "O&" /* `blend` */
+ ":fragment_out",
+ _keywords,
+ nullptr,
+ };
+ if (!_PyArg_ParseTupleAndKeywordsFast(args,
+ kwds,
+ &_parser,
+ &slot,
+ PyC_ParseStringEnum,
+ &pygpu_type,
+ &name,
+ PyC_ParseStringEnum,
+ &blend_type)) {
+ return nullptr;
+ }
+
+#ifdef USE_GPU_PY_REFERENCES
+ PyObject *py_name = PyTuple_GET_ITEM(args, 2);
+ PyList_Append(self->references, py_name);
+#endif
+
+ ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(self->info);
+ info->fragment_out(slot, (Type)pygpu_type.value_found, name, (DualBlend)blend_type.value_found);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(
+ pygpu_shader_info_uniform_buf_doc,
+ ".. method:: uniform_buf(slot, type_name, name)\n"
+ "\n"
+ " Specify a uniform variable whose type can be one of those declared in `typedef_source`.\n"
+ "\n"
+ " :param slot: The uniform variable index.\n"
+ " :type slot: int\n"
+ " :param type_name: Name of the data type. It can be a struct type defined in the source "
+ "passed through the :meth:`gpu.types.GPUShaderCreateInfo.typedef_source`.\n"
+ " :type type_name: str\n"
+ " :param name: The uniform variable name.\n"
+ " :type name: str\n");
+static PyObject *pygpu_shader_info_uniform_buf(BPyGPUShaderCreateInfo *self, PyObject *args)
+{
+ int slot;
+ const char *type_name;
+ const char *name;
+
+ if (!PyArg_ParseTuple(args, "iss:uniform_buf", &slot, &type_name, &name)) {
+ return nullptr;
+ }
+
+#ifdef USE_GPU_PY_REFERENCES
+ PyList_Append(self->references, PyTuple_GET_ITEM(args, 1)); /* type_name */
+ PyList_Append(self->references, PyTuple_GET_ITEM(args, 2)); /* name */
+#endif
+
+ ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(self->info);
+ info->uniform_buf(slot, type_name, name);
+
+ Py_RETURN_NONE;
+}
+
+#ifdef USE_PYGPU_SHADER_INFO_IMAGE_METHOD
+PyDoc_STRVAR(
+ pygpu_shader_info_image_doc,
+ ".. method:: image(slot, format, type, name, qualifiers={'NO_RESTRICT'})\n"
+ "\n"
+ " Specify an image resource used for arbitrary load and store operations.\n"
+ "\n"
+ " :param slot: The image resource index.\n"
+ " :type slot: int\n"
+ " :param format: The GPUTexture format that is passed to the shader. Possible values are:\n"
+ "" PYDOC_TEX_FORMAT_ITEMS
+ " :type format: str\n"
+ " :param type: The data type describing how the image is to be read in the shader. "
+ "Possible values are:\n"
+ "\n" PYDOC_IMAGE_TYPES
+ "\n"
+ " :type type: str\n"
+ " :param name: The image resource name.\n"
+ " :type name: str\n"
+ " :param qualifiers: Set containing values that describe how the image resource is to be "
+ "read or written. Possible values are:\n"
+ "" PYDOC_QUALIFIERS
+ ""
+ " :type qualifiers: set\n");
+static PyObject *pygpu_shader_info_image(BPyGPUShaderCreateInfo *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ int slot;
+ struct PyC_StringEnum pygpu_texformat = {pygpu_textureformat_items};
+ struct PyC_StringEnum pygpu_imagetype = {pygpu_imagetype_items};
+ const char *name;
+ PyObject *py_qualifiers = nullptr;
+ Qualifier qualifier = Qualifier::NO_RESTRICT;
+
+ static const char *_keywords[] = {"slot", "format", "type", "name", "qualifiers", nullptr};
+ static _PyArg_Parser _parser = {
+ "i" /* `slot` */
+ "O&" /* `format` */
+ "O&" /* `type` */
+ "s" /* `name` */
+ "|$" /* Optional keyword only arguments. */
+ "O" /* `qualifiers` */
+ ":image",
+ _keywords,
+ nullptr,
+ };
+ if (!_PyArg_ParseTupleAndKeywordsFast(args,
+ kwds,
+ &_parser,
+ &slot,
+ PyC_ParseStringEnum,
+ &pygpu_texformat,
+ PyC_ParseStringEnum,
+ &pygpu_imagetype,
+ &name,
+ &py_qualifiers)) {
+ return nullptr;
+ }
+
+ if (py_qualifiers &&
+ PyC_FlagSet_ToBitfield(
+ pygpu_qualifiers, py_qualifiers, (int *)&qualifier, "shader_info.image") == -1) {
+ return nullptr;
+ }
+
+# ifdef USE_GPU_PY_REFERENCES
+ PyList_Append(self->references, PyTuple_GET_ITEM(args, 3)); /* name */
+# endif
+
+ ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(self->info);
+ info->image(slot,
+ (eGPUTextureFormat)pygpu_texformat.value_found,
+ qualifier,
+ (ImageType)pygpu_imagetype.value_found,
+ name);
+
+ Py_RETURN_NONE;
+}
+#endif
+
+PyDoc_STRVAR(
+ pygpu_shader_info_sampler_doc,
+ ".. method:: sampler(slot, type, name)\n"
+ "\n"
+ " Specify an image texture sampler.\n"
+ "\n"
+ " :param slot: The image texture sampler index.\n"
+ " :type slot: int\n"
+ " :param type: The data type describing the format of each sampler unit. Possible values "
+ "are:\n"
+ "\n" PYDOC_IMAGE_TYPES
+ "\n"
+ " :type type: str\n"
+ " :param name: The image texture sampler name.\n"
+ " :type name: str\n");
+static PyObject *pygpu_shader_info_sampler(BPyGPUShaderCreateInfo *self, PyObject *args)
+{
+ int slot;
+ struct PyC_StringEnum pygpu_samplertype = {pygpu_imagetype_items};
+ const char *name;
+
+ if (!PyArg_ParseTuple(
+ args, "iO&s:sampler", &slot, PyC_ParseStringEnum, &pygpu_samplertype, &name)) {
+ return nullptr;
+ }
+
+#ifdef USE_GPU_PY_REFERENCES
+ PyList_Append(self->references, PyTuple_GET_ITEM(args, 2)); /* name */
+#endif
+
+ ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(self->info);
+ info->sampler(slot, (ImageType)pygpu_samplertype.value_found, name);
+
+ Py_RETURN_NONE;
+}
+
+static int constant_type_size(Type type)
+{
+ switch (type) {
+ case Type::BOOL:
+ case Type::FLOAT:
+ case Type::INT:
+ case Type::UINT:
+ return 4;
+ break;
+ case Type::VEC2:
+ case Type::UVEC2:
+ case Type::IVEC2:
+ return 8;
+ break;
+ case Type::VEC3:
+ case Type::UVEC3:
+ case Type::IVEC3:
+ return 12;
+ break;
+ case Type::VEC4:
+ case Type::UVEC4:
+ case Type::IVEC4:
+ return 16;
+ break;
+ case Type::MAT3:
+ return 36 + 3 * 4;
+ case Type::MAT4:
+ return 64;
+ break;
+ }
+ BLI_assert(false);
+ return -1;
+}
+
+static int constants_calc_size(ShaderCreateInfo *info)
+{
+ int size_prev = 0;
+ int size_last = 0;
+ for (const ShaderCreateInfo::PushConst &uniform : info->push_constants_) {
+ int pad = 0;
+ int size = constant_type_size(uniform.type);
+ if (size_last && size_last != size) {
+ /* Calc pad. */
+ int pack = (size == 8) ? 8 : 16;
+ if (size_last < size) {
+ pad = pack - (size_last % pack);
+ }
+ else {
+ pad = size_prev % pack;
+ }
+ }
+ else if (size == 12) {
+ /* It is still unclear how Vulkan handles padding for `vec3` constants. For now let's follow
+ * the rules of the `std140` layout. */
+ pad = 4;
+ }
+ size_prev += pad + size * std::max(1, uniform.array_size);
+ size_last = size;
+ }
+ return size_prev + (size_prev % 16);
+}
+
+PyDoc_STRVAR(pygpu_shader_info_push_constant_doc,
+ ".. method:: push_constant(type, name, size=0)\n"
+ "\n"
+ " Specify a global access constant.\n"
+ "\n"
+ " :param type: One of these types:\n"
+ "\n" PYDOC_TYPE_LIST
+ "\n"
+ " :type type: str\n"
+ " :param name: Name of the constant.\n"
+ " :type name: str\n"
+ " :param size: If not zero, indicates that the constant is an array with the "
+ "specified size.\n"
+ " :type size: uint\n");
+static PyObject *pygpu_shader_info_push_constant(BPyGPUShaderCreateInfo *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ struct PyC_StringEnum pygpu_type = {pygpu_attrtype_items};
+ const char *name = nullptr;
+ int array_size = 0;
+
+ static const char *_keywords[] = {"type", "name", "size", nullptr};
+ static _PyArg_Parser _parser = {
+ "O&" /* `type` */
+ "s" /* `name` */
+ "|" /* Optional arguments. */
+ "I" /* `size` */
+ ":push_constant",
+ _keywords,
+ nullptr,
+ };
+ if (!_PyArg_ParseTupleAndKeywordsFast(
+ args, kwds, &_parser, PyC_ParseStringEnum, &pygpu_type, &name, &array_size)) {
+ return nullptr;
+ }
+
+#ifdef USE_GPU_PY_REFERENCES
+ PyObject *py_name = PyTuple_GET_ITEM(args, 1);
+ PyList_Append(self->references, py_name);
+#endif
+
+ ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(self->info);
+ info->push_constant((Type)pygpu_type.value_found, name, array_size);
+
+#define VULKAN_LIMIT 128
+ int size = constants_calc_size(info);
+ if (size > VULKAN_LIMIT) {
+ printf("Push constants have a minimum supported size of "
+ STRINGIFY(VULKAN_LIMIT)
+ " bytes, however the constants added so far already reach %d bytes. Consider using UBO.\n", size);
+ }
+#undef VULKAN_LIMIT
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(
+ pygpu_shader_info_vertex_source_doc,
+ ".. method:: vertex_source(source)\n"
+ "\n"
+ " Vertex shader source code written in GLSL.\n"
+ "\n"
+ " Example:\n"
+ "\n"
+ " .. code-block:: python\n"
+ "\n"
+ " \"void main {gl_Position = vec4(pos, 1.0);}\"\n"
+ "\n"
+ " :param source: The vertex shader source code.\n"
+ " :type source: str\n"
+ "\n"
+ " .. seealso:: `GLSL Cross Compilation "
+ "<https://wiki.blender.org/wiki/EEVEE_%26_Viewport/GPU_Module/GLSL_Cross_Compilation>`__\n");
+static PyObject *pygpu_shader_info_vertex_source(BPyGPUShaderCreateInfo *self, PyObject *o)
+{
+ const char *vertex_source = PyUnicode_AsUTF8(o);
+ if (vertex_source == nullptr) {
+ PyErr_Format(PyExc_ValueError, "expected a string, got %s", Py_TYPE(o)->tp_name);
+ return nullptr;
+ }
+
+#ifdef USE_GPU_PY_REFERENCES
+ if (self->vertex_source) {
+ Py_DECREF(self->vertex_source);
+ }
+
+ self->vertex_source = o;
+ Py_INCREF(o);
+#endif
+
+ ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(self->info);
+ info->vertex_source("common_colormanagement_lib.glsl");
+ info->vertex_source_generated = vertex_source;
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(
+ pygpu_shader_info_fragment_source_doc,
+ ".. method:: fragment_source(source)\n"
+ "\n"
+ " Fragment shader source code written in GLSL.\n"
+ "\n"
+ " Example:\n"
+ "\n"
+ " .. code-block:: python\n"
+ "\n"
+ " \"void main {fragColor = vec4(0.0, 0.0, 0.0, 1.0);}\"\n"
+ "\n"
+ " :param source: The fragment shader source code.\n"
+ " :type source: str\n"
+ "\n"
+ " .. seealso:: `GLSL Cross Compilation "
+ "<https://wiki.blender.org/wiki/EEVEE_%26_Viewport/GPU_Module/GLSL_Cross_Compilation>`__\n");
+static PyObject *pygpu_shader_info_fragment_source(BPyGPUShaderCreateInfo *self, PyObject *o)
+{
+ const char *fragment_source = PyUnicode_AsUTF8(o);
+ if (fragment_source == nullptr) {
+ PyErr_Format(PyExc_ValueError, "expected a string, got %s", Py_TYPE(o)->tp_name);
+ return nullptr;
+ }
+
+#ifdef USE_GPU_PY_REFERENCES
+ if (self->fragment_source) {
+ Py_DECREF(self->fragment_source);
+ }
+
+ self->fragment_source = o;
+ Py_INCREF(o);
+#endif
+
+ ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(self->info);
+ info->fragment_source("common_colormanagement_lib.glsl");
+ info->fragment_source_generated = fragment_source;
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(pygpu_shader_info_typedef_source_doc,
+ ".. method:: typedef_source(source)\n"
+ "\n"
+ " Source code included before resource declaration. "
+ "Useful for defining structs used by Uniform Buffers.\n"
+ "\n"
+ " Example:\n"
+ "\n"
+ ".. code-block:: python\n"
+ "\n"
+ " \"struct MyType {int foo; float bar;};\"\n"
+ "\n"
+ " :param source: The source code defining types.\n"
+ " :type source: str\n");
+static PyObject *pygpu_shader_info_typedef_source(BPyGPUShaderCreateInfo *self, PyObject *o)
+{
+ const char *typedef_source = PyUnicode_AsUTF8(o);
+ if (typedef_source == nullptr) {
+ PyErr_Format(PyExc_ValueError, "expected a string, got %s", Py_TYPE(o)->tp_name);
+ return nullptr;
+ }
+
+#ifdef USE_GPU_PY_REFERENCES
+ if (self->typedef_source) {
+ Py_DECREF(self->typedef_source);
+ }
+
+ self->typedef_source = o;
+ Py_INCREF(o);
+#endif
+
+ ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(self->info);
+#if 0
+ if (info->typedef_sources_.is_empty()) {
+ info->typedef_source("GPU_shader_shared_utils.h");
+ }
+#endif
+ info->typedef_source_generated = typedef_source;
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(pygpu_shader_info_define_doc,
+ ".. method:: define(name, value)\n"
+ "\n"
+ " Add a preprocessing define directive. In GLSL it would be something like:\n"
+ "\n"
+ ".. code-block:: glsl\n"
+ "\n"
+ " #define name value\n"
+ "\n"
+ " :param name: Token name.\n"
+ " :type name: str\n"
+ " :param value: Text that replaces token occurrences.\n"
+ " :type value: str\n");
+static PyObject *pygpu_shader_info_define(BPyGPUShaderCreateInfo *self, PyObject *args)
+{
+ const char *name;
+ const char *value = nullptr;
+
+ if (!PyArg_ParseTuple(args, "s|s:define", &name, &value)) {
+ return nullptr;
+ }
+
+#ifdef USE_GPU_PY_REFERENCES
+ PyList_Append(self->references, PyTuple_GET_ITEM(args, 0)); /* name */
+ if (value) {
+ PyList_Append(self->references, PyTuple_GET_ITEM(args, 1)); /* value */
+ }
+#endif
+
+ ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(self->info);
+ if (value) {
+ info->define(name, value);
+ }
+ else {
+ info->define(name);
+ }
+
+ Py_RETURN_NONE;
+}
+
+static struct PyMethodDef pygpu_shader_info__tp_methods[] = {
+ {"vertex_in",
+ (PyCFunction)pygpu_shader_info_vertex_in,
+ METH_VARARGS,
+ pygpu_shader_info_vertex_in_doc},
+ {"vertex_out",
+ (PyCFunction)pygpu_shader_info_vertex_out,
+ METH_O,
+ pygpu_shader_info_vertex_out_doc},
+ {"fragment_out",
+ (PyCFunction)(void *)pygpu_shader_info_fragment_out,
+ METH_VARARGS | METH_KEYWORDS,
+ pygpu_shader_info_fragment_out_doc},
+ {"uniform_buf",
+ (PyCFunction)(void *)pygpu_shader_info_uniform_buf,
+ METH_VARARGS,
+ pygpu_shader_info_uniform_buf_doc},
+#ifdef USE_PYGPU_SHADER_INFO_IMAGE_METHOD
+ {"image",
+ (PyCFunction)(void *)pygpu_shader_info_image,
+ METH_VARARGS | METH_KEYWORDS,
+ pygpu_shader_info_image_doc},
+#endif
+ {"sampler",
+ (PyCFunction)pygpu_shader_info_sampler,
+ METH_VARARGS,
+ pygpu_shader_info_sampler_doc},
+ {"push_constant",
+ (PyCFunction)(void *)pygpu_shader_info_push_constant,
+ METH_VARARGS | METH_KEYWORDS,
+ pygpu_shader_info_push_constant_doc},
+ {"vertex_source",
+ (PyCFunction)pygpu_shader_info_vertex_source,
+ METH_O,
+ pygpu_shader_info_vertex_source_doc},
+ {"fragment_source",
+ (PyCFunction)pygpu_shader_info_fragment_source,
+ METH_O,
+ pygpu_shader_info_fragment_source_doc},
+ {"typedef_source",
+ (PyCFunction)pygpu_shader_info_typedef_source,
+ METH_O,
+ pygpu_shader_info_typedef_source_doc},
+ {"define", (PyCFunction)pygpu_shader_info_define, METH_VARARGS, pygpu_shader_info_define_doc},
+ {nullptr, nullptr, 0, nullptr},
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name GPUShaderCreateInfo Init
+ * \{ */
+
+static PyObject *pygpu_shader_info__tp_new(PyTypeObject *UNUSED(type),
+ PyObject *args,
+ PyObject *kwds)
+{
+ if (PyTuple_Size(args) || kwds) {
+ PyErr_SetString(PyExc_TypeError, "no args or keywords are expected");
+ return nullptr;
+ }
+
+ ShaderCreateInfo *info = new ShaderCreateInfo("pyGPU_Shader");
+ GPUShaderCreateInfo *shader_info = reinterpret_cast<GPUShaderCreateInfo *>(info);
+
+ return BPyGPUShaderCreateInfo_CreatePyObject(shader_info);
+}
+
+#ifdef USE_GPU_PY_REFERENCES
+
+static int pygpu_shader_info__tp_traverse(PyObject *self, visitproc visit, void *arg)
+{
+ BPyGPUShaderCreateInfo *py_info = reinterpret_cast<BPyGPUShaderCreateInfo *>(self);
+ Py_VISIT(py_info->vertex_source);
+ Py_VISIT(py_info->fragment_source);
+ Py_VISIT(py_info->references);
+ return 0;
+}
+
+static int pygpu_shader_info__tp_clear(PyObject *self)
+{
+ BPyGPUShaderCreateInfo *py_info = reinterpret_cast<BPyGPUShaderCreateInfo *>(self);
+ Py_CLEAR(py_info->vertex_source);
+ Py_CLEAR(py_info->fragment_source);
+ Py_CLEAR(py_info->references);
+ return 0;
+}
+
+#endif
+
+static void pygpu_shader_info__tp_dealloc(PyObject *self)
+{
+ BPyGPUShaderCreateInfo *py_info = reinterpret_cast<BPyGPUShaderCreateInfo *>(self);
+ ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(py_info->info);
+ delete info;
+
+#ifdef USE_GPU_PY_REFERENCES
+ PyObject_GC_UnTrack(self);
+ if (py_info->references || py_info->vertex_source || py_info->fragment_source) {
+ pygpu_shader_info__tp_clear(self);
+ Py_XDECREF(py_info->vertex_source);
+ Py_XDECREF(py_info->fragment_source);
+ Py_XDECREF(py_info->references);
+ }
+#endif
+
+ Py_TYPE(self)->tp_free((PyObject *)self);
+}
+
+PyDoc_STRVAR(pygpu_shader_info__tp_doc,
+ ".. class:: GPUShaderCreateInfo()\n"
+ "\n"
+ " Stores and describes types and variables that are used in shader sources.\n");
+constexpr PyTypeObject pygpu_shader_info_type()
+{
+ PyTypeObject pytype = {PyVarObject_HEAD_INIT(nullptr, 0)};
+ pytype.tp_name = "GPUShaderCreateInfo";
+ pytype.tp_basicsize = sizeof(BPyGPUShaderCreateInfo);
+ pytype.tp_dealloc = pygpu_shader_info__tp_dealloc;
+ pytype.tp_doc = pygpu_shader_info__tp_doc;
+#ifdef USE_GPU_PY_REFERENCES
+ pytype.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC;
+ pytype.tp_traverse = pygpu_shader_info__tp_traverse;
+ pytype.tp_clear = pygpu_shader_info__tp_clear;
+#else
+ pytype.tp_flags = Py_TPFLAGS_DEFAULT,
+#endif
+ pytype.tp_methods = pygpu_shader_info__tp_methods;
+ pytype.tp_new = pygpu_shader_info__tp_new;
+ return pytype;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Public API
+ * \{ */
+
+PyTypeObject BPyGPUStageInterfaceInfo_Type = pygpu_interface_info_type();
+PyTypeObject BPyGPUShaderCreateInfo_Type = pygpu_shader_info_type();
+
+PyObject *BPyGPUStageInterfaceInfo_CreatePyObject(GPUStageInterfaceInfo *interface)
+{
+ BPyGPUStageInterfaceInfo *self;
+
+#ifdef USE_GPU_PY_REFERENCES
+ self = (BPyGPUStageInterfaceInfo *)_PyObject_GC_New(&BPyGPUStageInterfaceInfo_Type);
+ self->references = PyList_New(0);
+#else
+ self = PyObject_New(BPyGPUStageInterfaceInfo, &BPyGPUStageInterfaceInfo_Type);
+#endif
+
+ self->interface = interface;
+
+ return (PyObject *)self;
+}
+
+PyObject *BPyGPUShaderCreateInfo_CreatePyObject(GPUShaderCreateInfo *info)
+{
+ BPyGPUShaderCreateInfo *self;
+
+#ifdef USE_GPU_PY_REFERENCES
+ self = (BPyGPUShaderCreateInfo *)_PyObject_GC_New(&BPyGPUShaderCreateInfo_Type);
+ self->vertex_source = nullptr;
+ self->fragment_source = nullptr;
+ self->typedef_source = nullptr;
+ self->references = PyList_New(0);
+#else
+ self = PyObject_New(BPyGPUShaderCreateInfo, &BPyGPUShaderCreateInfo_Type);
+#endif
+
+ self->info = info;
+ self->constants_total_size = 0;
+
+ return (PyObject *)self;
+}
+
+/** \} */
diff --git a/source/blender/python/gpu/gpu_py_types.c b/source/blender/python/gpu/gpu_py_types.c
index 6dff70ad530..65201df4a9e 100644
--- a/source/blender/python/gpu/gpu_py_types.c
+++ b/source/blender/python/gpu/gpu_py_types.c
@@ -59,6 +59,12 @@ PyObject *bpygpu_types_init(void)
if (PyType_Ready(&BPyGPUUniformBuf_Type) < 0) {
return NULL;
}
+ if (PyType_Ready(&BPyGPUShaderCreateInfo_Type) < 0) {
+ return NULL;
+ }
+ if (PyType_Ready(&BPyGPUStageInterfaceInfo_Type) < 0) {
+ return NULL;
+ }
PyModule_AddType(submodule, &BPyGPU_BufferType);
PyModule_AddType(submodule, &BPyGPUVertFormat_Type);
@@ -70,6 +76,8 @@ PyObject *bpygpu_types_init(void)
PyModule_AddType(submodule, &BPyGPUTexture_Type);
PyModule_AddType(submodule, &BPyGPUFrameBuffer_Type);
PyModule_AddType(submodule, &BPyGPUUniformBuf_Type);
+ PyModule_AddType(submodule, &BPyGPUShaderCreateInfo_Type);
+ PyModule_AddType(submodule, &BPyGPUStageInterfaceInfo_Type);
return submodule;
}
diff --git a/source/blender/python/gpu/gpu_py_uniformbuffer.c b/source/blender/python/gpu/gpu_py_uniformbuffer.c
index f5a0af860b4..f8f88d61cf6 100644
--- a/source/blender/python/gpu/gpu_py_uniformbuffer.c
+++ b/source/blender/python/gpu/gpu_py_uniformbuffer.c
@@ -64,26 +64,37 @@ static PyObject *pygpu_uniformbuffer__tp_new(PyTypeObject *UNUSED(self),
BPYGPU_IS_INIT_OR_ERROR_OBJ;
GPUUniformBuf *ubo = NULL;
- BPyGPUBuffer *pybuffer_obj;
+ PyObject *pybuffer_obj;
char err_out[256] = "unknown error. See console";
static const char *_keywords[] = {"data", NULL};
static _PyArg_Parser _parser = {
- "O!" /* `data` */
+ "O" /* `data` */
":GPUUniformBuf.__new__",
_keywords,
0,
};
- if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, &BPyGPU_BufferType, &pybuffer_obj)) {
+ if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, &pybuffer_obj)) {
return NULL;
}
- if (GPU_context_active_get()) {
- ubo = GPU_uniformbuf_create_ex(
- bpygpu_Buffer_size(pybuffer_obj), pybuffer_obj->buf.as_void, "python_uniformbuffer");
+ if (!GPU_context_active_get()) {
+ STRNCPY(err_out, "No active GPU context found");
}
else {
- STRNCPY(err_out, "No active GPU context found");
+ Py_buffer pybuffer;
+ if (PyObject_GetBuffer(pybuffer_obj, &pybuffer, PyBUF_SIMPLE) == -1) {
+ /* PyObject_GetBuffer raise a PyExc_BufferError */
+ return NULL;
+ }
+
+ if ((pybuffer.len % 16) != 0) {
+ STRNCPY(err_out, "UBO is not padded to size of vec4");
+ }
+ else {
+ ubo = GPU_uniformbuf_create_ex(pybuffer.len, pybuffer.buf, "python_uniformbuffer");
+ }
+ PyBuffer_Release(&pybuffer);
}
if (ubo == NULL) {
@@ -102,11 +113,14 @@ static PyObject *pygpu_uniformbuffer_update(BPyGPUUniformBuf *self, PyObject *ob
{
BPYGPU_UNIFORMBUF_CHECK_OBJ(self);
- if (!BPyGPU_Buffer_Check(obj)) {
+ Py_buffer pybuffer;
+ if (PyObject_GetBuffer(obj, &pybuffer, PyBUF_SIMPLE) == -1) {
+ /* PyObject_GetBuffer raise a PyExc_BufferError */
return NULL;
}
- GPU_uniformbuf_update(self->ubo, ((BPyGPUBuffer *)obj)->buf.as_void);
+ GPU_uniformbuf_update(self->ubo, pybuffer.buf);
+ PyBuffer_Release(&pybuffer);
Py_RETURN_NONE;
}
@@ -151,8 +165,8 @@ PyDoc_STRVAR(pygpu_uniformbuffer__tp_doc,
"\n"
" This object gives access to off uniform buffers.\n"
"\n"
- " :arg data: Buffer object.\n"
- " :type data: :class:`gpu.types.Buffer`\n");
+ " :arg data: Data to fill the buffer.\n"
+ " :type data: object exposing buffer interface\n");
PyTypeObject BPyGPUUniformBuf_Type = {
PyVarObject_HEAD_INIT(NULL, 0).tp_name = "GPUUniformBuf",
.tp_basicsize = sizeof(BPyGPUUniformBuf),
diff --git a/source/blender/sequencer/intern/effects.c b/source/blender/sequencer/intern/effects.c
index 70f83485bb5..0192f4f625c 100644
--- a/source/blender/sequencer/intern/effects.c
+++ b/source/blender/sequencer/intern/effects.c
@@ -3381,7 +3381,7 @@ static ImBuf *do_text_effect(const SeqRenderData *context,
/* vars for calculating wordwrap and optional box */
struct {
struct ResultBLF info;
- rctf rect;
+ rcti rect;
} wrap;
BLF_boundbox_ex(font, data->text, sizeof(data->text), &wrap.rect, &wrap.info);
@@ -3391,10 +3391,10 @@ static ImBuf *do_text_effect(const SeqRenderData *context,
}
else {
if (data->align == SEQ_TEXT_ALIGN_X_RIGHT) {
- x -= BLI_rctf_size_x(&wrap.rect);
+ x -= BLI_rcti_size_x(&wrap.rect);
}
else if (data->align == SEQ_TEXT_ALIGN_X_CENTER) {
- x -= BLI_rctf_size_x(&wrap.rect) / 2;
+ x -= BLI_rcti_size_x(&wrap.rect) / 2;
}
if (data->align_y == SEQ_TEXT_ALIGN_Y_TOP) {
diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c
index 3c8474b1b6c..4b506564260 100644
--- a/source/blender/windowmanager/intern/wm_draw.c
+++ b/source/blender/windowmanager/intern/wm_draw.c
@@ -24,6 +24,7 @@
#include "BLI_utildefines.h"
#include "BKE_context.h"
+#include "BKE_global.h"
#include "BKE_image.h"
#include "BKE_main.h"
#include "BKE_scene.h"
@@ -459,13 +460,24 @@ static void wm_draw_region_buffer_create(ARegion *region, bool stereo, bool use_
}
}
-static void wm_draw_region_bind(ARegion *region, int view)
+static bool wm_draw_region_bind(bContext *C, ARegion *region, int view)
{
if (!region->draw_buffer) {
- return;
+ return true;
}
if (region->draw_buffer->viewport) {
+ if (G.is_rendering && C != NULL) {
+ Scene *scene = CTX_data_scene(C);
+ RenderEngineType *render_engine_type = RE_engines_find(scene->r.engine);
+ if (RE_engine_is_opengl(render_engine_type)) {
+ /* Do not try to acquire the viewport as this would be locking at the moment.
+ * But tag the viewport to update after the rendering finishes. */
+ GPU_viewport_tag_update(region->draw_buffer->viewport);
+ return false;
+ }
+ }
+
GPU_viewport_bind(region->draw_buffer->viewport, view, &region->winrct);
}
else {
@@ -478,6 +490,7 @@ static void wm_draw_region_bind(ARegion *region, int view)
}
region->draw_buffer->bound_view = view;
+ return true;
}
static void wm_draw_region_unbind(ARegion *region)
@@ -700,9 +713,10 @@ static void wm_draw_window_offscreen(bContext *C, wmWindow *win, bool stereo)
wm_draw_region_stereo_set(bmain, area, region, sview);
}
- wm_draw_region_bind(region, view);
- ED_region_do_draw(C, region);
- wm_draw_region_unbind(region);
+ if (wm_draw_region_bind(C, region, view)) {
+ ED_region_do_draw(C, region);
+ wm_draw_region_unbind(region);
+ }
}
if (use_viewport) {
GPUViewport *viewport = region->draw_buffer->viewport;
@@ -711,9 +725,10 @@ static void wm_draw_window_offscreen(bContext *C, wmWindow *win, bool stereo)
}
else {
wm_draw_region_buffer_create(region, false, use_viewport);
- wm_draw_region_bind(region, 0);
- ED_region_do_draw(C, region);
- wm_draw_region_unbind(region);
+ if (wm_draw_region_bind(C, region, 0)) {
+ ED_region_do_draw(C, region);
+ wm_draw_region_unbind(region);
+ }
}
GPU_debug_group_end();
@@ -744,10 +759,11 @@ 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.0f, 0.0f, 0.0f, 0.0f);
- ED_region_do_draw(C, region);
- wm_draw_region_unbind(region);
+ if (wm_draw_region_bind(C, region, 0)) {
+ GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f);
+ ED_region_do_draw(C, region);
+ wm_draw_region_unbind(region);
+ }
GPU_debug_group_end();
@@ -1102,10 +1118,11 @@ void wm_draw_region_test(bContext *C, ScrArea *area, ARegion *region)
/* Function for redraw timer benchmark. */
bool use_viewport = WM_region_use_viewport(area, region);
wm_draw_region_buffer_create(region, false, use_viewport);
- wm_draw_region_bind(region, 0);
- ED_region_do_draw(C, region);
- wm_draw_region_unbind(region);
- region->do_draw = false;
+ if (wm_draw_region_bind(C, region, 0)) {
+ ED_region_do_draw(C, region);
+ wm_draw_region_unbind(region);
+ region->do_draw = false;
+ }
}
void WM_redraw_windows(bContext *C)
@@ -1141,7 +1158,7 @@ void WM_draw_region_viewport_ensure(ARegion *region, short space_type)
void WM_draw_region_viewport_bind(ARegion *region)
{
- wm_draw_region_bind(region, 0);
+ wm_draw_region_bind(NULL, region, 0);
}
void WM_draw_region_viewport_unbind(ARegion *region)
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index 84c40c42adc..87588c40b57 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -2691,6 +2691,51 @@ static int wm_action_not_handled(int action)
return action == WM_HANDLER_CONTINUE || action == (WM_HANDLER_BREAK | WM_HANDLER_MODAL);
}
+static const char *keymap_handler_log_action_str(const int action)
+{
+ if (action & WM_HANDLER_BREAK) {
+ return "handled";
+ }
+ if (action & WM_HANDLER_HANDLED) {
+ return "handled (and pass on)";
+ }
+ return "un-handled";
+}
+
+static const char *keymap_handler_log_kmi_event_str(const wmKeyMapItem *kmi,
+ char *buf,
+ size_t buf_maxlen)
+{
+ /* Short representation of the key that was pressed,
+ * include this since it may differ from the event in minor details
+ * which can help looking up the key-map definition. */
+ WM_keymap_item_to_string(kmi, false, buf, buf_maxlen);
+ return buf;
+}
+
+static const char *keymap_handler_log_kmi_op_str(bContext *C,
+ const wmKeyMapItem *kmi,
+ char *buf,
+ size_t buf_maxlen)
+{
+ /* The key-map item properties can further help distinguish this item from others. */
+ char *kmi_props = NULL;
+ if (kmi->properties != NULL) {
+ wmOperatorType *ot = WM_operatortype_find(kmi->idname, 0);
+ if (ot) {
+ kmi_props = RNA_pointer_as_string_keywords(C, kmi->ptr, false, false, true, 512);
+ }
+ else { /* Fallback. */
+ kmi_props = IDP_reprN(kmi->properties, NULL);
+ }
+ }
+ BLI_snprintf(buf, buf_maxlen, "%s(%s)", kmi->idname, kmi_props ? kmi_props : "");
+ if (kmi_props != NULL) {
+ MEM_freeN(kmi_props);
+ }
+ return buf;
+}
+
#define PRINT \
if (do_debug_handler) \
printf
@@ -2722,52 +2767,26 @@ static int wm_handlers_do_keymap_with_keymap_handler(
if (wm_eventmatch(event, kmi)) {
struct wmEventHandler_KeymapPost keymap_post = handler->post;
- if (do_debug_handler) {
- /* Short representation of the key that was pressed,
- * include this since it may differ from the event in minor details
- * which can help looking up the key-map definition. */
- char kmi_buf[256];
- WM_keymap_item_to_string(kmi, false, kmi_buf, sizeof(kmi_buf));
-
- /* The key-map item properties can further help distinguish this item from others. */
- char *kmi_props = NULL;
- if (kmi->properties != NULL) {
- wmOperatorType *ot = WM_operatortype_find(kmi->idname, 0);
- if (ot) {
- kmi_props = RNA_pointer_as_string_keywords(C, kmi->ptr, false, false, true, 512);
- }
- else { /* Fallback. */
- kmi_props = IDP_reprN(kmi->properties, NULL);
- }
- }
-
- printf("%s: item matched: \"%s\", %s(%s)\n",
- __func__,
- kmi_buf,
- kmi->idname,
- kmi_props ? kmi_props : "");
- if (kmi_props != NULL) {
- MEM_freeN(kmi_props);
- }
- }
-
action |= wm_handler_operator_call(
C, handlers, &handler->head, event, kmi->ptr, kmi->idname);
+ char op_buf[512];
+ char kmi_buf[128];
+ CLOG_INFO(WM_LOG_HANDLERS,
+ 2,
+ "keymap '%s', %s, %s, event: %s",
+ keymap->idname,
+ keymap_handler_log_kmi_op_str(C, kmi, op_buf, sizeof(op_buf)),
+ keymap_handler_log_action_str(action),
+ keymap_handler_log_kmi_event_str(kmi, kmi_buf, sizeof(kmi_buf)));
+
if (action & WM_HANDLER_BREAK) {
/* Not always_pass here, it denotes removed handler_base. */
- CLOG_INFO(WM_LOG_HANDLERS, 2, "handled! '%s'", kmi->idname);
if (keymap_post.post_fn != NULL) {
keymap_post.post_fn(keymap, kmi, keymap_post.user_data);
}
break;
}
- if (action & WM_HANDLER_HANDLED) {
- CLOG_INFO(WM_LOG_HANDLERS, 2, "handled - and pass on! '%s'", kmi->idname);
- }
- else {
- CLOG_INFO(WM_LOG_HANDLERS, 2, "un-handled '%s'", kmi->idname);
- }
}
}
}
@@ -3249,7 +3268,7 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
event->keymodifier = event->prev_press_keymodifier;
event->direction = direction;
- CLOG_INFO(WM_LOG_HANDLERS, 1, "handling PRESS_DRAG");
+ CLOG_INFO(WM_LOG_HANDLERS, 1, "handling CLICK_DRAG");
action |= wm_handlers_do_intern(C, win, event, handlers);
@@ -3262,13 +3281,17 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
win->event_queue_check_click = false;
if (!((action & WM_HANDLER_BREAK) == 0 || wm_action_not_handled(action))) {
/* Only disable when handled as other handlers may use this drag event. */
+ CLOG_INFO(WM_LOG_HANDLERS, 3, "canceling CLICK_DRAG: drag was generated & handled");
win->event_queue_check_drag = false;
}
}
}
}
else {
- win->event_queue_check_drag = false;
+ if (win->event_queue_check_drag) {
+ CLOG_INFO(WM_LOG_HANDLERS, 3, "canceling CLICK_DRAG: motion event was handled");
+ win->event_queue_check_drag = false;
+ }
}
}
else if (ISKEYBOARD_OR_BUTTON(event->type)) {
@@ -3282,7 +3305,10 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
if (event->val == KM_PRESS) {
if ((event->flag & WM_EVENT_IS_REPEAT) == 0) {
win->event_queue_check_click = true;
+
+ CLOG_INFO(WM_LOG_HANDLERS, 3, "detecting CLICK_DRAG: press event detected");
win->event_queue_check_drag = true;
+
win->event_queue_check_drag_handled = false;
}
}
@@ -3293,6 +3319,8 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
/* Support releasing modifier keys without canceling the drag event, see T89989. */
}
else {
+ CLOG_INFO(
+ WM_LOG_HANDLERS, 3, "CLICK_DRAG: canceling (release event didn't match press)");
win->event_queue_check_drag = false;
}
}
@@ -3305,7 +3333,12 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
if (win->event_queue_check_click == true) {
if (WM_event_drag_test(event, event->prev_press_xy)) {
win->event_queue_check_click = false;
- win->event_queue_check_drag = false;
+ if (win->event_queue_check_drag) {
+ CLOG_INFO(WM_LOG_HANDLERS,
+ 3,
+ "CLICK_DRAG: canceling (key-release exceeds drag threshold)");
+ win->event_queue_check_drag = false;
+ }
}
else {
/* Position is where the actual click happens, for more
@@ -3315,7 +3348,7 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
copy_v2_v2_int(event->xy, event->prev_press_xy);
event->val = KM_CLICK;
- CLOG_INFO(WM_LOG_HANDLERS, 1, "handling CLICK");
+ CLOG_INFO(WM_LOG_HANDLERS, 1, "CLICK: handling");
action |= wm_handlers_do_intern(C, win, event, handlers);
@@ -3339,7 +3372,14 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
}
else {
win->event_queue_check_click = false;
- win->event_queue_check_drag = false;
+
+ if (win->event_queue_check_drag) {
+ CLOG_INFO(WM_LOG_HANDLERS,
+ 3,
+ "CLICK_DRAG: canceling (button event was handled: value=%d)",
+ event->val);
+ win->event_queue_check_drag = false;
+ }
}
}
else if (ISMOUSE_WHEEL(event->type) || ISMOUSE_GESTURE(event->type)) {
@@ -4932,7 +4972,7 @@ static void wm_event_state_update_and_click_set(const GHOST_TEventType type,
/* Double click test. */
if (wm_event_is_double_click(event)) {
- CLOG_INFO(WM_LOG_HANDLERS, 1, "Send double click");
+ CLOG_INFO(WM_LOG_HANDLERS, 1, "DBL_CLICK: detected");
event->val = KM_DBL_CLICK;
}
else if (event->val == KM_PRESS) {
diff --git a/source/blender/windowmanager/intern/wm_toolsystem.c b/source/blender/windowmanager/intern/wm_toolsystem.c
index a8d4ca0f02b..388abe21578 100644
--- a/source/blender/windowmanager/intern/wm_toolsystem.c
+++ b/source/blender/windowmanager/intern/wm_toolsystem.c
@@ -551,7 +551,7 @@ void WM_toolsystem_refresh_screen_area(WorkSpace *workspace, ViewLayer *view_lay
void WM_toolsystem_refresh_screen_window(wmWindow *win)
{
WorkSpace *workspace = WM_window_get_active_workspace(win);
- bool space_type_has_tools[SPACE_TYPE_LAST + 1] = {0};
+ bool space_type_has_tools[SPACE_TYPE_NUM] = {0};
LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
space_type_has_tools[tref->space_type] = true;
}
diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c
index 89bb6906a22..382a37e09e5 100644
--- a/source/blender/windowmanager/intern/wm_window.c
+++ b/source/blender/windowmanager/intern/wm_window.c
@@ -1541,7 +1541,15 @@ void wm_ghost_init(bContext *C)
}
g_system = GHOST_CreateSystem();
- GHOST_SystemInitDebug(g_system, G.debug & G_DEBUG_GHOST);
+
+ GHOST_Debug debug = {0};
+ if (G.debug & G_DEBUG_GHOST) {
+ debug.flags |= GHOST_kDebugDefault;
+ }
+ if (G.debug & G_DEBUG_WINTAB) {
+ debug.flags |= GHOST_kDebugWintab;
+ }
+ GHOST_SystemInitDebug(g_system, debug);
if (C != NULL) {
GHOST_AddEventConsumer(g_system, consumer);
diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c
index 05b7f1bcb85..b3f5d24ee8c 100644
--- a/source/creator/creator_args.c
+++ b/source/creator/creator_args.c
@@ -576,6 +576,7 @@ static int arg_handle_print_help(int UNUSED(argc), const char **UNUSED(argv), vo
BLI_args_print_arg_doc(ba, "--debug-depsgraph-pretty");
BLI_args_print_arg_doc(ba, "--debug-depsgraph-uuid");
BLI_args_print_arg_doc(ba, "--debug-ghost");
+ BLI_args_print_arg_doc(ba, "--debug-wintab");
BLI_args_print_arg_doc(ba, "--debug-gpu");
BLI_args_print_arg_doc(ba, "--debug-gpu-force-workarounds");
BLI_args_print_arg_doc(ba, "--debug-wm");
@@ -943,6 +944,12 @@ static const char arg_handle_debug_mode_generic_set_doc_wm[] =
"\n\t"
"Enable debug messages for the window manager, shows all operators in search, shows "
"keymap errors.";
+static const char arg_handle_debug_mode_generic_set_doc_ghost[] =
+ "\n\t"
+ "Enable debug messages for Ghost (Linux only).";
+static const char arg_handle_debug_mode_generic_set_doc_wintab[] =
+ "\n\t"
+ "Enable debug messages for Wintab.";
# ifdef WITH_XR_OPENXR
static const char arg_handle_debug_mode_generic_set_doc_xr[] =
"\n\t"
@@ -2130,8 +2137,13 @@ void main_args_setup(bContext *C, bArgs *ba)
BLI_args_add(ba,
NULL,
"--debug-ghost",
- CB_EX(arg_handle_debug_mode_generic_set, handlers),
+ CB_EX(arg_handle_debug_mode_generic_set, ghost),
(void *)G_DEBUG_GHOST);
+ BLI_args_add(ba,
+ NULL,
+ "--debug-wintab",
+ CB_EX(arg_handle_debug_mode_generic_set, wintab),
+ (void *)G_DEBUG_WINTAB);
BLI_args_add(ba, NULL, "--debug-all", CB(arg_handle_debug_mode_all), NULL);
BLI_args_add(ba, NULL, "--debug-io", CB(arg_handle_debug_mode_io), NULL);
diff --git a/source/tools b/source/tools
-Subproject 1e658ca996f11e5ff3398d89bd81f5b719304a5
+Subproject 4c1e01e3e309282beb1af3b1eddb2c7f9a666b5
diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt
index a0b2e6207bf..38c3fc4389a 100644
--- a/tests/python/CMakeLists.txt
+++ b/tests/python/CMakeLists.txt
@@ -627,13 +627,13 @@ endif()
if(WITH_CYCLES OR WITH_OPENGL_RENDER_TESTS)
if(NOT OPENIMAGEIO_IDIFF)
- MESSAGE(STATUS "Disabling Cycles tests because OIIO idiff does not exist")
+ MESSAGE(WARNING "Disabling render tests because OIIO idiff does not exist")
elseif(NOT EXISTS "${TEST_SRC_DIR}/render/shader")
- MESSAGE(STATUS "Disabling Cycles tests because tests folder does not exist at ${TEST_SRC_DIR}")
+ MESSAGE(WARNING "Disabling render tests because tests folder does not exist at ${TEST_SRC_DIR}")
elseif(NOT WITH_COMPOSITOR)
- MESSAGE(STATUS "Disabling Cycles tests because WITH_COMPOSITOR is disabled")
+ MESSAGE(WARNING "Disabling render tests because WITH_COMPOSITOR is disabled")
elseif(NOT WITH_OPENCOLORIO)
- MESSAGE(STATUS "Disabling Cycles tests because WITH_OPENCOLORIO is disabled")
+ MESSAGE(WARNING "Disabling render tests because WITH_OPENCOLORIO is disabled")
else()
set(render_tests
camera