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:
Diffstat (limited to 'source/blender')
-rw-r--r--source/blender/blenfont/BLF_api.h2
-rw-r--r--source/blender/blenfont/intern/blf.c1
-rw-r--r--source/blender/blenfont/intern/blf_font.c859
-rw-r--r--source/blender/blenfont/intern/blf_glyph.c61
-rw-r--r--source/blender/blenfont/intern/blf_internal.h4
-rw-r--r--source/blender/blenfont/intern/blf_internal_types.h40
-rw-r--r--source/blender/blenkernel/BKE_blender_version.h2
-rw-r--r--source/blender/blenkernel/BKE_bvhutils.h2
-rw-r--r--source/blender/blenkernel/BKE_cachefile.h5
-rw-r--r--source/blender/blenkernel/BKE_collection.h9
-rw-r--r--source/blender/blenkernel/BKE_constraint.h22
-rw-r--r--source/blender/blenkernel/BKE_context.h2
-rw-r--r--source/blender/blenkernel/BKE_global.h4
-rw-r--r--source/blender/blenkernel/BKE_gpencil.h2
-rw-r--r--source/blender/blenkernel/BKE_gpencil_geom.h3
-rw-r--r--source/blender/blenkernel/BKE_image.h6
-rw-r--r--source/blender/blenkernel/BKE_key.h2
-rw-r--r--source/blender/blenkernel/BKE_lib_id.h4
-rw-r--r--source/blender/blenkernel/BKE_mesh.h49
-rw-r--r--source/blender/blenkernel/BKE_mesh_remesh_voxel.h4
-rw-r--r--source/blender/blenkernel/BKE_modifier.h6
-rw-r--r--source/blender/blenkernel/BKE_node.h30
-rw-r--r--source/blender/blenkernel/BKE_object.h5
-rw-r--r--source/blender/blenkernel/BKE_ocean.h6
-rw-r--r--source/blender/blenkernel/BKE_pointcache.h2
-rw-r--r--source/blender/blenkernel/BKE_scene.h4
-rw-r--r--source/blender/blenkernel/BKE_screen.h2
-rw-r--r--source/blender/blenkernel/BKE_sound.h9
-rw-r--r--source/blender/blenkernel/BKE_spline.hh3
-rw-r--r--source/blender/blenkernel/CMakeLists.txt7
-rw-r--r--source/blender/blenkernel/intern/DerivedMesh.cc36
-rw-r--r--source/blender/blenkernel/intern/action.c21
-rw-r--r--source/blender/blenkernel/intern/anim_sys.c6
-rw-r--r--source/blender/blenkernel/intern/armature.c35
-rw-r--r--source/blender/blenkernel/intern/armature_test.cc6
-rw-r--r--source/blender/blenkernel/intern/autoexec.c2
-rw-r--r--source/blender/blenkernel/intern/blendfile.c4
-rw-r--r--source/blender/blenkernel/intern/boids.c30
-rw-r--r--source/blender/blenkernel/intern/brush.c78
-rw-r--r--source/blender/blenkernel/intern/cachefile.c140
-rw-r--r--source/blender/blenkernel/intern/camera.c19
-rw-r--r--source/blender/blenkernel/intern/cloth.c17
-rw-r--r--source/blender/blenkernel/intern/collection.c31
-rw-r--r--source/blender/blenkernel/intern/colortools.c20
-rw-r--r--source/blender/blenkernel/intern/constraint.c165
-rw-r--r--source/blender/blenkernel/intern/context.c4
-rw-r--r--source/blender/blenkernel/intern/curve.c98
-rw-r--r--source/blender/blenkernel/intern/customdata.c7
-rw-r--r--source/blender/blenkernel/intern/data_transfer.c6
-rw-r--r--source/blender/blenkernel/intern/deform.c5
-rw-r--r--source/blender/blenkernel/intern/displist.cc2
-rw-r--r--source/blender/blenkernel/intern/dynamicpaint.c10
-rw-r--r--source/blender/blenkernel/intern/effect.c2
-rw-r--r--source/blender/blenkernel/intern/fluid.c32
-rw-r--r--source/blender/blenkernel/intern/font.c132
-rw-r--r--source/blender/blenkernel/intern/geometry_set_instances.cc4
-rw-r--r--source/blender/blenkernel/intern/gpencil.c78
-rw-r--r--source/blender/blenkernel/intern/gpencil_geom.cc255
-rw-r--r--source/blender/blenkernel/intern/hair.c49
-rw-r--r--source/blender/blenkernel/intern/icons.cc2
-rw-r--r--source/blender/blenkernel/intern/image.c146
-rw-r--r--source/blender/blenkernel/intern/image_gpu.c3
-rw-r--r--source/blender/blenkernel/intern/image_save.c19
-rw-r--r--source/blender/blenkernel/intern/key.c54
-rw-r--r--source/blender/blenkernel/intern/lattice.c33
-rw-r--r--source/blender/blenkernel/intern/layer.c84
-rw-r--r--source/blender/blenkernel/intern/lib_id.c14
-rw-r--r--source/blender/blenkernel/intern/lib_id_delete.c35
-rw-r--r--source/blender/blenkernel/intern/lib_override.c30
-rw-r--r--source/blender/blenkernel/intern/lib_query.c4
-rw-r--r--source/blender/blenkernel/intern/light.c31
-rw-r--r--source/blender/blenkernel/intern/lightprobe.c15
-rw-r--r--source/blender/blenkernel/intern/linestyle.c35
-rw-r--r--source/blender/blenkernel/intern/mask.c61
-rw-r--r--source/blender/blenkernel/intern/mask_rasterize.c10
-rw-r--r--source/blender/blenkernel/intern/material.c40
-rw-r--r--source/blender/blenkernel/intern/mball.c43
-rw-r--r--source/blender/blenkernel/intern/mesh.c515
-rw-r--r--source/blender/blenkernel/intern/mesh_convert.c9
-rw-r--r--source/blender/blenkernel/intern/mesh_mirror.c17
-rw-r--r--source/blender/blenkernel/intern/mesh_normals.cc574
-rw-r--r--source/blender/blenkernel/intern/mesh_remap.c12
-rw-r--r--source/blender/blenkernel/intern/mesh_remesh_voxel.cc134
-rw-r--r--source/blender/blenkernel/intern/modifier.c4
-rw-r--r--source/blender/blenkernel/intern/movieclip.c45
-rw-r--r--source/blender/blenkernel/intern/multires.c10
-rw-r--r--source/blender/blenkernel/intern/node.cc26
-rw-r--r--source/blender/blenkernel/intern/object.c169
-rw-r--r--source/blender/blenkernel/intern/object_deform.c10
-rw-r--r--source/blender/blenkernel/intern/object_dupli.cc6
-rw-r--r--source/blender/blenkernel/intern/ocean.c117
-rw-r--r--source/blender/blenkernel/intern/ocean_spectrum.c2
-rw-r--r--source/blender/blenkernel/intern/paint.c22
-rw-r--r--source/blender/blenkernel/intern/particle.c110
-rw-r--r--source/blender/blenkernel/intern/particle_system.c18
-rw-r--r--source/blender/blenkernel/intern/pbvh.c2
-rw-r--r--source/blender/blenkernel/intern/pointcache.c14
-rw-r--r--source/blender/blenkernel/intern/pointcloud.cc41
-rw-r--r--source/blender/blenkernel/intern/scene.c31
-rw-r--r--source/blender/blenkernel/intern/screen.c22
-rw-r--r--source/blender/blenkernel/intern/simulation.cc29
-rw-r--r--source/blender/blenkernel/intern/softbody.c18
-rw-r--r--source/blender/blenkernel/intern/sound.c48
-rw-r--r--source/blender/blenkernel/intern/speaker.c15
-rw-r--r--source/blender/blenkernel/intern/spline_base.cc16
-rw-r--r--source/blender/blenkernel/intern/studiolight.c24
-rw-r--r--source/blender/blenkernel/intern/text.c5
-rw-r--r--source/blender/blenkernel/intern/text_suggestions.c10
-rw-r--r--source/blender/blenkernel/intern/texture.c53
-rw-r--r--source/blender/blenkernel/intern/tracking_auto.c4
-rw-r--r--source/blender/blenkernel/intern/volume.cc31
-rw-r--r--source/blender/blenkernel/intern/volume_to_mesh.cc2
-rw-r--r--source/blender/blenkernel/intern/workspace.c2
-rw-r--r--source/blender/blenkernel/intern/world.c29
-rw-r--r--source/blender/blenkernel/intern/writeffmpeg.c44
-rw-r--r--source/blender/blenlib/BLI_array.h2
-rw-r--r--source/blender/blenlib/BLI_enumerable_thread_specific.hh2
-rw-r--r--source/blender/blenlib/BLI_fileops.h21
-rw-r--r--source/blender/blenlib/BLI_filereader.h81
-rw-r--r--source/blender/blenlib/BLI_index_mask.hh27
-rw-r--r--source/blender/blenlib/BLI_math_base.h3
-rw-r--r--source/blender/blenlib/BLI_math_geom.h4
-rw-r--r--source/blender/blenlib/BLI_math_vector.h1
-rw-r--r--source/blender/blenlib/BLI_range.h (renamed from source/blender/functions/FN_multi_function_network_optimization.hh)24
-rw-r--r--source/blender/blenlib/BLI_winstuff.h4
-rw-r--r--source/blender/blenlib/CMakeLists.txt8
-rw-r--r--source/blender/blenlib/intern/boxpack_2d.c4
-rw-r--r--source/blender/blenlib/intern/fileops.c243
-rw-r--r--source/blender/blenlib/intern/filereader_file.c80
-rw-r--r--source/blender/blenlib/intern/filereader_gzip.c108
-rw-r--r--source/blender/blenlib/intern/filereader_memory.c145
-rw-r--r--source/blender/blenlib/intern/filereader_zstd.c335
-rw-r--r--source/blender/blenlib/intern/freetypefont.c47
-rw-r--r--source/blender/blenlib/intern/math_base_inline.c7
-rw-r--r--source/blender/blenlib/intern/math_geom.c72
-rw-r--r--source/blender/blenlib/intern/math_vector_inline.c24
-rw-r--r--source/blender/blenlib/intern/path_util.c11
-rw-r--r--source/blender/blenlib/intern/polyfill_2d_beautify.c2
-rw-r--r--source/blender/blenlib/intern/scanfill.c22
-rw-r--r--source/blender/blenlib/intern/storage.c2
-rw-r--r--source/blender/blenlib/tests/BLI_array_store_test.cc11
-rw-r--r--source/blender/blenlib/tests/BLI_math_vector_test.cc18
-rw-r--r--source/blender/blenloader/BLO_readfile.h4
-rw-r--r--source/blender/blenloader/BLO_undofile.h14
-rw-r--r--source/blender/blenloader/CMakeLists.txt2
-rw-r--r--source/blender/blenloader/intern/readfile.c458
-rw-r--r--source/blender/blenloader/intern/readfile.h32
-rw-r--r--source/blender/blenloader/intern/undofile.c95
-rw-r--r--source/blender/blenloader/intern/versioning_250.c3
-rw-r--r--source/blender/blenloader/intern/versioning_260.c2
-rw-r--r--source/blender/blenloader/intern/versioning_280.c67
-rw-r--r--source/blender/blenloader/intern/versioning_290.c2
-rw-r--r--source/blender/blenloader/intern/versioning_300.c109
-rw-r--r--source/blender/blenloader/intern/versioning_common.cc37
-rw-r--r--source/blender/blenloader/intern/versioning_common.h6
-rw-r--r--source/blender/blenloader/intern/versioning_cycles.c37
-rw-r--r--source/blender/blenloader/intern/versioning_defaults.c58
-rw-r--r--source/blender/blenloader/intern/versioning_legacy.c5
-rw-r--r--source/blender/blenloader/intern/versioning_userdef.c7
-rw-r--r--source/blender/blenloader/intern/writefile.c322
-rw-r--r--source/blender/blentranslation/intern/blt_lang.c5
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_normals.c28
-rw-r--r--source/blender/bmesh/intern/bmesh_walkers_impl.c8
-rw-r--r--source/blender/bmesh/operators/bmo_removedoubles.c1
-rw-r--r--source/blender/bmesh/operators/bmo_smooth_laplacian.c336
-rw-r--r--source/blender/bmesh/tools/bmesh_bevel.c4
-rw-r--r--source/blender/bmesh/tools/bmesh_decimate_collapse.c4
-rw-r--r--source/blender/compositor/CMakeLists.txt13
-rw-r--r--source/blender/compositor/COM_defines.h6
-rw-r--r--source/blender/compositor/intern/COM_BufferOperation.cc13
-rw-r--r--source/blender/compositor/intern/COM_BufferOperation.h1
-rw-r--r--source/blender/compositor/intern/COM_ConstantFolder.cc4
-rw-r--r--source/blender/compositor/intern/COM_Debug.cc16
-rw-r--r--source/blender/compositor/intern/COM_MemoryBuffer.h5
-rw-r--r--source/blender/compositor/intern/COM_TiledExecutionModel.cc2
-rw-r--r--source/blender/compositor/nodes/COM_MaskNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_MovieClipNode.cc2
-rw-r--r--source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc30
-rw-r--r--source/blender/compositor/operations/COM_AlphaOverKeyOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc27
-rw-r--r--source/blender/compositor/operations/COM_AlphaOverMixedOperation.h2
-rw-r--r--source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc29
-rw-r--r--source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_BilateralBlurOperation.cc86
-rw-r--r--source/blender/compositor/operations/COM_BilateralBlurOperation.h11
-rw-r--r--source/blender/compositor/operations/COM_BokehImageOperation.cc25
-rw-r--r--source/blender/compositor/operations/COM_BokehImageOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_ChangeHSVOperation.cc23
-rw-r--r--source/blender/compositor/operations/COM_ChangeHSVOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_ColorCurveOperation.cc53
-rw-r--r--source/blender/compositor/operations/COM_ColorCurveOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_CompositorOperation.cc16
-rw-r--r--source/blender/compositor/operations/COM_CompositorOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_ConstantOperation.cc17
-rw-r--r--source/blender/compositor/operations/COM_ConstantOperation.h16
-rw-r--r--source/blender/compositor/operations/COM_CryptomatteOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_CurveBaseOperation.cc1
-rw-r--r--source/blender/compositor/operations/COM_CurveBaseOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_DilateErodeOperation.cc46
-rw-r--r--source/blender/compositor/operations/COM_GlareBaseOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc27
-rw-r--r--source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_InvertOperation.cc28
-rw-r--r--source/blender/compositor/operations/COM_InvertOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_MaskOperation.cc37
-rw-r--r--source/blender/compositor/operations/COM_MaskOperation.h11
-rw-r--r--source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc19
-rw-r--r--source/blender/compositor/operations/COM_MovieClipAttributeOperation.h9
-rw-r--r--source/blender/compositor/operations/COM_OutputFileOperation.cc35
-rw-r--r--source/blender/compositor/operations/COM_OutputFileOperation.h14
-rw-r--r--source/blender/compositor/operations/COM_SetColorOperation.cc10
-rw-r--r--source/blender/compositor/operations/COM_SetColorOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_SetValueOperation.cc10
-rw-r--r--source/blender/compositor/operations/COM_SetValueOperation.h3
-rw-r--r--source/blender/compositor/operations/COM_SplitOperation.cc13
-rw-r--r--source/blender/compositor/operations/COM_SplitOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_SunBeamsOperation.cc53
-rw-r--r--source/blender/compositor/operations/COM_SunBeamsOperation.h12
-rw-r--r--source/blender/compositor/operations/COM_TonemapOperation.cc124
-rw-r--r--source/blender/compositor/operations/COM_TonemapOperation.h16
-rw-r--r--source/blender/compositor/operations/COM_TrackPositionOperation.cc26
-rw-r--r--source/blender/compositor/operations/COM_TrackPositionOperation.h11
-rw-r--r--source/blender/compositor/operations/COM_VectorCurveOperation.cc10
-rw-r--r--source/blender/compositor/operations/COM_VectorCurveOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_WrapOperation.cc12
-rw-r--r--source/blender/compositor/operations/COM_ZCombineOperation.cc70
-rw-r--r--source/blender/compositor/operations/COM_ZCombineOperation.h20
-rw-r--r--source/blender/datatoc/datatoc.c9
-rw-r--r--source/blender/depsgraph/DEG_depsgraph.h2
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.cc6
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc6
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.cc8
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc6
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_rna.cc4
-rw-r--r--source/blender/depsgraph/intern/depsgraph_tag.cc4
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h10
-rw-r--r--source/blender/draw/CMakeLists.txt7
-rw-r--r--source/blender/draw/engines/eevee/eevee_lookdev.c2
-rw-r--r--source/blender/draw/engines/overlay/overlay_edit_mesh.c5
-rw-r--r--source/blender/draw/engines/overlay/overlay_engine.c28
-rw-r--r--source/blender/draw/engines/overlay/overlay_extra.c3
-rw-r--r--source/blender/draw/engines/overlay/overlay_paint.c27
-rw-r--r--source/blender/draw/engines/overlay/overlay_private.h2
-rw-r--r--source/blender/draw/engines/overlay/overlay_shader.c13
-rw-r--r--source/blender/draw/engines/overlay/shaders/edit_mesh_normal_vert.glsl19
-rw-r--r--source/blender/draw/engines/overlay/shaders/paint_weight_frag.glsl23
-rw-r--r--source/blender/draw/engines/overlay/shaders/paint_weight_vert.glsl15
-rw-r--r--source/blender/draw/engines/select/select_debug_engine.c135
-rw-r--r--source/blender/draw/engines/select/select_engine.c2
-rw-r--r--source/blender/draw/engines/select/select_engine.h6
-rw-r--r--source/blender/draw/engines/workbench/workbench_private.h2
-rw-r--r--source/blender/draw/intern/draw_cache_impl_gpencil.c3
-rw-r--r--source/blender/draw/intern/draw_cache_impl_mesh.c12
-rw-r--r--source/blender/draw/intern/draw_manager.c12
-rw-r--r--source/blender/draw/intern/draw_manager_data.c7
-rw-r--r--source/blender/draw/intern/draw_manager_exec.c4
-rw-r--r--source/blender/draw/intern/draw_manager_texture.c24
-rw-r--r--source/blender/draw/intern/draw_view.c13
-rw-r--r--source/blender/draw/tests/shaders_test.cc3
-rw-r--r--source/blender/editors/animation/CMakeLists.txt2
-rw-r--r--source/blender/editors/animation/anim_draw.c24
-rw-r--r--source/blender/editors/animation/anim_filter.c4
-rw-r--r--source/blender/editors/animation/anim_ipo_utils.c22
-rw-r--r--source/blender/editors/animation/anim_markers.c2
-rw-r--r--source/blender/editors/animation/anim_motion_paths.c64
-rw-r--r--source/blender/editors/animation/fmodifier_ui.c5
-rw-r--r--source/blender/editors/animation/keyframes_draw.c765
-rw-r--r--source/blender/editors/animation/keyframes_general.c10
-rw-r--r--source/blender/editors/animation/keyframes_keylist.cc (renamed from source/blender/editors/animation/keyframes_keylist.c)394
-rw-r--r--source/blender/editors/animation/time_scrub_ui.c4
-rw-r--r--source/blender/editors/armature/meshlaplacian.c5
-rw-r--r--source/blender/editors/armature/pose_lib.c10
-rw-r--r--source/blender/editors/armature/pose_lib_2.c12
-rw-r--r--source/blender/editors/armature/pose_slide.c110
-rw-r--r--source/blender/editors/armature/pose_transform.c4
-rw-r--r--source/blender/editors/asset/ED_asset_handle.h2
-rw-r--r--source/blender/editors/asset/ED_asset_temp_id_consumer.h2
-rw-r--r--source/blender/editors/asset/intern/asset_handle.cc4
-rw-r--r--source/blender/editors/asset/intern/asset_ops.cc8
-rw-r--r--source/blender/editors/asset/intern/asset_temp_id_consumer.cc10
-rw-r--r--source/blender/editors/curve/editcurve.c10
-rw-r--r--source/blender/editors/curve/editcurve_add.c4
-rw-r--r--source/blender/editors/curve/editfont.c30
-rw-r--r--source/blender/editors/datafiles/CMakeLists.txt3
-rw-r--r--source/blender/editors/gpencil/annotate_paint.c85
-rw-r--r--source/blender/editors/gpencil/gpencil_convert.c35
-rw-r--r--source/blender/editors/gpencil/gpencil_fill.c3
-rw-r--r--source/blender/editors/gpencil/gpencil_intern.h1
-rw-r--r--source/blender/editors/gpencil/gpencil_interpolate.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_mesh.c3
-rw-r--r--source/blender/editors/gpencil/gpencil_ops.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_paint.c57
-rw-r--r--source/blender/editors/gpencil/gpencil_primitive.c12
-rw-r--r--source/blender/editors/gpencil/gpencil_sculpt_paint.c5
-rw-r--r--source/blender/editors/gpencil/gpencil_select.c235
-rw-r--r--source/blender/editors/gpencil/gpencil_undo.c4
-rw-r--r--source/blender/editors/gpencil/gpencil_utils.c2
-rw-r--r--source/blender/editors/include/ED_keyframes_draw.h41
-rw-r--r--source/blender/editors/include/ED_keyframes_keylist.h55
-rw-r--r--source/blender/editors/include/ED_object.h4
-rw-r--r--source/blender/editors/include/UI_icons.h6
-rw-r--r--source/blender/editors/include/UI_interface.h5
-rw-r--r--source/blender/editors/interface/interface.c14
-rw-r--r--source/blender/editors/interface/interface_align.c2
-rw-r--r--source/blender/editors/interface/interface_context_menu.c10
-rw-r--r--source/blender/editors/interface/interface_draw.c4
-rw-r--r--source/blender/editors/interface/interface_eyedropper_driver.c5
-rw-r--r--source/blender/editors/interface/interface_handlers.c29
-rw-r--r--source/blender/editors/interface/interface_icons.c20
-rw-r--r--source/blender/editors/interface/interface_intern.h2
-rw-r--r--source/blender/editors/interface/interface_layout.c2
-rw-r--r--source/blender/editors/interface/interface_ops.c109
-rw-r--r--source/blender/editors/interface/interface_panel.c8
-rw-r--r--source/blender/editors/interface/interface_query.c7
-rw-r--r--source/blender/editors/interface/interface_region_tooltip.c3
-rw-r--r--source/blender/editors/interface/interface_style.c45
-rw-r--r--source/blender/editors/interface/interface_template_asset_view.cc24
-rw-r--r--source/blender/editors/interface/interface_templates.c131
-rw-r--r--source/blender/editors/interface/interface_widgets.c53
-rw-r--r--source/blender/editors/io/io_alembic.c10
-rw-r--r--source/blender/editors/io/io_ops.c1
-rw-r--r--source/blender/editors/io/io_usd.c279
-rw-r--r--source/blender/editors/io/io_usd.h2
-rw-r--r--source/blender/editors/mask/mask_add.c2
-rw-r--r--source/blender/editors/mask/mask_draw.c8
-rw-r--r--source/blender/editors/mask/mask_ops.c24
-rw-r--r--source/blender/editors/mask/mask_query.c8
-rw-r--r--source/blender/editors/mask/mask_relationships.c4
-rw-r--r--source/blender/editors/mask/mask_select.c24
-rw-r--r--source/blender/editors/mask/mask_shapekey.c4
-rw-r--r--source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c4
-rw-r--r--source/blender/editors/mesh/editmesh_knife.c41
-rw-r--r--source/blender/editors/mesh/editmesh_knife_project.c46
-rw-r--r--source/blender/editors/mesh/editmesh_preselect_edgering.c28
-rw-r--r--source/blender/editors/mesh/editmesh_select.c28
-rw-r--r--source/blender/editors/mesh/editmesh_tools.c262
-rw-r--r--source/blender/editors/mesh/editmesh_undo.c4
-rw-r--r--source/blender/editors/mesh/editmesh_utils.c5
-rw-r--r--source/blender/editors/mesh/mesh_data.c11
-rw-r--r--source/blender/editors/mesh/mesh_intern.h5
-rw-r--r--source/blender/editors/mesh/mesh_mirror.c5
-rw-r--r--source/blender/editors/object/object_add.c5
-rw-r--r--source/blender/editors/object/object_bake_api.c10
-rw-r--r--source/blender/editors/object/object_constraint.c331
-rw-r--r--source/blender/editors/object/object_edit.c6
-rw-r--r--source/blender/editors/object/object_intern.h3
-rw-r--r--source/blender/editors/object/object_modes.c2
-rw-r--r--source/blender/editors/object/object_ops.c3
-rw-r--r--source/blender/editors/object/object_relations.c41
-rw-r--r--source/blender/editors/object/object_remesh.cc3
-rw-r--r--source/blender/editors/object/object_vgroup.c2
-rw-r--r--source/blender/editors/physics/particle_edit.c17
-rw-r--r--source/blender/editors/physics/particle_edit_undo.c5
-rw-r--r--source/blender/editors/render/render_opengl.c2
-rw-r--r--source/blender/editors/render/render_preview.c96
-rw-r--r--source/blender/editors/render/render_update.c34
-rw-r--r--source/blender/editors/screen/area.c10
-rw-r--r--source/blender/editors/screen/screen_context.c6
-rw-r--r--source/blender/editors/screen/screen_draw.c2
-rw-r--r--source/blender/editors/screen/screen_edit.c12
-rw-r--r--source/blender/editors/screen/screen_ops.c79
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_2d.c10
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_proj.c6
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c8
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_dyntopo.c10
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_smooth.c2
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_uv.c4
-rw-r--r--source/blender/editors/space_action/action_data.c14
-rw-r--r--source/blender/editors/space_action/action_draw.c40
-rw-r--r--source/blender/editors/space_action/action_select.c53
-rw-r--r--source/blender/editors/space_buttons/space_buttons.c3
-rw-r--r--source/blender/editors/space_clip/clip_editor.c1
-rw-r--r--source/blender/editors/space_clip/clip_utils.c2
-rw-r--r--source/blender/editors/space_clip/space_clip.c7
-rw-r--r--source/blender/editors/space_clip/tracking_ops_solve.c5
-rw-r--r--source/blender/editors/space_console/space_console.c14
-rw-r--r--source/blender/editors/space_file/file_draw.c5
-rw-r--r--source/blender/editors/space_file/file_ops.c67
-rw-r--r--source/blender/editors/space_file/filelist.c149
-rw-r--r--source/blender/editors/space_file/filelist.h2
-rw-r--r--source/blender/editors/space_file/filesel.c20
-rw-r--r--source/blender/editors/space_file/fsmenu.c2
-rw-r--r--source/blender/editors/space_file/space_file.c46
-rw-r--r--source/blender/editors/space_graph/graph_buttons.c2
-rw-r--r--source/blender/editors/space_image/image_ops.c42
-rw-r--r--source/blender/editors/space_image/image_sequence.c34
-rw-r--r--source/blender/editors/space_image/space_image.c7
-rw-r--r--source/blender/editors/space_info/info_stats.c10
-rw-r--r--source/blender/editors/space_nla/nla_channels.c3
-rw-r--r--source/blender/editors/space_nla/nla_draw.c45
-rw-r--r--source/blender/editors/space_nla/nla_edit.c4
-rw-r--r--source/blender/editors/space_node/drawnode.cc22
-rw-r--r--source/blender/editors/space_node/node_select.cc3
-rw-r--r--source/blender/editors/space_node/space_node.c46
-rw-r--r--source/blender/editors/space_outliner/outliner_collections.c10
-rw-r--r--source/blender/editors/space_outliner/outliner_dragdrop.c165
-rw-r--r--source/blender/editors/space_outliner/outliner_edit.c2
-rw-r--r--source/blender/editors/space_outliner/outliner_select.c3
-rw-r--r--source/blender/editors/space_outliner/outliner_tools.c91
-rw-r--r--source/blender/editors/space_sequencer/sequencer_add.c35
-rw-r--r--source/blender/editors/space_sequencer/sequencer_draw.c331
-rw-r--r--source/blender/editors/space_sequencer/sequencer_edit.c2
-rw-r--r--source/blender/editors/space_sequencer/sequencer_proxy.c4
-rw-r--r--source/blender/editors/space_sequencer/sequencer_select.c8
-rw-r--r--source/blender/editors/space_sequencer/sequencer_view.c3
-rw-r--r--source/blender/editors/space_sequencer/space_sequencer.c26
-rw-r--r--source/blender/editors/space_text/space_text.c14
-rw-r--r--source/blender/editors/space_text/text_draw.c5
-rw-r--r--source/blender/editors/space_text/text_ops.c5
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c89
-rw-r--r--source/blender/editors/space_view3d/view3d_buttons.c10
-rw-r--r--source/blender/editors/space_view3d/view3d_draw.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_edit.c5
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_ruler.c4
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_walk.c16
-rw-r--r--source/blender/editors/space_view3d/view3d_project.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_select.c26
-rw-r--r--source/blender/editors/transform/CMakeLists.txt1
-rw-r--r--source/blender/editors/transform/transform.c5
-rw-r--r--source/blender/editors/transform/transform.h3
-rw-r--r--source/blender/editors/transform/transform_convert.c36
-rw-r--r--source/blender/editors/transform/transform_convert.h4
-rw-r--r--source/blender/editors/transform/transform_convert_action.c72
-rw-r--r--source/blender/editors/transform/transform_convert_armature.c6
-rw-r--r--source/blender/editors/transform/transform_convert_graph.c62
-rw-r--r--source/blender/editors/transform/transform_convert_mask.c4
-rw-r--r--source/blender/editors/transform/transform_convert_nla.c73
-rw-r--r--source/blender/editors/transform/transform_convert_object.c2
-rw-r--r--source/blender/editors/transform/transform_generics.c4
-rw-r--r--source/blender/editors/transform/transform_mode.c100
-rw-r--r--source/blender/editors/transform/transform_mode.h3
-rw-r--r--source/blender/editors/transform/transform_mode_align.c2
-rw-r--r--source/blender/editors/transform/transform_mode_baketime.c4
-rw-r--r--source/blender/editors/transform/transform_mode_bbone_resize.c4
-rw-r--r--source/blender/editors/transform/transform_mode_bend.c4
-rw-r--r--source/blender/editors/transform/transform_mode_boneenvelope.c4
-rw-r--r--source/blender/editors/transform/transform_mode_boneroll.c4
-rw-r--r--source/blender/editors/transform/transform_mode_curveshrinkfatten.c4
-rw-r--r--source/blender/editors/transform/transform_mode_edge_bevelweight.c4
-rw-r--r--source/blender/editors/transform/transform_mode_edge_crease.c4
-rw-r--r--source/blender/editors/transform/transform_mode_edge_rotate_normal.c3
-rw-r--r--source/blender/editors/transform/transform_mode_edge_slide.c4
-rw-r--r--source/blender/editors/transform/transform_mode_gpopacity.c4
-rw-r--r--source/blender/editors/transform/transform_mode_gpshrinkfatten.c4
-rw-r--r--source/blender/editors/transform/transform_mode_maskshrinkfatten.c4
-rw-r--r--source/blender/editors/transform/transform_mode_mirror.c2
-rw-r--r--source/blender/editors/transform/transform_mode_push_pull.c4
-rw-r--r--source/blender/editors/transform/transform_mode_rotate.c4
-rw-r--r--source/blender/editors/transform/transform_mode_shear.c4
-rw-r--r--source/blender/editors/transform/transform_mode_shrink_fatten.c4
-rw-r--r--source/blender/editors/transform/transform_mode_skin_resize.c4
-rw-r--r--source/blender/editors/transform/transform_mode_tilt.c4
-rw-r--r--source/blender/editors/transform/transform_mode_timescale.c7
-rw-r--r--source/blender/editors/transform/transform_mode_timeslide.c2
-rw-r--r--source/blender/editors/transform/transform_mode_timetranslate.c37
-rw-r--r--source/blender/editors/transform/transform_mode_tosphere.c4
-rw-r--r--source/blender/editors/transform/transform_mode_trackball.c4
-rw-r--r--source/blender/editors/transform/transform_mode_translate.c20
-rw-r--r--source/blender/editors/transform/transform_ops.c35
-rw-r--r--source/blender/editors/transform/transform_snap.c83
-rw-r--r--source/blender/editors/transform/transform_snap.h18
-rw-r--r--source/blender/editors/transform/transform_snap_animation.c159
-rw-r--r--source/blender/editors/transform/transform_snap_sequencer.c2
-rw-r--r--source/blender/editors/util/CMakeLists.txt1
-rw-r--r--source/blender/editors/util/numinput.c2
-rw-r--r--source/blender/editors/uvedit/uvedit_parametrizer.c5
-rw-r--r--source/blender/editors/uvedit/uvedit_select.c57
-rw-r--r--source/blender/editors/uvedit/uvedit_smart_stitch.c27
-rw-r--r--source/blender/freestyle/intern/python/BPy_Freestyle.cpp2
-rw-r--r--source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp10
-rw-r--r--source/blender/functions/CMakeLists.txt7
-rw-r--r--source/blender/functions/FN_cpp_type.hh178
-rw-r--r--source/blender/functions/FN_cpp_type_make.hh96
-rw-r--r--source/blender/functions/FN_generic_vector_array.hh2
-rw-r--r--source/blender/functions/FN_generic_virtual_array.hh2
-rw-r--r--source/blender/functions/FN_multi_function_network.hh536
-rw-r--r--source/blender/functions/FN_multi_function_network_evaluation.hh62
-rw-r--r--source/blender/functions/FN_multi_function_params.hh11
-rw-r--r--source/blender/functions/FN_multi_function_signature.hh15
-rw-r--r--source/blender/functions/intern/generic_vector_array.cc9
-rw-r--r--source/blender/functions/intern/multi_function_network.cc330
-rw-r--r--source/blender/functions/intern/multi_function_network_evaluation.cc1083
-rw-r--r--source/blender/functions/intern/multi_function_network_optimization.cc501
-rw-r--r--source/blender/functions/tests/FN_multi_function_network_test.cc280
-rw-r--r--source/blender/functions/tests/FN_multi_function_test.cc92
-rw-r--r--source/blender/functions/tests/FN_multi_function_test_common.hh174
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencillength.c7
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c5
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h2
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c8
-rw-r--r--source/blender/gpu/GPU_framebuffer.h2
-rw-r--r--source/blender/gpu/GPU_matrix.h6
-rw-r--r--source/blender/gpu/GPU_texture.h21
-rw-r--r--source/blender/gpu/intern/gpu_framebuffer.cc5
-rw-r--r--source/blender/gpu/intern/gpu_immediate.cc12
-rw-r--r--source/blender/gpu/intern/gpu_material.c1
-rw-r--r--source/blender/gpu/intern/gpu_matrix.cc122
-rw-r--r--source/blender/gpu/intern/gpu_texture.cc36
-rw-r--r--source/blender/gpu/opengl/gl_backend.cc3
-rw-r--r--source/blender/gpu/shaders/gpu_shader_3D_point_uniform_size_aa_vert.glsl6
-rw-r--r--source/blender/gpu/tests/gpu_shader_test.cc3
-rw-r--r--source/blender/imbuf/IMB_imbuf.h5
-rw-r--r--source/blender/imbuf/intern/IMB_anim.h1
-rw-r--r--source/blender/imbuf/intern/anim_movie.c159
-rw-r--r--source/blender/imbuf/intern/colormanagement.c6
-rw-r--r--source/blender/imbuf/intern/dds/ColorBlock.h2
-rw-r--r--source/blender/imbuf/intern/indexer.c2
-rw-r--r--source/blender/imbuf/intern/metadata.c2
-rw-r--r--source/blender/imbuf/intern/moviecache.c16
-rw-r--r--source/blender/imbuf/intern/openexr/openexr_api.cpp4
-rw-r--r--source/blender/io/alembic/ABC_alembic.h18
-rw-r--r--source/blender/io/alembic/intern/abc_reader_curves.cc2
-rw-r--r--source/blender/io/alembic/intern/abc_reader_mesh.cc4
-rw-r--r--source/blender/io/alembic/intern/abc_reader_nurbs.cc4
-rw-r--r--source/blender/io/alembic/intern/abc_reader_object.cc4
-rw-r--r--source/blender/io/alembic/intern/abc_reader_object.h2
-rw-r--r--source/blender/io/alembic/intern/abc_reader_points.cc2
-rw-r--r--source/blender/io/alembic/intern/abc_util.h9
-rw-r--r--source/blender/io/alembic/intern/alembic_capi.cc37
-rw-r--r--source/blender/io/avi/intern/avi_endian.c2
-rw-r--r--source/blender/io/collada/AnimationImporter.cpp2
-rw-r--r--source/blender/io/collada/SceneExporter.cpp2
-rw-r--r--source/blender/io/collada/collada_internal.cpp2
-rw-r--r--source/blender/io/common/CMakeLists.txt1
-rw-r--r--source/blender/io/common/IO_types.h34
-rw-r--r--source/blender/io/gpencil/CMakeLists.txt4
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_import_svg.cc2
-rw-r--r--source/blender/io/gpencil/nanosvg/nanosvg.h3327
-rw-r--r--source/blender/io/usd/CMakeLists.txt30
-rw-r--r--source/blender/io/usd/intern/usd_capi_export.cc (renamed from source/blender/io/usd/intern/usd_capi.cc)18
-rw-r--r--source/blender/io/usd/intern/usd_capi_import.cc577
-rw-r--r--source/blender/io/usd/intern/usd_common.cc43
-rw-r--r--source/blender/io/usd/intern/usd_common.h (renamed from source/blender/nodes/NOD_type_callbacks.hh)23
-rw-r--r--source/blender/io/usd/intern/usd_reader_camera.cc100
-rw-r--r--source/blender/io/usd/intern/usd_reader_camera.h42
-rw-r--r--source/blender/io/usd/intern/usd_reader_curve.cc256
-rw-r--r--source/blender/io/usd/intern/usd_reader_curve.h62
-rw-r--r--source/blender/io/usd/intern/usd_reader_geom.cc59
-rw-r--r--source/blender/io/usd/intern/usd_reader_geom.h52
-rw-r--r--source/blender/io/usd/intern/usd_reader_instance.cc64
-rw-r--r--source/blender/io/usd/intern/usd_reader_instance.h47
-rw-r--r--source/blender/io/usd/intern/usd_reader_light.cc252
-rw-r--r--source/blender/io/usd/intern/usd_reader_light.h41
-rw-r--r--source/blender/io/usd/intern/usd_reader_material.cc703
-rw-r--r--source/blender/io/usd/intern/usd_reader_material.h132
-rw-r--r--source/blender/io/usd/intern/usd_reader_mesh.cc853
-rw-r--r--source/blender/io/usd/intern/usd_reader_mesh.h95
-rw-r--r--source/blender/io/usd/intern/usd_reader_nurbs.cc256
-rw-r--r--source/blender/io/usd/intern/usd_reader_nurbs.h61
-rw-r--r--source/blender/io/usd/intern/usd_reader_prim.cc80
-rw-r--r--source/blender/io/usd/intern/usd_reader_prim.h131
-rw-r--r--source/blender/io/usd/intern/usd_reader_stage.cc324
-rw-r--r--source/blender/io/usd/intern/usd_reader_stage.h90
-rw-r--r--source/blender/io/usd/intern/usd_reader_volume.cc114
-rw-r--r--source/blender/io/usd/intern/usd_reader_volume.h49
-rw-r--r--source/blender/io/usd/intern/usd_reader_xform.cc184
-rw-r--r--source/blender/io/usd/intern/usd_reader_xform.h68
-rw-r--r--source/blender/io/usd/usd.h69
-rw-r--r--source/blender/makesdna/DNA_ID.h26
-rw-r--r--source/blender/makesdna/DNA_asset_types.h2
-rw-r--r--source/blender/makesdna/DNA_brush_types.h4
-rw-r--r--source/blender/makesdna/DNA_cachefile_defaults.h3
-rw-r--r--source/blender/makesdna/DNA_cachefile_types.h43
-rw-r--r--source/blender/makesdna/DNA_collection_types.h6
-rw-r--r--source/blender/makesdna/DNA_constraint_types.h6
-rw-r--r--source/blender/makesdna/DNA_gpencil_types.h1
-rw-r--r--source/blender/makesdna/DNA_layer_types.h2
-rw-r--r--source/blender/makesdna/DNA_lineart_types.h8
-rw-r--r--source/blender/makesdna/DNA_mask_types.h10
-rw-r--r--source/blender/makesdna/DNA_mesh_types.h1
-rw-r--r--source/blender/makesdna/DNA_modifier_types.h25
-rw-r--r--source/blender/makesdna/DNA_node_types.h25
-rw-r--r--source/blender/makesdna/DNA_object_types.h27
-rw-r--r--source/blender/makesdna/DNA_sequence_types.h1
-rw-r--r--source/blender/makesdna/DNA_sound_types.h1
-rw-r--r--source/blender/makesdna/DNA_space_types.h10
-rw-r--r--source/blender/makesdna/DNA_userdef_types.h5
-rw-r--r--source/blender/makesdna/DNA_view3d_defaults.h1
-rw-r--r--source/blender/makesdna/DNA_view3d_types.h4
-rw-r--r--source/blender/makesdna/DNA_workspace_types.h2
-rw-r--r--source/blender/makesdna/DNA_xr_types.h118
-rw-r--r--source/blender/makesdna/intern/dna_rename_defs.h2
-rw-r--r--source/blender/makesdna/intern/makesdna.c69
-rw-r--r--source/blender/makesrna/RNA_access.h2
-rw-r--r--source/blender/makesrna/RNA_enum_items.h240
-rw-r--r--source/blender/makesrna/RNA_enum_types.h212
-rw-r--r--source/blender/makesrna/intern/CMakeLists.txt1
-rw-r--r--source/blender/makesrna/intern/makesrna.c93
-rw-r--r--source/blender/makesrna/intern/rna_ID.c127
-rw-r--r--source/blender/makesrna/intern/rna_access.c48
-rw-r--r--source/blender/makesrna/intern/rna_access_compare_override.c16
-rw-r--r--source/blender/makesrna/intern/rna_armature.c8
-rw-r--r--source/blender/makesrna/intern/rna_asset.c6
-rw-r--r--source/blender/makesrna/intern/rna_brush.c12
-rw-r--r--source/blender/makesrna/intern/rna_cachefile.c46
-rw-r--r--source/blender/makesrna/intern/rna_collection.c12
-rw-r--r--source/blender/makesrna/intern/rna_constraint.c56
-rw-r--r--source/blender/makesrna/intern/rna_curveprofile.c27
-rw-r--r--source/blender/makesrna/intern/rna_fcurve.c2
-rw-r--r--source/blender/makesrna/intern/rna_gpencil.c2
-rw-r--r--source/blender/makesrna/intern/rna_image.c9
-rw-r--r--source/blender/makesrna/intern/rna_layer.c2
-rw-r--r--source/blender/makesrna/intern/rna_light.c1
-rw-r--r--source/blender/makesrna/intern/rna_linestyle.c1
-rw-r--r--source/blender/makesrna/intern/rna_mask.c6
-rw-r--r--source/blender/makesrna/intern/rna_material.c3
-rw-r--r--source/blender/makesrna/intern/rna_mesh.c2
-rw-r--r--source/blender/makesrna/intern/rna_modifier.c85
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c140
-rw-r--r--source/blender/makesrna/intern/rna_object.c131
-rw-r--r--source/blender/makesrna/intern/rna_particle.c10
-rw-r--r--source/blender/makesrna/intern/rna_render.c6
-rw-r--r--source/blender/makesrna/intern/rna_scene.c12
-rw-r--r--source/blender/makesrna/intern/rna_sequencer.c8
-rw-r--r--source/blender/makesrna/intern/rna_sequencer_api.c5
-rw-r--r--source/blender/makesrna/intern/rna_simulation.c1
-rw-r--r--source/blender/makesrna/intern/rna_space.c46
-rw-r--r--source/blender/makesrna/intern/rna_speaker.c70
-rw-r--r--source/blender/makesrna/intern/rna_text.c3
-rw-r--r--source/blender/makesrna/intern/rna_texture.c1
-rw-r--r--source/blender/makesrna/intern/rna_texture_api.c28
-rw-r--r--source/blender/makesrna/intern/rna_ui.c4
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c26
-rw-r--r--source/blender/makesrna/intern/rna_wm.c28
-rw-r--r--source/blender/makesrna/intern/rna_wm_api.c2
-rw-r--r--source/blender/makesrna/intern/rna_wm_gizmo.c22
-rw-r--r--source/blender/makesrna/intern/rna_workspace.c4
-rw-r--r--source/blender/makesrna/intern/rna_world.c3
-rw-r--r--source/blender/makesrna/intern/rna_xr.c1529
-rw-r--r--source/blender/modifiers/CMakeLists.txt10
-rw-r--r--source/blender/modifiers/intern/MOD_build.c4
-rw-r--r--source/blender/modifiers/intern/MOD_cloth.c4
-rw-r--r--source/blender/modifiers/intern/MOD_collision.c4
-rw-r--r--source/blender/modifiers/intern/MOD_displace.c4
-rw-r--r--source/blender/modifiers/intern/MOD_dynamicpaint.c4
-rw-r--r--source/blender/modifiers/intern/MOD_explode.c4
-rw-r--r--source/blender/modifiers/intern/MOD_fluid.c4
-rw-r--r--source/blender/modifiers/intern/MOD_laplaciansmooth.c24
-rw-r--r--source/blender/modifiers/intern/MOD_meshcache.c4
-rw-r--r--source/blender/modifiers/intern/MOD_meshsequencecache.c120
-rw-r--r--source/blender/modifiers/intern/MOD_nodes.cc13
-rw-r--r--source/blender/modifiers/intern/MOD_nodes_evaluator.cc61
-rw-r--r--source/blender/modifiers/intern/MOD_nodes_evaluator.hh8
-rw-r--r--source/blender/modifiers/intern/MOD_normal_edit.c16
-rw-r--r--source/blender/modifiers/intern/MOD_ocean.c14
-rw-r--r--source/blender/modifiers/intern/MOD_softbody.c4
-rw-r--r--source/blender/modifiers/intern/MOD_solidify_extrude.c53
-rw-r--r--source/blender/modifiers/intern/MOD_solidify_nonmanifold.c24
-rw-r--r--source/blender/modifiers/intern/MOD_subsurf.c9
-rw-r--r--source/blender/modifiers/intern/MOD_surface.c4
-rw-r--r--source/blender/modifiers/intern/MOD_volume_displace.cc4
-rw-r--r--source/blender/modifiers/intern/MOD_warp.c4
-rw-r--r--source/blender/modifiers/intern/MOD_wave.c4
-rw-r--r--source/blender/modifiers/intern/MOD_weighted_normal.c4
-rw-r--r--source/blender/modifiers/intern/MOD_weightvgedit.c4
-rw-r--r--source/blender/modifiers/intern/MOD_weightvgmix.c4
-rw-r--r--source/blender/modifiers/intern/MOD_weightvgproximity.c4
-rw-r--r--source/blender/nodes/CMakeLists.txt8
-rw-r--r--source/blender/nodes/NOD_geometry.h2
-rw-r--r--source/blender/nodes/NOD_multi_function.hh130
-rw-r--r--source/blender/nodes/NOD_node_tree_multi_function.hh390
-rw-r--r--source/blender/nodes/NOD_static_types.h6
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_antialiasing.c6
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_switchview.c2
-rw-r--r--source/blender/nodes/function/node_function_util.hh2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_boolean_math.cc19
-rw-r--r--source/blender/nodes/function/nodes/node_fn_float_compare.cc25
-rw-r--r--source/blender/nodes/function/nodes/node_fn_float_to_int.cc19
-rw-r--r--source/blender/nodes/function/nodes/node_fn_input_string.cc12
-rw-r--r--source/blender/nodes/function/nodes/node_fn_input_vector.cc10
-rw-r--r--source/blender/nodes/function/nodes/node_fn_random_float.cc9
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc147
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc307
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc11
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc12
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc1
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc20
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc20
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_raycast.cc152
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc47
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc3
-rw-r--r--source/blender/nodes/intern/node_geometry_exec.cc5
-rw-r--r--source/blender/nodes/intern/node_multi_function.cc40
-rw-r--r--source/blender/nodes/intern/node_socket.cc69
-rw-r--r--source/blender/nodes/intern/node_tree_multi_function.cc409
-rw-r--r--source/blender/nodes/intern/type_callbacks.cc77
-rw-r--r--source/blender/nodes/shader/node_shader_tree.c1
-rw-r--r--source/blender/nodes/shader/node_shader_util.h2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_clamp.cc6
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_curves.cc14
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_eevee_specular.c2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_map_range.cc8
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_math.cc67
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_mixRgb.cc58
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc8
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc8
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_valToRgb.cc7
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_value.cc6
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vector_math.cc28
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc37
-rw-r--r--source/blender/python/bmesh/bmesh_py_types.c2
-rw-r--r--source/blender/python/bmesh/bmesh_py_types_customdata.c2
-rw-r--r--source/blender/python/bmesh/bmesh_py_types_meshdata.c6
-rw-r--r--source/blender/python/bmesh/bmesh_py_types_select.c21
-rw-r--r--source/blender/python/generic/blf_py_api.c1
-rw-r--r--source/blender/python/generic/py_capi_utils.h4
-rw-r--r--source/blender/python/gpu/gpu_py_buffer.c6
-rw-r--r--source/blender/python/gpu/gpu_py_capabilities.c162
-rw-r--r--source/blender/python/gpu/gpu_py_offscreen.c29
-rw-r--r--source/blender/python/gpu/gpu_py_platform.c36
-rw-r--r--source/blender/python/gpu/gpu_py_shader.c45
-rw-r--r--source/blender/python/intern/bpy.c3
-rw-r--r--source/blender/python/intern/bpy_app_icons.c6
-rw-r--r--source/blender/python/intern/bpy_app_translations.c2
-rw-r--r--source/blender/python/intern/bpy_gizmo_wrap.c2
-rw-r--r--source/blender/python/intern/bpy_interface.c11
-rw-r--r--source/blender/python/intern/bpy_rna.c67
-rw-r--r--source/blender/python/intern/bpy_rna.h1
-rw-r--r--source/blender/python/intern/bpy_rna_array.c2
-rw-r--r--source/blender/python/intern/bpy_rna_id_collection.c2
-rw-r--r--source/blender/python/intern/bpy_rna_operator.c2
-rw-r--r--source/blender/python/intern/bpy_utils_units.c2
-rw-r--r--source/blender/python/mathutils/mathutils.c4
-rw-r--r--source/blender/python/mathutils/mathutils_Vector.c4
-rw-r--r--source/blender/python/mathutils/mathutils_geometry.c2
-rw-r--r--source/blender/render/RE_engine.h7
-rw-r--r--source/blender/render/intern/engine.c13
-rw-r--r--source/blender/render/intern/pipeline.c2
-rw-r--r--source/blender/render/intern/render_result.c2
-rw-r--r--source/blender/render/intern/texture_pointdensity.c5
-rw-r--r--source/blender/render/intern/zbuf.c9
-rw-r--r--source/blender/sequencer/SEQ_add.h6
-rw-r--r--source/blender/sequencer/intern/effects.c40
-rw-r--r--source/blender/sequencer/intern/image_cache.c32
-rw-r--r--source/blender/sequencer/intern/sound.c7
-rw-r--r--source/blender/sequencer/intern/strip_add.c50
-rw-r--r--source/blender/sequencer/intern/strip_time.c4
-rw-r--r--source/blender/windowmanager/CMakeLists.txt7
-rw-r--r--source/blender/windowmanager/WM_api.h103
-rw-r--r--source/blender/windowmanager/WM_types.h27
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c8
-rw-r--r--source/blender/windowmanager/intern/wm.c1
-rw-r--r--source/blender/windowmanager/intern/wm_dragdrop.c87
-rw-r--r--source/blender/windowmanager/intern/wm_draw.c160
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c7
-rw-r--r--source/blender/windowmanager/intern/wm_files.c264
-rw-r--r--source/blender/windowmanager/intern/wm_files_link.c83
-rw-r--r--source/blender/windowmanager/intern/wm_gesture_ops.c2
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c124
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c17
-rw-r--r--source/blender/windowmanager/intern/wm_window.c4
-rw-r--r--source/blender/windowmanager/wm_files.h47
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr.c2
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_action.c (renamed from source/blender/windowmanager/xr/intern/wm_xr_actions.c)281
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_actionmap.c567
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_draw.c32
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_intern.h79
-rw-r--r--source/blender/windowmanager/xr/intern/wm_xr_session.c186
762 files changed, 21577 insertions, 15710 deletions
diff --git a/source/blender/blenfont/BLF_api.h b/source/blender/blenfont/BLF_api.h
index 7e92f79a523..eb3f9805240 100644
--- a/source/blender/blenfont/BLF_api.h
+++ b/source/blender/blenfont/BLF_api.h
@@ -271,7 +271,7 @@ void BLF_state_print(int fontid);
#define BLF_ROTATION (1 << 0)
#define BLF_CLIPPING (1 << 1)
#define BLF_SHADOW (1 << 2)
-#define BLF_KERNING_DEFAULT (1 << 3)
+// #define BLF_FLAG_UNUSED_3 (1 << 3) /* dirty */
#define BLF_MATRIX (1 << 4)
#define BLF_ASPECT (1 << 5)
#define BLF_WORD_WRAP (1 << 6)
diff --git a/source/blender/blenfont/intern/blf.c b/source/blender/blenfont/intern/blf.c
index 9168e7aa19c..2f9eb0753ac 100644
--- a/source/blender/blenfont/intern/blf.c
+++ b/source/blender/blenfont/intern/blf.c
@@ -108,7 +108,6 @@ void BLF_cache_clear(void)
FontBLF *font = global_font[i];
if (font) {
blf_glyph_cache_clear(font);
- blf_kerning_cache_clear(font);
}
}
}
diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c
index 53c4135254a..75a2e893119 100644
--- a/source/blender/blenfont/intern/blf_font.c
+++ b/source/blender/blenfont/intern/blf_font.c
@@ -72,6 +72,38 @@ static SpinLock ft_lib_mutex;
static SpinLock blf_glyph_cache_mutex;
/* -------------------------------------------------------------------- */
+/** \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)
+{
+ /* 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. */
+ 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
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Glyph Batching
* \{ */
@@ -257,151 +289,78 @@ static void blf_batch_draw_end(void)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Glyph Stepping Utilities (Internal)
+ * \{ */
-int blf_font_init(void)
-{
- memset(&g_batch, 0, sizeof(g_batch));
- BLI_spin_init(&ft_lib_mutex);
- BLI_spin_init(&blf_glyph_cache_mutex);
- return FT_Init_FreeType(&ft_lib);
-}
-
-void blf_font_exit(void)
-{
- FT_Done_FreeType(ft_lib);
- BLI_spin_end(&ft_lib_mutex);
- BLI_spin_end(&blf_glyph_cache_mutex);
- blf_batch_draw_exit();
-}
+/* Fast path for runs of ASCII characters. Given that common UTF-8
+ * input will consist of an overwhelming majority of ASCII
+ * characters.
+ */
-void blf_font_size(FontBLF *font, unsigned int size, unsigned int dpi)
+BLI_INLINE GlyphBLF *blf_utf8_next_fast(
+ FontBLF *font, GlyphCacheBLF *gc, const char *str, size_t *i_p, uint *r_c)
{
- GlyphCacheBLF *gc;
- FT_Error err;
-
- blf_glyph_cache_acquire(font);
-
- gc = blf_glyph_cache_find(font, size, dpi);
- if (gc) {
- /* Optimization: do not call FT_Set_Char_Size if size did not change. */
- if (font->size == size && font->dpi == dpi) {
- blf_glyph_cache_release(font);
- return;
+ GlyphBLF *g;
+ if ((*r_c = str[*i_p]) < GLYPH_ASCII_TABLE_SIZE) {
+ g = (gc->glyph_ascii_table)[*r_c];
+ if (UNLIKELY(g == NULL)) {
+ g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, *r_c), *r_c);
+ gc->glyph_ascii_table[*r_c] = g;
}
+ (*i_p)++;
}
-
- err = FT_Set_Char_Size(font->face, 0, ((FT_F26Dot6)(size)) * 64, dpi, dpi);
- if (err) {
- /* FIXME: here we can go through the fixed size and choice a close one */
- printf("The current font don't support the size, %u and dpi, %u\n", size, dpi);
-
- blf_glyph_cache_release(font);
- return;
+ else if ((*r_c = BLI_str_utf8_as_unicode_step(str, i_p)) != BLI_UTF8_ERR) {
+ g = blf_glyph_search(gc, *r_c);
+ if (UNLIKELY(g == NULL)) {
+ g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, *r_c), *r_c);
+ }
}
-
- font->size = size;
- font->dpi = dpi;
-
- if (!gc) {
- blf_glyph_cache_new(font);
+ else {
+ g = NULL;
}
- blf_glyph_cache_release(font);
+ return g;
}
-static GlyphBLF **blf_font_ensure_ascii_table(FontBLF *font, GlyphCacheBLF *gc)
+BLI_INLINE void blf_kerning_step_fast(FontBLF *font,
+ const GlyphBLF *g_prev,
+ const GlyphBLF *g,
+ const uint c_prev,
+ const uint c,
+ int *pen_x_p)
{
- GlyphBLF **glyph_ascii_table;
-
- glyph_ascii_table = gc->glyph_ascii_table;
-
- /* build ascii on demand */
- if (glyph_ascii_table['0'] == NULL) {
- GlyphBLF *g;
- for (uint i = 0; i < 256; i++) {
- g = blf_glyph_search(gc, i);
- if (!g) {
- FT_UInt glyph_index = FT_Get_Char_Index(font->face, i);
- g = blf_glyph_add(font, gc, glyph_index, i);
- }
- glyph_ascii_table[i] = g;
- }
+ if (!FT_HAS_KERNING(font->face) || g_prev == NULL) {
+ return;
}
- return glyph_ascii_table;
-}
+ FT_Vector delta = {KERNING_ENTRY_UNSET};
-static void blf_font_ensure_ascii_kerning(FontBLF *font,
- GlyphCacheBLF *gc,
- const FT_UInt kern_mode)
-{
- KerningCacheBLF *kc = font->kerning_cache;
+ /* Get unscaled kerning value from our cache if ASCII. */
+ if ((c_prev < KERNING_CACHE_TABLE_SIZE) && (c < GLYPH_ASCII_TABLE_SIZE)) {
+ delta.x = font->kerning_cache->ascii_table[c][c_prev];
+ }
- font->kerning_mode = kern_mode;
+ /* 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 (!kc || kc->mode != kern_mode) {
- font->kerning_cache = kc = blf_kerning_cache_find(font);
- if (!kc) {
- font->kerning_cache = kc = blf_kerning_cache_new(font, gc);
- }
+ /* If ASCII we save this value to our cache for quicker access next time. */
+ if ((c_prev < KERNING_CACHE_TABLE_SIZE) && (c < GLYPH_ASCII_TABLE_SIZE)) {
+ font->kerning_cache->ascii_table[c][c_prev] = (int)delta.x;
+ }
+
+ if (delta.x != 0) {
+ /* Convert unscaled design units to pixels and move pen. */
+ *pen_x_p += blf_unscaled_F26Dot6_to_pixels(font, delta.x);
}
}
-/* Fast path for runs of ASCII characters. Given that common UTF-8
- * input will consist of an overwhelming majority of ASCII
- * characters.
- */
+/** \} */
-/* NOTE: `blf_font_ensure_ascii_table(font, gc);` must be called before this macro. */
-
-#define BLF_UTF8_NEXT_FAST(_font, _gc, _g, _str, _i, _c, _glyph_ascii_table) \
- if (((_c) = (_str)[_i]) < 0x80) { \
- _g = (_glyph_ascii_table)[_c]; \
- _i++; \
- } \
- else if ((_c = BLI_str_utf8_as_unicode_step(_str, &(_i))) != BLI_UTF8_ERR) { \
- if ((_g = blf_glyph_search(_gc, _c)) == NULL) { \
- _g = blf_glyph_add(_font, _gc, FT_Get_Char_Index((_font)->face, _c), _c); \
- } \
- } \
- else { \
- _g = NULL; \
- } \
- (void)0
-
-#define BLF_KERNING_VARS(_font, _has_kerning, _kern_mode) \
- const bool _has_kerning = FT_HAS_KERNING((_font)->face) != 0; \
- const FT_UInt _kern_mode = (_has_kerning == 0) ? 0 : \
- (((_font)->flags & BLF_KERNING_DEFAULT) ? \
- ft_kerning_default : \
- (FT_UInt)FT_KERNING_UNFITTED)
-
-/* NOTE: `blf_font_ensure_ascii_kerning(font, gc, kern_mode);` must be called before this macro. */
-
-#define BLF_KERNING_STEP_FAST(_font, _kern_mode, _g_prev, _g, _c_prev, _c, _pen_x) \
- { \
- if (_g_prev) { \
- FT_Vector _delta; \
- if (_c_prev < 0x80 && _c < 0x80) { \
- _pen_x += (_font)->kerning_cache->table[_c][_c_prev]; \
- } \
- else if (FT_Get_Kerning((_font)->face, (_g_prev)->idx, (_g)->idx, _kern_mode, &(_delta)) == \
- 0) { \
- _pen_x += (int)_delta.x >> 6; \
- } \
- } \
- } \
- (void)0
-
-#define BLF_KERNING_STEP(_font, _kern_mode, _g_prev, _g, _delta, _pen_x) \
- { \
- if (_g_prev) { \
- _delta.x = _delta.y = 0; \
- if (FT_Get_Kerning((_font)->face, (_g_prev)->idx, (_g)->idx, _kern_mode, &(_delta)) == 0) { \
- _pen_x += (int)_delta.x >> 6; \
- } \
- } \
- } \
- (void)0
+/* -------------------------------------------------------------------- */
+/** \name Text Drawing: GPU
+ * \{ */
static void blf_font_draw_ex(FontBLF *font,
GlyphCacheBLF *gc,
@@ -420,16 +379,10 @@ static void blf_font_draw_ex(FontBLF *font,
return;
}
- GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc);
-
- BLF_KERNING_VARS(font, has_kerning, kern_mode);
-
- blf_font_ensure_ascii_kerning(font, gc, kern_mode);
-
blf_batch_draw_begin(font);
while ((i < len) && str[i]) {
- BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c, glyph_ascii_table);
+ g = blf_utf8_next_fast(font, gc, str, &i, &c);
if (UNLIKELY(c == BLI_UTF8_ERR)) {
break;
@@ -437,9 +390,7 @@ static void blf_font_draw_ex(FontBLF *font,
if (UNLIKELY(g == NULL)) {
continue;
}
- if (has_kerning) {
- BLF_KERNING_STEP_FAST(font, kern_mode, g_prev, g, c_prev, c, pen_x);
- }
+ blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x);
/* do not return this loop if clipped, we want every character tested */
blf_glyph_render(font, gc, g, (float)pen_x, (float)pen_y);
@@ -472,22 +423,20 @@ static void blf_font_draw_ascii_ex(
int pen_x = 0;
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
- GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc);
-
- BLF_KERNING_VARS(font, has_kerning, kern_mode);
-
- blf_font_ensure_ascii_kerning(font, gc, kern_mode);
blf_batch_draw_begin(font);
while ((c = *(str++)) && len--) {
- BLI_assert(c < 128);
- if ((g = glyph_ascii_table[c]) == NULL) {
- continue;
- }
- if (has_kerning) {
- BLF_KERNING_STEP_FAST(font, kern_mode, g_prev, g, c_prev, c, pen_x);
+ BLI_assert(c < GLYPH_ASCII_TABLE_SIZE);
+ g = gc->glyph_ascii_table[c];
+ if (UNLIKELY(g == NULL)) {
+ g = blf_glyph_add(font, gc, FT_Get_Char_Index((font)->face, c), c);
+ gc->glyph_ascii_table[c] = g;
+ if (UNLIKELY(g == NULL)) {
+ continue;
+ }
}
+ blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x);
/* do not return this loop if clipped, we want every character tested */
blf_glyph_render(font, gc, g, (float)pen_x, (float)pen_y);
@@ -522,12 +471,11 @@ int blf_font_draw_mono(FontBLF *font, const char *str, size_t len, int cwidth)
size_t i = 0;
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
- GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc);
blf_batch_draw_begin(font);
while ((i < len) && str[i]) {
- BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c, glyph_ascii_table);
+ g = blf_utf8_next_fast(font, gc, str, &i, &c);
if (UNLIKELY(c == BLI_UTF8_ERR)) {
break;
@@ -554,6 +502,12 @@ int blf_font_draw_mono(FontBLF *font, const char *str, size_t len, int cwidth)
return columns;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Text Drawgin: Buffer
+ * \{ */
+
/* Sanity checks are done by BLF_draw_buffer() */
static void blf_font_draw_buffer_ex(FontBLF *font,
GlyphCacheBLF *gc,
@@ -568,8 +522,6 @@ static void blf_font_draw_buffer_ex(FontBLF *font,
int pen_y_basis = (int)font->pos[1] + pen_y;
size_t i = 0;
- GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc);
-
/* buffer specific vars */
FontBufInfoBLF *buf_info = &font->buf_info;
const float *b_col_float = buf_info->col_float;
@@ -577,14 +529,10 @@ static void blf_font_draw_buffer_ex(FontBLF *font,
int chx, chy;
int y, x;
- BLF_KERNING_VARS(font, has_kerning, kern_mode);
-
- blf_font_ensure_ascii_kerning(font, gc, kern_mode);
-
/* another buffer specific call for color conversion */
while ((i < len) && str[i]) {
- BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c, glyph_ascii_table);
+ g = blf_utf8_next_fast(font, gc, str, &i, &c);
if (UNLIKELY(c == BLI_UTF8_ERR)) {
break;
@@ -592,9 +540,7 @@ static void blf_font_draw_buffer_ex(FontBLF *font,
if (UNLIKELY(g == NULL)) {
continue;
}
- if (has_kerning) {
- BLF_KERNING_STEP_FAST(font, kern_mode, g_prev, g, c_prev, c, pen_x);
- }
+ blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x);
chx = pen_x + ((int)g->pos[0]);
chy = pen_y_basis + g->dims[1];
@@ -707,9 +653,17 @@ void blf_font_draw_buffer(FontBLF *font, const char *str, size_t len, struct Res
blf_glyph_cache_release(font);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Text Evaluation: Width to Sting Length
+ *
+ * Use to implement exported functions:
+ * - #BLF_width_to_strlen
+ * - #BLF_width_to_rstrlen
+ * \{ */
+
static bool blf_font_width_to_strlen_glyph_process(FontBLF *font,
- const bool has_kerning,
- const FT_UInt kern_mode,
const uint c_prev,
const uint c,
GlyphBLF *g_prev,
@@ -723,9 +677,7 @@ static bool blf_font_width_to_strlen_glyph_process(FontBLF *font,
if (UNLIKELY(g == NULL)) {
return false; /* continue the calling loop. */
}
- if (has_kerning) {
- BLF_KERNING_STEP_FAST(font, kern_mode, g_prev, g, c_prev, c, *pen_x);
- }
+ blf_kerning_step_fast(font, g_prev, g, c_prev, c, pen_x);
*pen_x += g->advance_i;
@@ -741,21 +693,13 @@ size_t blf_font_width_to_strlen(
size_t i, i_prev;
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
- GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc);
const int width_i = (int)width;
- BLF_KERNING_VARS(font, has_kerning, kern_mode);
-
- if (has_kerning) {
- blf_font_ensure_ascii_kerning(font, gc, kern_mode);
- }
-
for (i_prev = i = 0, width_new = pen_x = 0, g_prev = NULL, c_prev = 0; (i < len) && str[i];
i_prev = i, width_new = pen_x, c_prev = c, g_prev = g) {
- BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c, glyph_ascii_table);
+ g = blf_utf8_next_fast(font, gc, str, &i, &c);
- if (blf_font_width_to_strlen_glyph_process(
- font, has_kerning, kern_mode, c_prev, c, g_prev, g, &pen_x, width_i)) {
+ if (blf_font_width_to_strlen_glyph_process(font, c_prev, c, g_prev, g, &pen_x, width_i)) {
break;
}
}
@@ -778,15 +722,8 @@ size_t blf_font_width_to_rstrlen(
char *s, *s_prev;
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
- GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc);
const int width_i = (int)width;
- BLF_KERNING_VARS(font, has_kerning, kern_mode);
-
- if (has_kerning) {
- blf_font_ensure_ascii_kerning(font, gc, kern_mode);
- }
-
i = BLI_strnlen(str, len);
s = BLI_str_find_prev_char_utf8(str, &str[i]);
i = (size_t)((s != NULL) ? s - str : 0);
@@ -794,7 +731,7 @@ size_t blf_font_width_to_rstrlen(
i_prev = (size_t)((s_prev != NULL) ? s_prev - str : 0);
i_tmp = i;
- BLF_UTF8_NEXT_FAST(font, gc, g, str, i_tmp, c, glyph_ascii_table);
+ g = blf_utf8_next_fast(font, gc, str, &i_tmp, &c);
for (width_new = pen_x = 0; (s != NULL);
i = i_prev, s = s_prev, c = c_prev, g = g_prev, g_prev = NULL, width_new = pen_x) {
s_prev = BLI_str_find_prev_char_utf8(str, s);
@@ -802,12 +739,11 @@ size_t blf_font_width_to_rstrlen(
if (s_prev != NULL) {
i_tmp = i_prev;
- BLF_UTF8_NEXT_FAST(font, gc, g_prev, str, i_tmp, c_prev, glyph_ascii_table);
+ g_prev = blf_utf8_next_fast(font, gc, str, &i_tmp, &c_prev);
BLI_assert(i_tmp == i);
}
- if (blf_font_width_to_strlen_glyph_process(
- font, has_kerning, kern_mode, c_prev, c, g_prev, g, &pen_x, width_i)) {
+ if (blf_font_width_to_strlen_glyph_process(font, c_prev, c, g_prev, g, &pen_x, width_i)) {
break;
}
}
@@ -820,6 +756,12 @@ size_t blf_font_width_to_rstrlen(
return i;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Text Evaluation: Glyph Bound Box with Callback
+ * \{ */
+
static void blf_font_boundbox_ex(FontBLF *font,
GlyphCacheBLF *gc,
const char *str,
@@ -832,22 +774,15 @@ static void blf_font_boundbox_ex(FontBLF *font,
GlyphBLF *g, *g_prev = NULL;
int pen_x = 0;
size_t i = 0;
-
- GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc);
-
rctf gbox;
- BLF_KERNING_VARS(font, has_kerning, kern_mode);
-
box->xmin = 32000.0f;
box->xmax = -32000.0f;
box->ymin = 32000.0f;
box->ymax = -32000.0f;
- blf_font_ensure_ascii_kerning(font, gc, kern_mode);
-
while ((i < len) && str[i]) {
- BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c, glyph_ascii_table);
+ g = blf_utf8_next_fast(font, gc, str, &i, &c);
if (UNLIKELY(c == BLI_UTF8_ERR)) {
break;
@@ -855,9 +790,7 @@ static void blf_font_boundbox_ex(FontBLF *font,
if (UNLIKELY(g == NULL)) {
continue;
}
- if (has_kerning) {
- BLF_KERNING_STEP_FAST(font, kern_mode, g_prev, g, c_prev, c, pen_x);
- }
+ blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x);
gbox.xmin = (float)pen_x;
gbox.xmax = (float)pen_x + g->advance;
@@ -903,8 +836,165 @@ void blf_font_boundbox(
blf_glyph_cache_release(font);
}
+void blf_font_width_and_height(FontBLF *font,
+ const char *str,
+ size_t len,
+ float *r_width,
+ float *r_height,
+ struct ResultBLF *r_info)
+{
+ float xa, ya;
+ rctf box;
+
+ if (font->flags & BLF_ASPECT) {
+ xa = font->aspect[0];
+ ya = font->aspect[1];
+ }
+ else {
+ xa = 1.0f;
+ ya = 1.0f;
+ }
+
+ if (font->flags & BLF_WORD_WRAP) {
+ blf_font_boundbox__wrap(font, str, len, &box, r_info);
+ }
+ else {
+ blf_font_boundbox(font, str, len, &box, r_info);
+ }
+ *r_width = (BLI_rctf_size_x(&box) * xa);
+ *r_height = (BLI_rctf_size_y(&box) * ya);
+}
+
+float blf_font_width(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info)
+{
+ float xa;
+ rctf box;
+
+ if (font->flags & BLF_ASPECT) {
+ xa = font->aspect[0];
+ }
+ else {
+ xa = 1.0f;
+ }
+
+ if (font->flags & BLF_WORD_WRAP) {
+ blf_font_boundbox__wrap(font, str, len, &box, r_info);
+ }
+ else {
+ blf_font_boundbox(font, str, len, &box, r_info);
+ }
+ return BLI_rctf_size_x(&box) * xa;
+}
+
+float blf_font_height(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info)
+{
+ float ya;
+ rctf box;
+
+ if (font->flags & BLF_ASPECT) {
+ ya = font->aspect[1];
+ }
+ else {
+ ya = 1.0f;
+ }
+
+ if (font->flags & BLF_WORD_WRAP) {
+ blf_font_boundbox__wrap(font, str, len, &box, r_info);
+ }
+ else {
+ blf_font_boundbox(font, str, len, &box, r_info);
+ }
+ return BLI_rctf_size_y(&box) * ya;
+}
+
+float blf_font_fixed_width(FontBLF *font)
+{
+ const unsigned int c = ' ';
+
+ GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
+ GlyphBLF *g = blf_glyph_search(gc, c);
+ if (!g) {
+ g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, c), c);
+
+ /* if we don't find the glyph. */
+ if (!g) {
+ blf_glyph_cache_release(font);
+ return 0.0f;
+ }
+ }
+
+ blf_glyph_cache_release(font);
+ return g->advance;
+}
+
+static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font,
+ GlyphCacheBLF *gc,
+ const char *str,
+ size_t len,
+ BLF_GlyphBoundsFn user_fn,
+ void *user_data,
+ struct ResultBLF *r_info,
+ int pen_y)
+{
+ unsigned int c, c_prev = BLI_UTF8_ERR;
+ GlyphBLF *g, *g_prev = NULL;
+ int pen_x = 0;
+ size_t i = 0, i_curr;
+ rcti gbox;
+
+ if (len == 0) {
+ /* early output. */
+ return;
+ }
+
+ while ((i < len) && str[i]) {
+ i_curr = i;
+ g = blf_utf8_next_fast(font, gc, str, &i, &c);
+
+ if (UNLIKELY(c == BLI_UTF8_ERR)) {
+ break;
+ }
+ if (UNLIKELY(g == NULL)) {
+ continue;
+ }
+ blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x);
+
+ 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 += g->advance_i;
+
+ if (user_fn(str, i_curr, &gbox, g->advance_i, &g->box, g->pos, user_data) == false) {
+ break;
+ }
+
+ g_prev = g;
+ c_prev = c;
+ }
+
+ if (r_info) {
+ r_info->lines = 1;
+ r_info->width = pen_x;
+ }
+}
+void blf_font_boundbox_foreach_glyph(FontBLF *font,
+ const char *str,
+ size_t len,
+ BLF_GlyphBoundsFn user_fn,
+ void *user_data,
+ struct ResultBLF *r_info)
+{
+ GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
+ blf_font_boundbox_foreach_glyph_ex(font, gc, str, len, user_fn, user_data, r_info, 0);
+ blf_glyph_cache_release(font);
+}
+
+/** \} */
+
/* -------------------------------------------------------------------- */
-/** \name Word-Wrap Support
+/** \name Text Evaluation: Word-Wrap with Callback
* \{ */
/**
@@ -928,18 +1018,14 @@ static void blf_font_wrap_apply(FontBLF *font,
void *userdata),
void *userdata)
{
- unsigned int c;
+ unsigned int c, c_prev = BLI_UTF8_ERR;
GlyphBLF *g, *g_prev = NULL;
- FT_Vector delta;
int pen_x = 0, pen_y = 0;
size_t i = 0;
int lines = 0;
int pen_x_next = 0;
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
- GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc);
-
- BLF_KERNING_VARS(font, has_kerning, kern_mode);
struct WordWrapVars {
int wrap_width;
@@ -953,7 +1039,7 @@ static void blf_font_wrap_apply(FontBLF *font,
size_t i_curr = i;
bool do_draw = false;
- BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c, glyph_ascii_table);
+ g = blf_utf8_next_fast(font, gc, str, &i, &c);
if (UNLIKELY(c == BLI_UTF8_ERR)) {
break;
@@ -961,9 +1047,7 @@ static void blf_font_wrap_apply(FontBLF *font,
if (UNLIKELY(g == NULL)) {
continue;
}
- if (has_kerning) {
- BLF_KERNING_STEP(font, kern_mode, g_prev, g, delta, pen_x);
- }
+ blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x);
/**
* Implementation Detail (utf8).
@@ -1003,12 +1087,14 @@ static void blf_font_wrap_apply(FontBLF *font,
pen_x = 0;
pen_y -= gc->glyph_height_max;
g_prev = NULL;
+ c_prev = BLI_UTF8_ERR;
lines += 1;
continue;
}
pen_x = pen_x_next;
g_prev = g;
+ c_prev = c;
}
// printf("done! lines: %d, width, %d\n", lines, pen_x_next);
@@ -1078,224 +1164,120 @@ void blf_font_draw_buffer__wrap(FontBLF *font,
/** \} */
-void blf_font_width_and_height(FontBLF *font,
- const char *str,
- size_t len,
- float *r_width,
- float *r_height,
- struct ResultBLF *r_info)
+/* -------------------------------------------------------------------- */
+/** \name Text Evaluation: Count Missing Characters
+ * \{ */
+
+int blf_font_count_missing_chars(FontBLF *font,
+ const char *str,
+ const size_t len,
+ int *r_tot_chars)
{
- float xa, ya;
- rctf box;
+ int missing = 0;
+ size_t i = 0;
- if (font->flags & BLF_ASPECT) {
- xa = font->aspect[0];
- ya = font->aspect[1];
- }
- else {
- xa = 1.0f;
- ya = 1.0f;
- }
+ *r_tot_chars = 0;
+ while (i < len) {
+ unsigned int c;
- if (font->flags & BLF_WORD_WRAP) {
- blf_font_boundbox__wrap(font, str, len, &box, r_info);
- }
- else {
- blf_font_boundbox(font, str, len, &box, r_info);
+ if ((c = str[i]) < GLYPH_ASCII_TABLE_SIZE) {
+ i++;
+ }
+ else if ((c = BLI_str_utf8_as_unicode_step(str, &i)) != BLI_UTF8_ERR) {
+ if (FT_Get_Char_Index((font)->face, c) == 0) {
+ missing++;
+ }
+ }
+ (*r_tot_chars)++;
}
- *r_width = (BLI_rctf_size_x(&box) * xa);
- *r_height = (BLI_rctf_size_y(&box) * ya);
+ return missing;
}
-float blf_font_width(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info)
-{
- float xa;
- rctf box;
-
- if (font->flags & BLF_ASPECT) {
- xa = font->aspect[0];
- }
- else {
- xa = 1.0f;
- }
+/** \} */
- if (font->flags & BLF_WORD_WRAP) {
- blf_font_boundbox__wrap(font, str, len, &box, r_info);
- }
- else {
- blf_font_boundbox(font, str, len, &box, r_info);
- }
- return BLI_rctf_size_x(&box) * xa;
-}
+/* -------------------------------------------------------------------- */
+/** \name Font Query: Attributes
+ * \{ */
-float blf_font_height(FontBLF *font, const char *str, size_t len, struct ResultBLF *r_info)
+int blf_font_height_max(FontBLF *font)
{
- float ya;
- rctf box;
+ int height_max;
- if (font->flags & BLF_ASPECT) {
- ya = font->aspect[1];
- }
- else {
- ya = 1.0f;
- }
+ GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
+ height_max = gc->glyph_height_max;
- if (font->flags & BLF_WORD_WRAP) {
- blf_font_boundbox__wrap(font, str, len, &box, r_info);
- }
- else {
- blf_font_boundbox(font, str, len, &box, r_info);
- }
- return BLI_rctf_size_y(&box) * ya;
+ blf_glyph_cache_release(font);
+ return height_max;
}
-float blf_font_fixed_width(FontBLF *font)
+int blf_font_width_max(FontBLF *font)
{
- const unsigned int c = ' ';
+ int width_max;
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
- blf_font_ensure_ascii_table(font, gc);
-
- GlyphBLF *g = blf_glyph_search(gc, c);
- if (!g) {
- g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, c), c);
-
- /* if we don't find the glyph. */
- if (!g) {
- blf_glyph_cache_release(font);
- return 0.0f;
- }
- }
+ width_max = gc->glyph_width_max;
blf_glyph_cache_release(font);
- return g->advance;
+ return width_max;
}
-/* -------------------------------------------------------------------- */
-/** \name Glyph Bound Box with Callback
- * \{ */
-
-static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font,
- GlyphCacheBLF *gc,
- const char *str,
- size_t len,
- BLF_GlyphBoundsFn user_fn,
- void *user_data,
- struct ResultBLF *r_info,
- int pen_y)
+float blf_font_descender(FontBLF *font)
{
- unsigned int c, c_prev = BLI_UTF8_ERR;
- GlyphBLF *g, *g_prev = NULL;
- int pen_x = 0;
- size_t i = 0, i_curr;
- rcti gbox;
-
- if (len == 0) {
- /* early output. */
- return;
- }
-
- GlyphBLF **glyph_ascii_table = blf_font_ensure_ascii_table(font, gc);
-
- BLF_KERNING_VARS(font, has_kerning, kern_mode);
-
- blf_font_ensure_ascii_kerning(font, gc, kern_mode);
-
- while ((i < len) && str[i]) {
- i_curr = i;
- BLF_UTF8_NEXT_FAST(font, gc, g, str, i, c, glyph_ascii_table);
-
- if (UNLIKELY(c == BLI_UTF8_ERR)) {
- break;
- }
- if (UNLIKELY(g == NULL)) {
- continue;
- }
- if (has_kerning) {
- BLF_KERNING_STEP_FAST(font, kern_mode, g_prev, g, c_prev, c, pen_x);
- }
+ float descender;
- 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];
+ GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
+ descender = gc->descender;
- pen_x += g->advance_i;
+ blf_glyph_cache_release(font);
+ return descender;
+}
- if (user_fn(str, i_curr, &gbox, g->advance_i, &g->box, g->pos, user_data) == false) {
- break;
- }
+float blf_font_ascender(FontBLF *font)
+{
+ float ascender;
- g_prev = g;
- c_prev = c;
- }
+ GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
+ ascender = gc->ascender;
- if (r_info) {
- r_info->lines = 1;
- r_info->width = pen_x;
- }
+ blf_glyph_cache_release(font);
+ return ascender;
}
-void blf_font_boundbox_foreach_glyph(FontBLF *font,
- const char *str,
- size_t len,
- BLF_GlyphBoundsFn user_fn,
- void *user_data,
- struct ResultBLF *r_info)
+
+char *blf_display_name(FontBLF *font)
{
- GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
- blf_font_boundbox_foreach_glyph_ex(font, gc, str, len, user_fn, user_data, r_info, 0);
- blf_glyph_cache_release(font);
+ if (!font->face->family_name) {
+ return NULL;
+ }
+ return BLI_sprintfN("%s %s", font->face->family_name, font->face->style_name);
}
/** \} */
-int blf_font_count_missing_chars(FontBLF *font,
- const char *str,
- const size_t len,
- int *r_tot_chars)
-{
- int missing = 0;
- size_t i = 0;
-
- *r_tot_chars = 0;
- while (i < len) {
- unsigned int c;
+/* -------------------------------------------------------------------- */
+/** \name Font Subsystem Init/Exit
+ * \{ */
- if ((c = str[i]) < 0x80) {
- i++;
- }
- else if ((c = BLI_str_utf8_as_unicode_step(str, &i)) != BLI_UTF8_ERR) {
- if (FT_Get_Char_Index((font)->face, c) == 0) {
- missing++;
- }
- }
- (*r_tot_chars)++;
- }
- return missing;
+int blf_font_init(void)
+{
+ memset(&g_batch, 0, sizeof(g_batch));
+ BLI_spin_init(&ft_lib_mutex);
+ BLI_spin_init(&blf_glyph_cache_mutex);
+ return FT_Init_FreeType(&ft_lib);
}
-void blf_font_free(FontBLF *font)
+void blf_font_exit(void)
{
- BLI_spin_lock(&blf_glyph_cache_mutex);
- GlyphCacheBLF *gc;
-
- while ((gc = BLI_pophead(&font->cache))) {
- blf_glyph_cache_free(gc);
- }
-
- blf_kerning_cache_clear(font);
+ FT_Done_FreeType(ft_lib);
+ BLI_spin_end(&ft_lib_mutex);
+ BLI_spin_end(&blf_glyph_cache_mutex);
+ blf_batch_draw_exit();
+}
- FT_Done_Face(font->face);
- if (font->filename) {
- MEM_freeN(font->filename);
- }
- if (font->name) {
- MEM_freeN(font->name);
- }
- MEM_freeN(font);
+/** \} */
- BLI_spin_unlock(&blf_glyph_cache_mutex);
-}
+/* -------------------------------------------------------------------- */
+/** \name Font New/Free
+ * \{ */
static void blf_font_fill(FontBLF *font)
{
@@ -1324,7 +1306,6 @@ static void blf_font_fill(FontBLF *font)
font->dpi = 0;
font->size = 0;
BLI_listbase_clear(&font->cache);
- BLI_listbase_clear(&font->kerning_caches);
font->kerning_cache = NULL;
#if BLF_BLUR_ENABLE
font->blur = 0;
@@ -1359,9 +1340,15 @@ FontBLF *blf_font_new(const char *name, const char *filename)
return NULL;
}
- err = FT_Select_Charmap(font->face, ft_encoding_unicode);
+ err = FT_Select_Charmap(font->face, FT_ENCODING_UNICODE);
if (err) {
- printf("Can't set the unicode character map!\n");
+ err = FT_Select_Charmap(font->face, FT_ENCODING_APPLE_ROMAN);
+ }
+ if (err && font->face->num_charmaps > 0) {
+ err = FT_Select_Charmap(font->face, font->face->charmaps[0]->encoding);
+ }
+ if (err) {
+ printf("Can't set a character map!\n");
FT_Done_Face(font->face);
MEM_freeN(font);
return NULL;
@@ -1379,6 +1366,17 @@ FontBLF *blf_font_new(const char *name, const char *filename)
font->name = BLI_strdup(name);
font->filename = BLI_strdup(filename);
blf_font_fill(font);
+
+ if (FT_HAS_KERNING(font->face)) {
+ /* Create kerning cache table and fill with value indicating "unset". */
+ font->kerning_cache = MEM_mallocN(sizeof(KerningCacheBLF), __func__);
+ for (uint i = 0; i < KERNING_CACHE_TABLE_SIZE; i++) {
+ for (uint j = 0; j < KERNING_CACHE_TABLE_SIZE; j++) {
+ font->kerning_cache->ascii_table[i][j] = KERNING_ENTRY_UNSET;
+ }
+ }
+ }
+
return font;
}
@@ -1418,58 +1416,61 @@ FontBLF *blf_font_new_from_mem(const char *name, const unsigned char *mem, int m
return font;
}
-int blf_font_height_max(FontBLF *font)
+void blf_font_free(FontBLF *font)
{
- int height_max;
-
- GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
- blf_font_ensure_ascii_table(font, gc);
- height_max = gc->glyph_height_max;
+ BLI_spin_lock(&blf_glyph_cache_mutex);
+ GlyphCacheBLF *gc;
- blf_glyph_cache_release(font);
- return height_max;
-}
+ while ((gc = BLI_pophead(&font->cache))) {
+ blf_glyph_cache_free(gc);
+ }
-int blf_font_width_max(FontBLF *font)
-{
- int width_max;
+ if (font->kerning_cache) {
+ MEM_freeN(font->kerning_cache);
+ }
- GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
- blf_font_ensure_ascii_table(font, gc);
- width_max = gc->glyph_width_max;
+ FT_Done_Face(font->face);
+ if (font->filename) {
+ MEM_freeN(font->filename);
+ }
+ if (font->name) {
+ MEM_freeN(font->name);
+ }
+ MEM_freeN(font);
- blf_glyph_cache_release(font);
- return width_max;
+ BLI_spin_unlock(&blf_glyph_cache_mutex);
}
-float blf_font_descender(FontBLF *font)
-{
- float descender;
-
- GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
- blf_font_ensure_ascii_table(font, gc);
- descender = gc->descender;
+/** \} */
- blf_glyph_cache_release(font);
- return descender;
-}
+/* -------------------------------------------------------------------- */
+/** \name Font Configure
+ * \{ */
-float blf_font_ascender(FontBLF *font)
+void blf_font_size(FontBLF *font, unsigned int size, unsigned int dpi)
{
- float ascender;
+ blf_glyph_cache_acquire(font);
- GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
- blf_font_ensure_ascii_table(font, gc);
- ascender = gc->ascender;
+ GlyphCacheBLF *gc = blf_glyph_cache_find(font, size, dpi);
+ if (gc && (font->size == size && font->dpi == dpi)) {
+ /* Optimization: do not call FT_Set_Char_Size if size did not change. */
+ }
+ else {
+ const FT_Error err = FT_Set_Char_Size(font->face, 0, ((FT_F26Dot6)(size)) * 64, dpi, dpi);
+ if (err) {
+ /* FIXME: here we can go through the fixed size and choice a close one */
+ printf("The current font don't support the size, %u and dpi, %u\n", size, dpi);
+ }
+ else {
+ font->size = size;
+ font->dpi = dpi;
+ if (gc == NULL) {
+ blf_glyph_cache_new(font);
+ }
+ }
+ }
blf_glyph_cache_release(font);
- return ascender;
}
-char *blf_display_name(FontBLF *font)
-{
- if (!font->face->family_name) {
- return NULL;
- }
- return BLI_sprintfN("%s %s", font->face->family_name, font->face->style_name);
-}
+/** \} */
diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c
index 3f01501fda4..6cdf5fc5996 100644
--- a/source/blender/blenfont/intern/blf_glyph.c
+++ b/source/blender/blenfont/intern/blf_glyph.c
@@ -55,64 +55,6 @@
#include "BLI_math_vector.h"
#include "BLI_strict_flags.h"
-KerningCacheBLF *blf_kerning_cache_find(FontBLF *font)
-{
- KerningCacheBLF *p;
-
- p = (KerningCacheBLF *)font->kerning_caches.first;
- while (p) {
- if (p->mode == font->kerning_mode) {
- return p;
- }
- p = p->next;
- }
- return NULL;
-}
-
-/* Create a new glyph cache for the current kerning mode. */
-KerningCacheBLF *blf_kerning_cache_new(FontBLF *font, GlyphCacheBLF *gc)
-{
- KerningCacheBLF *kc;
-
- kc = (KerningCacheBLF *)MEM_callocN(sizeof(KerningCacheBLF), "blf_kerning_cache_new");
- kc->next = NULL;
- kc->prev = NULL;
- kc->mode = font->kerning_mode;
-
- unsigned int i, j;
- for (i = 0; i < 0x80; i++) {
- for (j = 0; j < 0x80; j++) {
- GlyphBLF *g = blf_glyph_search(gc, i);
- if (!g) {
- FT_UInt glyph_index = FT_Get_Char_Index(font->face, i);
- g = blf_glyph_add(font, gc, glyph_index, i);
- }
- /* Can fail on certain fonts */
- GlyphBLF *g_prev = blf_glyph_search(gc, j);
-
- FT_Vector delta = {
- .x = 0,
- .y = 0,
- };
- if (g && g_prev && FT_Get_Kerning(font->face, g_prev->idx, g->idx, kc->mode, &delta) == 0) {
- kc->table[i][j] = (int)delta.x >> 6;
- }
- else {
- kc->table[i][j] = 0;
- }
- }
- }
-
- BLI_addhead(&font->kerning_caches, kc);
- return kc;
-}
-
-void blf_kerning_cache_clear(FontBLF *font)
-{
- font->kerning_cache = NULL;
- BLI_freelistN(&font->kerning_caches);
-}
-
GlyphCacheBLF *blf_glyph_cache_find(FontBLF *font, unsigned int size, unsigned int dpi)
{
GlyphCacheBLF *p;
@@ -144,8 +86,6 @@ GlyphCacheBLF *blf_glyph_cache_new(FontBLF *font)
memset(gc->glyph_ascii_table, 0, sizeof(gc->glyph_ascii_table));
memset(gc->bucket, 0, sizeof(gc->bucket));
- gc->glyphs_len_max = (int)font->face->num_glyphs;
- gc->glyphs_len_free = (int)font->face->num_glyphs;
gc->ascender = ((float)font->face->size->metrics.ascender) / 64.0f;
gc->descender = ((float)font->face->size->metrics.descender) / 64.0f;
@@ -514,7 +454,6 @@ void blf_glyph_render(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, fl
memcpy(&gc->bitmap_result[gc->bitmap_len], g->bitmap, (size_t)buff_size);
gc->bitmap_len = bitmap_len;
- gc->glyphs_len_free--;
g->glyph_cache = gc;
}
diff --git a/source/blender/blenfont/intern/blf_internal.h b/source/blender/blenfont/intern/blf_internal.h
index 35a6d019eac..ab2a26b1e06 100644
--- a/source/blender/blenfont/intern/blf_internal.h
+++ b/source/blender/blenfont/intern/blf_internal.h
@@ -121,10 +121,6 @@ int blf_font_count_missing_chars(struct FontBLF *font,
void blf_font_free(struct FontBLF *font);
-struct KerningCacheBLF *blf_kerning_cache_find(struct FontBLF *font);
-struct KerningCacheBLF *blf_kerning_cache_new(struct FontBLF *font, struct GlyphCacheBLF *gc);
-void blf_kerning_cache_clear(struct FontBLF *font);
-
struct GlyphCacheBLF *blf_glyph_cache_find(struct FontBLF *font,
unsigned int size,
unsigned int dpi);
diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h
index 36bb8769306..38d7d7b6e21 100644
--- a/source/blender/blenfont/intern/blf_internal_types.h
+++ b/source/blender/blenfont/intern/blf_internal_types.h
@@ -28,6 +28,15 @@
#define BLF_BATCH_DRAW_LEN_MAX 2048 /* in glyph */
+/* Number of characters in GlyphCacheBLF.glyph_ascii_table. */
+#define GLYPH_ASCII_TABLE_SIZE 128
+
+/* Number of characters in KerningCacheBLF.table. */
+#define KERNING_CACHE_TABLE_SIZE 128
+
+/* A value in the kerning cache that indicates it is not yet set. */
+#define KERNING_ENTRY_UNSET INT_MAX
+
typedef struct BatchBLF {
struct FontBLF *font; /* can only batch glyph from the same font */
struct GPUBatch *batch;
@@ -44,14 +53,11 @@ typedef struct BatchBLF {
extern BatchBLF g_batch;
typedef struct KerningCacheBLF {
- struct KerningCacheBLF *next, *prev;
-
- /* kerning mode. */
- FT_UInt mode;
-
- /* only cache a ascii glyph pairs. Only store the x
- * offset we are interested in, instead of the full FT_Vector. */
- int table[0x80][0x80];
+ /**
+ * Cache a ascii glyph pairs. Only store the x offset we are interested in,
+ * instead of the full #FT_Vector since it's not used for drawing at the moment.
+ */
+ int ascii_table[KERNING_CACHE_TABLE_SIZE][KERNING_CACHE_TABLE_SIZE];
} KerningCacheBLF;
typedef struct GlyphCacheBLF {
@@ -71,7 +77,7 @@ typedef struct GlyphCacheBLF {
ListBase bucket[257];
/* fast ascii lookup */
- struct GlyphBLF *glyph_ascii_table[256];
+ struct GlyphBLF *glyph_ascii_table[GLYPH_ASCII_TABLE_SIZE];
/* texture array, to draw the glyphs. */
GPUTexture *texture;
@@ -84,12 +90,6 @@ typedef struct GlyphCacheBLF {
int glyph_width_max;
int glyph_height_max;
- /* number of glyphs in the font. */
- int glyphs_len_max;
-
- /* number of glyphs not yet loaded (decreases every glyph loaded). */
- int glyphs_len_free;
-
/* ascender and descender value. */
float ascender;
float descender;
@@ -99,7 +99,7 @@ typedef struct GlyphBLF {
struct GlyphBLF *next;
struct GlyphBLF *prev;
- /* and the character, as UTF8 */
+ /* and the character, as UTF-32 */
unsigned int c;
/* freetype2 index, to speed-up the search. */
@@ -225,10 +225,7 @@ typedef struct FontBLF {
*/
ListBase cache;
- /* list of kerning cache for this font. */
- ListBase kerning_caches;
-
- /* current kerning cache for this font and kerning mode. */
+ /* Cache of unscaled kerning values. Will be NULL if font does not have kerning. */
KerningCacheBLF *kerning_cache;
/* freetype2 lib handle. */
@@ -240,9 +237,6 @@ typedef struct FontBLF {
/* freetype2 face. */
FT_Face face;
- /* freetype kerning */
- FT_UInt kerning_mode;
-
/* data for buffer usage (drawing into a texture buffer) */
FontBufInfoBLF buf_info;
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h
index e76e3ed8fe0..4ed4225c836 100644
--- a/source/blender/blenkernel/BKE_blender_version.h
+++ b/source/blender/blenkernel/BKE_blender_version.h
@@ -39,7 +39,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
-#define BLENDER_FILE_SUBVERSION 15
+#define BLENDER_FILE_SUBVERSION 18
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and show a warning if the file
diff --git a/source/blender/blenkernel/BKE_bvhutils.h b/source/blender/blenkernel/BKE_bvhutils.h
index 8be2fcbdb83..06be8ec80fc 100644
--- a/source/blender/blenkernel/BKE_bvhutils.h
+++ b/source/blender/blenkernel/BKE_bvhutils.h
@@ -69,7 +69,7 @@ typedef struct BVHTreeFromMesh {
BVHTree_NearestPointCallback nearest_callback;
BVHTree_RayCastCallback raycast_callback;
- /* Vertex array, so that callbacks have instante access to data */
+ /* Vertex array, so that callbacks have instant access to data. */
const struct MVert *vert;
const struct MEdge *edge; /* only used for BVHTreeFromMeshEdges */
const struct MFace *face;
diff --git a/source/blender/blenkernel/BKE_cachefile.h b/source/blender/blenkernel/BKE_cachefile.h
index a6b2aa8540a..836597f95da 100644
--- a/source/blender/blenkernel/BKE_cachefile.h
+++ b/source/blender/blenkernel/BKE_cachefile.h
@@ -32,6 +32,7 @@ struct CacheReader;
struct Depsgraph;
struct Main;
struct Object;
+struct Scene;
void BKE_cachefiles_init(void);
void BKE_cachefiles_exit(void);
@@ -60,6 +61,10 @@ void BKE_cachefile_reader_open(struct CacheFile *cache_file,
const char *object_path);
void BKE_cachefile_reader_free(struct CacheFile *cache_file, struct CacheReader **reader);
+bool BKE_cache_file_uses_render_procedural(const struct CacheFile *cache_file,
+ struct Scene *scene,
+ const int dag_eval_mode);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_collection.h b/source/blender/blenkernel/BKE_collection.h
index 0326386e5c1..2c7143be60e 100644
--- a/source/blender/blenkernel/BKE_collection.h
+++ b/source/blender/blenkernel/BKE_collection.h
@@ -65,7 +65,7 @@ void BKE_collection_add_from_collection(struct Main *bmain,
struct Scene *scene,
struct Collection *collection_src,
struct Collection *collection_dst);
-void BKE_collection_free(struct Collection *collection);
+void BKE_collection_free_data(struct Collection *collection);
bool BKE_collection_delete(struct Main *bmain, struct Collection *collection, bool hierarchy);
struct Collection *BKE_collection_duplicate(struct Main *bmain,
@@ -197,13 +197,14 @@ typedef void (*BKE_scene_collections_Cb)(struct Collection *ob, void *data);
#define FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN(_collection, _object, _mode) \
{ \
int _base_flag = (_mode == DAG_EVAL_VIEWPORT) ? BASE_ENABLED_VIEWPORT : BASE_ENABLED_RENDER; \
- int _object_restrict_flag = (_mode == DAG_EVAL_VIEWPORT) ? OB_RESTRICT_VIEWPORT : \
- OB_RESTRICT_RENDER; \
+ int _object_visibility_flag = (_mode == DAG_EVAL_VIEWPORT) ? OB_HIDE_VIEWPORT : \
+ OB_HIDE_RENDER; \
int _base_id = 0; \
for (Base *_base = (Base *)BKE_collection_object_cache_get(_collection).first; _base; \
_base = _base->next, _base_id++) { \
Object *_object = _base->object; \
- if ((_base->flag & _base_flag) && (_object->restrictflag & _object_restrict_flag) == 0) {
+ if ((_base->flag & _base_flag) && \
+ (_object->visibility_flag & _object_visibility_flag) == 0) {
#define FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END \
} \
diff --git a/source/blender/blenkernel/BKE_constraint.h b/source/blender/blenkernel/BKE_constraint.h
index 575df93a9fc..784b395dfa5 100644
--- a/source/blender/blenkernel/BKE_constraint.h
+++ b/source/blender/blenkernel/BKE_constraint.h
@@ -192,6 +192,28 @@ bool BKE_constraint_remove_ex(ListBase *list,
bool clear_dep);
bool BKE_constraint_remove(ListBase *list, struct bConstraint *con);
+bool BKE_constraint_apply_for_object(struct Depsgraph *depsgraph,
+ struct Scene *scene,
+ struct Object *ob,
+ struct bConstraint *con);
+bool BKE_constraint_apply_and_remove_for_object(struct Depsgraph *depsgraph,
+ struct Scene *scene,
+ ListBase /*bConstraint*/ *constraints,
+ struct Object *ob,
+ struct bConstraint *con);
+
+bool BKE_constraint_apply_for_pose(struct Depsgraph *depsgraph,
+ struct Scene *scene,
+ struct Object *ob,
+ struct bPoseChannel *pchan,
+ struct bConstraint *con);
+bool BKE_constraint_apply_and_remove_for_pose(struct Depsgraph *depsgraph,
+ struct Scene *scene,
+ ListBase /*bConstraint*/ *constraints,
+ struct Object *ob,
+ struct bConstraint *con,
+ struct bPoseChannel *pchan);
+
void BKE_constraint_panel_expand(struct bConstraint *con);
/* Constraints + Proxies function prototypes */
diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h
index 8917580689d..eda6a03fa1a 100644
--- a/source/blender/blenkernel/BKE_context.h
+++ b/source/blender/blenkernel/BKE_context.h
@@ -360,7 +360,7 @@ int CTX_data_visible_gpencil_layers(const bContext *C, ListBase *list);
int CTX_data_editable_gpencil_layers(const bContext *C, ListBase *list);
int CTX_data_editable_gpencil_strokes(const bContext *C, ListBase *list);
-const struct AssetLibraryReference *CTX_wm_asset_library(const bContext *C);
+const struct AssetLibraryReference *CTX_wm_asset_library_ref(const bContext *C);
struct AssetHandle CTX_wm_asset_handle(const bContext *C, bool *r_is_valid);
bool CTX_wm_interface_locked(const bContext *C);
diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h
index 69c950a86dc..89713e9ad0a 100644
--- a/source/blender/blenkernel/BKE_global.h
+++ b/source/blender/blenkernel/BKE_global.h
@@ -70,6 +70,7 @@ typedef struct Global {
* * -16384 and below: Reserved for python (add-ons) usage.
* * -1: Disable faster motion paths computation (since 08/2018).
* * 1 - 30: EEVEE debug/stats values (01/2018).
+ * * 31: Enable the Select Debug Engine. Only available with #WITH_DRAW_DEBUG (08/2021).
* * 101: Enable UI debug drawing of fullscreen area's corner widget (10/2014).
* * 666: Use quicker batch delete for outliners' delete hierarchy (01/2019).
* * 777: Enable UI node panel's sockets polling (11/2011).
@@ -144,7 +145,8 @@ enum {
G_DEBUG_DEPSGRAPH_TIME = (1 << 11), /* depsgraph timing statistics and messages */
G_DEBUG_DEPSGRAPH_NO_THREADS = (1 << 12), /* single threaded depsgraph */
G_DEBUG_DEPSGRAPH_PRETTY = (1 << 13), /* use pretty colors in depsgraph messages */
- G_DEBUG_DEPSGRAPH_UUID = (1 << 14), /* use pretty colors in depsgraph messages */
+ G_DEBUG_DEPSGRAPH_UUID = (1 << 14), /* Verify validness of session-wide identifiers
+ * assigned to ID datablocks */
G_DEBUG_DEPSGRAPH = (G_DEBUG_DEPSGRAPH_BUILD | G_DEBUG_DEPSGRAPH_EVAL | G_DEBUG_DEPSGRAPH_TAG |
G_DEBUG_DEPSGRAPH_TIME | G_DEBUG_DEPSGRAPH_UUID),
G_DEBUG_SIMDATA = (1 << 15), /* sim debug data display */
diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h
index 92e70b41e7b..b58317f4815 100644
--- a/source/blender/blenkernel/BKE_gpencil.h
+++ b/source/blender/blenkernel/BKE_gpencil.h
@@ -93,7 +93,7 @@ void BKE_gpencil_free_stroke(struct bGPDstroke *gps);
bool BKE_gpencil_free_strokes(struct bGPDframe *gpf);
void BKE_gpencil_free_frames(struct bGPDlayer *gpl);
void BKE_gpencil_free_layers(struct ListBase *list);
-void BKE_gpencil_free(struct bGPdata *gpd, bool free_all);
+void BKE_gpencil_free_data(struct bGPdata *gpd, bool free_all);
void BKE_gpencil_eval_delete(struct bGPdata *gpd_eval);
void BKE_gpencil_free_layer_masks(struct bGPDlayer *gpl);
void BKE_gpencil_tag(struct bGPdata *gpd);
diff --git a/source/blender/blenkernel/BKE_gpencil_geom.h b/source/blender/blenkernel/BKE_gpencil_geom.h
index c1ccae7a437..29e3a74b1b2 100644
--- a/source/blender/blenkernel/BKE_gpencil_geom.h
+++ b/source/blender/blenkernel/BKE_gpencil_geom.h
@@ -169,7 +169,8 @@ bool BKE_gpencil_convert_mesh(struct Main *bmain,
const float matrix[4][4],
const int frame_offset,
const bool use_seams,
- const bool use_faces);
+ const bool use_faces,
+ const bool use_vgroups);
void BKE_gpencil_stroke_uniform_subdivide(struct bGPdata *gpd,
struct bGPDstroke *gps,
diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h
index d298e5dcf6d..b62ad3ad24a 100644
--- a/source/blender/blenkernel/BKE_image.h
+++ b/source/blender/blenkernel/BKE_image.h
@@ -45,7 +45,7 @@ struct StampData;
struct anim;
#define IMA_MAX_SPACE 64
-#define IMA_UDIM_MAX 1999
+#define IMA_UDIM_MAX 2000
void BKE_images_init(void);
void BKE_images_exit(void);
@@ -56,7 +56,7 @@ void BKE_image_free_buffers(struct Image *image);
void BKE_image_free_buffers_ex(struct Image *image, bool do_lock);
void BKE_image_free_gputextures(struct Image *ima);
/* call from library */
-void BKE_image_free(struct Image *image);
+void BKE_image_free_data(struct Image *image);
typedef void(StampCallback)(void *data, const char *propname, char *propvalue, int len);
@@ -308,6 +308,8 @@ void BKE_image_get_tile_label(struct Image *ima,
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);
+void BKE_image_reassign_tile(struct Image *ima, struct ImageTile *tile, int new_tile_number);
+void BKE_image_sort_tiles(struct Image *ima);
bool BKE_image_fill_tile(struct Image *ima,
struct ImageTile *tile,
diff --git a/source/blender/blenkernel/BKE_key.h b/source/blender/blenkernel/BKE_key.h
index 70d65e02246..cb4fc607703 100644
--- a/source/blender/blenkernel/BKE_key.h
+++ b/source/blender/blenkernel/BKE_key.h
@@ -36,7 +36,7 @@ struct Object;
extern "C" {
#endif
-void BKE_key_free(struct Key *key);
+void BKE_key_free_data(struct Key *key);
void BKE_key_free_nolib(struct Key *key);
struct Key *BKE_key_add(struct Main *bmain, struct ID *id);
void BKE_key_sort(struct Key *key);
diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h
index fac5dc8c010..bb875f8d1c9 100644
--- a/source/blender/blenkernel/BKE_lib_id.h
+++ b/source/blender/blenkernel/BKE_lib_id.h
@@ -152,8 +152,6 @@ void BKE_libblock_copy_ex(struct Main *bmain,
const int orig_flag);
void *BKE_libblock_copy(struct Main *bmain, const struct ID *id) ATTR_WARN_UNUSED_RESULT
ATTR_NONNULL();
-/* Special version: used by data-block localization. */
-void *BKE_libblock_copy_for_localize(const struct ID *id);
void BKE_libblock_rename(struct Main *bmain, struct ID *id, const char *name) ATTR_NONNULL();
void BLI_libblock_ensure_unique_name(struct Main *bmain, const char *name) ATTR_NONNULL();
@@ -201,6 +199,8 @@ enum {
void BKE_libblock_free_datablock(struct ID *id, const int flag) ATTR_NONNULL();
void BKE_libblock_free_data(struct ID *id, const bool do_id_user) ATTR_NONNULL();
+void BKE_libblock_free_data_py(struct ID *id);
+
void BKE_id_free_ex(struct Main *bmain, void *idv, int flag, const bool use_flag_from_idtag);
void BKE_id_free(struct Main *bmain, void *idv);
diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h
index e3be9cd8ef8..ae464a48e9e 100644
--- a/source/blender/blenkernel/BKE_mesh.h
+++ b/source/blender/blenkernel/BKE_mesh.h
@@ -95,7 +95,7 @@ void BKE_mesh_looptri_get_real_edges(const struct Mesh *mesh,
const struct MLoopTri *looptri,
int r_edges[3]);
-void BKE_mesh_free(struct Mesh *me);
+void BKE_mesh_free_data_for_undo(struct Mesh *me);
void BKE_mesh_clear_geometry(struct Mesh *me);
struct Mesh *BKE_mesh_add(struct Main *bmain, const char *name);
void BKE_mesh_copy_parameters_for_eval(struct Mesh *me_dst, const struct Mesh *me_src);
@@ -280,39 +280,22 @@ void BKE_mesh_recalc_looptri_with_normals(const struct MLoop *mloop,
/* *** mesh_normals.cc *** */
-void BKE_mesh_calc_normals_mapping_simple(struct Mesh *me);
-void BKE_mesh_calc_normals_mapping(struct MVert *mverts,
- int numVerts,
- const struct MLoop *mloop,
- const struct MPoly *mpolys,
- int numLoops,
- int numPolys,
- float (*r_polyNors)[3],
- const struct MFace *mfaces,
- int numFaces,
- const int *origIndexFace,
- float (*r_faceNors)[3]);
-void BKE_mesh_calc_normals_mapping_ex(struct MVert *mverts,
- int numVerts,
- const struct MLoop *mloop,
- const struct MPoly *mpolys,
- int numLoops,
- int numPolys,
- float (*r_polyNors)[3],
- const struct MFace *mfaces,
- int numFaces,
- const int *origIndexFace,
- float (*r_faceNors)[3],
- const bool only_face_normals);
-void BKE_mesh_calc_normals_poly(struct MVert *mverts,
- float (*r_vertnors)[3],
- int numVerts,
+void BKE_mesh_normals_tag_dirty(struct Mesh *mesh);
+void BKE_mesh_calc_normals_poly(const struct MVert *mvert,
+ int mvert_len,
const struct MLoop *mloop,
- const struct MPoly *mpolys,
- int numLoops,
- int numPolys,
- float (*r_polyNors)[3],
- const bool only_face_normals);
+ int mloop_len,
+ const struct MPoly *mpoly,
+ int mpoly_len,
+ float (*r_poly_normals)[3]);
+void BKE_mesh_calc_normals_poly_and_vertex(struct MVert *mvert,
+ int mvert_len,
+ const struct MLoop *mloop,
+ int mloop_len,
+ const struct MPoly *mpolys,
+ int mpoly_len,
+ float (*r_poly_normals)[3],
+ float (*r_vert_normals)[3]);
void BKE_mesh_calc_normals(struct Mesh *me);
void BKE_mesh_ensure_normals(struct Mesh *me);
void BKE_mesh_ensure_normals_for_display(struct Mesh *mesh);
diff --git a/source/blender/blenkernel/BKE_mesh_remesh_voxel.h b/source/blender/blenkernel/BKE_mesh_remesh_voxel.h
index 5887db59ff2..80ced9b5f57 100644
--- a/source/blender/blenkernel/BKE_mesh_remesh_voxel.h
+++ b/source/blender/blenkernel/BKE_mesh_remesh_voxel.h
@@ -23,10 +23,6 @@
* \ingroup bke
*/
-#ifdef WITH_OPENVDB
-# include "openvdb_capi.h"
-#endif
-
#ifdef __cplusplus
extern "C" {
#endif
diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h
index 0b4e1191956..8be563e4c96 100644
--- a/source/blender/blenkernel/BKE_modifier.h
+++ b/source/blender/blenkernel/BKE_modifier.h
@@ -319,8 +319,10 @@ typedef struct ModifierTypeInfo {
* changes.
*
* This function is optional (assumes false if not present).
+ *
+ * The dag_eval_mode should be of type eEvaluationMode.
*/
- bool (*dependsOnTime)(struct ModifierData *md);
+ bool (*dependsOnTime)(struct Scene *scene, struct ModifierData *md, const int dag_eval_mode);
/**
* True when a deform modifier uses normals, the requiredDataMask
@@ -425,7 +427,7 @@ void BKE_modifier_copydata(struct ModifierData *md, struct ModifierData *target)
void BKE_modifier_copydata_ex(struct ModifierData *md,
struct ModifierData *target,
const int flag);
-bool BKE_modifier_depends_ontime(struct ModifierData *md);
+bool BKE_modifier_depends_ontime(struct Scene *scene, struct ModifierData *md, int dag_eval_mode);
bool BKE_modifier_supports_mapping(struct ModifierData *md);
bool BKE_modifier_supports_cage(struct Scene *scene, struct ModifierData *md);
bool BKE_modifier_couldbe_cage(struct Scene *scene, struct ModifierData *md);
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index cecb3118038..c4393246926 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -111,8 +111,7 @@ typedef struct bNodeSocketTemplate {
#ifdef __cplusplus
namespace blender {
namespace nodes {
-class SocketMFNetworkBuilder;
-class NodeMFNetworkBuilder;
+class NodeMultiFunctionBuilder;
class GeoNodeExecParams;
} // namespace nodes
namespace fn {
@@ -121,17 +120,20 @@ class MFDataType;
} // namespace fn
} // namespace blender
-using NodeExpandInMFNetworkFunction = void (*)(blender::nodes::NodeMFNetworkBuilder &builder);
+using NodeMultiFunctionBuildFunction = void (*)(blender::nodes::NodeMultiFunctionBuilder &builder);
using NodeGeometryExecFunction = void (*)(blender::nodes::GeoNodeExecParams params);
using SocketGetCPPTypeFunction = const blender::fn::CPPType *(*)();
using SocketGetCPPValueFunction = void (*)(const struct bNodeSocket &socket, void *r_value);
-using SocketExpandInMFNetworkFunction = void (*)(blender::nodes::SocketMFNetworkBuilder &builder);
+using SocketGetGeometryNodesCPPTypeFunction = const blender::fn::CPPType *(*)();
+using SocketGetGeometryNodesCPPValueFunction = void (*)(const struct bNodeSocket &socket,
+ void *r_value);
#else
-typedef void *NodeExpandInMFNetworkFunction;
-typedef void *SocketExpandInMFNetworkFunction;
+typedef void *NodeMultiFunctionBuildFunction;
typedef void *NodeGeometryExecFunction;
typedef void *SocketGetCPPTypeFunction;
+typedef void *SocketGetGeometryNodesCPPTypeFunction;
+typedef void *SocketGetGeometryNodesCPPValueFunction;
typedef void *SocketGetCPPValueFunction;
#endif
@@ -191,12 +193,14 @@ typedef struct bNodeSocketType {
/* Callback to free the socket type. */
void (*free_self)(struct bNodeSocketType *stype);
- /* Expands the socket into a multi-function node that outputs the socket value. */
- SocketExpandInMFNetworkFunction expand_in_mf_network;
/* Return the CPPType of this socket. */
- SocketGetCPPTypeFunction get_cpp_type;
+ SocketGetCPPTypeFunction get_base_cpp_type;
/* Get the value of this socket in a generic way. */
- SocketGetCPPValueFunction get_cpp_value;
+ SocketGetCPPValueFunction get_base_cpp_value;
+ /* Get geometry nodes cpp type. */
+ SocketGetGeometryNodesCPPTypeFunction get_geometry_nodes_cpp_type;
+ /* Get geometry nodes cpp value. */
+ SocketGetGeometryNodesCPPValueFunction get_geometry_nodes_cpp_value;
} bNodeSocketType;
typedef void *(*NodeInitExecFunction)(struct bNodeExecContext *context,
@@ -323,8 +327,8 @@ typedef struct bNodeType {
/* gpu */
NodeGPUExecFunction gpu_fn;
- /* Expands the bNode into nodes in a multi-function network, which will be evaluated later on. */
- NodeExpandInMFNetworkFunction expand_in_mf_network;
+ /* Build a multi-function for this node. */
+ NodeMultiFunctionBuildFunction build_multi_function;
/* Execute a geometry node. */
NodeGeometryExecFunction geometry_node_execute;
@@ -1466,6 +1470,8 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL 1070
#define GEO_NODE_CURVE_TRIM 1071
#define GEO_NODE_CURVE_SET_HANDLES 1072
+#define GEO_NODE_CURVE_SPLINE_TYPE 1073
+#define GEO_NODE_CURVE_SELECT_HANDLES 1074
/** \} */
diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h
index 4724e6dfab6..a823602e341 100644
--- a/source/blender/blenkernel/BKE_object.h
+++ b/source/blender/blenkernel/BKE_object.h
@@ -401,7 +401,10 @@ void BKE_object_groups_clear(struct Main *bmain, struct Scene *scene, struct Obj
struct KDTree_3d *BKE_object_as_kdtree(struct Object *ob, int *r_tot);
-bool BKE_object_modifier_use_time(struct Object *ob, struct ModifierData *md);
+bool BKE_object_modifier_use_time(struct Scene *scene,
+ struct Object *ob,
+ struct ModifierData *md,
+ int dag_eval_mode);
bool BKE_object_modifier_update_subframe(struct Depsgraph *depsgraph,
struct Scene *scene,
diff --git a/source/blender/blenkernel/BKE_ocean.h b/source/blender/blenkernel/BKE_ocean.h
index 2c0c6989acf..380f9045520 100644
--- a/source/blender/blenkernel/BKE_ocean.h
+++ b/source/blender/blenkernel/BKE_ocean.h
@@ -74,11 +74,13 @@ struct Ocean *BKE_ocean_add(void);
void BKE_ocean_free_data(struct Ocean *oc);
void BKE_ocean_free(struct Ocean *oc);
bool BKE_ocean_ensure(struct OceanModifierData *omd, const int resolution);
-void BKE_ocean_init_from_modifier(struct Ocean *ocean,
+bool BKE_ocean_init_from_modifier(struct Ocean *ocean,
struct OceanModifierData const *omd,
const int resolution);
-void BKE_ocean_init(struct Ocean *o,
+bool BKE_ocean_is_valid(const struct Ocean *o);
+
+bool BKE_ocean_init(struct Ocean *o,
int M,
int N,
float Lx,
diff --git a/source/blender/blenkernel/BKE_pointcache.h b/source/blender/blenkernel/BKE_pointcache.h
index 3f99a0dc793..c83fca767a1 100644
--- a/source/blender/blenkernel/BKE_pointcache.h
+++ b/source/blender/blenkernel/BKE_pointcache.h
@@ -302,7 +302,7 @@ void BKE_ptcache_remove(void);
/************ ID specific functions ************************/
void BKE_ptcache_id_clear(PTCacheID *id, int mode, unsigned int cfra);
-int BKE_ptcache_id_exist(PTCacheID *id, int cfra);
+bool BKE_ptcache_id_exist(PTCacheID *id, int cfra);
int BKE_ptcache_id_reset(struct Scene *scene, PTCacheID *id, int mode);
void BKE_ptcache_id_time(PTCacheID *pid,
struct Scene *scene,
diff --git a/source/blender/blenkernel/BKE_scene.h b/source/blender/blenkernel/BKE_scene.h
index 6d58e165ea3..83ce5e72794 100644
--- a/source/blender/blenkernel/BKE_scene.h
+++ b/source/blender/blenkernel/BKE_scene.h
@@ -174,6 +174,10 @@ bool BKE_scene_uses_blender_eevee(const struct Scene *scene);
bool BKE_scene_uses_blender_workbench(const struct Scene *scene);
bool BKE_scene_uses_cycles(const struct Scene *scene);
+/* Return whether the Cycles experimental feature is enabled. It is invalid to call without first
+ * ensuring that Cycles is the active render engine (e.g. with BKE_scene_uses_cycles). */
+bool BKE_scene_uses_cycles_experimental_features(struct Scene *scene);
+
void BKE_scene_copy_data_eevee(struct Scene *sce_dst, const struct Scene *sce_src);
void BKE_scene_disable_color_management(struct Scene *scene);
diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h
index 0b08bbfeff5..6f341a12b82 100644
--- a/source/blender/blenkernel/BKE_screen.h
+++ b/source/blender/blenkernel/BKE_screen.h
@@ -473,7 +473,7 @@ void BKE_screen_view3d_shading_init(struct View3DShading *shading);
/* screen */
void BKE_screen_foreach_id_screen_area(struct LibraryForeachIDData *data, struct ScrArea *area);
-void BKE_screen_free(struct bScreen *screen);
+void BKE_screen_free_data(struct bScreen *screen);
void BKE_screen_area_map_free(struct ScrAreaMap *area_map) ATTR_NONNULL();
struct ScrEdge *BKE_screen_find_edge(const struct bScreen *screen,
diff --git a/source/blender/blenkernel/BKE_sound.h b/source/blender/blenkernel/BKE_sound.h
index 57ce33a239f..4b257b3b8ab 100644
--- a/source/blender/blenkernel/BKE_sound.h
+++ b/source/blender/blenkernel/BKE_sound.h
@@ -97,6 +97,7 @@ typedef struct SoundInfo {
eSoundChannels channels;
} specs;
float length;
+ double start_offset;
} SoundInfo;
/* Get information about given sound. Returns truth on success., false if sound can not be loaded
@@ -139,8 +140,12 @@ void BKE_sound_remove_scene_sound(struct Scene *scene, void *handle);
void BKE_sound_mute_scene_sound(void *handle, char mute);
-void BKE_sound_move_scene_sound(
- struct Scene *scene, void *handle, int startframe, int endframe, int frameskip);
+void BKE_sound_move_scene_sound(struct Scene *scene,
+ void *handle,
+ int startframe,
+ int endframe,
+ int frameskip,
+ double audio_offset);
void BKE_sound_move_scene_sound_defaults(struct Scene *scene, struct Sequence *sequence);
void BKE_sound_update_scene_sound(void *handle, struct bSound *sound);
diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh
index 53485ecbd6b..fc145f1ddf1 100644
--- a/source/blender/blenkernel/BKE_spline.hh
+++ b/source/blender/blenkernel/BKE_spline.hh
@@ -109,6 +109,7 @@ class Spline {
SplinePtr copy() const;
SplinePtr copy_only_settings() const;
SplinePtr copy_without_attributes() const;
+ static void copy_base_settings(const Spline &src, Spline &dst);
Spline::Type type() const;
@@ -209,8 +210,6 @@ class Spline {
virtual void correct_end_tangents() const = 0;
virtual void copy_settings(Spline &dst) const = 0;
virtual void copy_data(Spline &dst) const = 0;
-
- static void copy_base_settings(const Spline &src, Spline &dst);
};
/**
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index 78bfe8c9afb..adaef22d5bc 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -698,6 +698,13 @@ if(WITH_ALEMBIC)
add_definitions(-DWITH_ALEMBIC)
endif()
+if(WITH_USD)
+ list(APPEND INC
+ ../io/usd
+ )
+ add_definitions(-DWITH_USD)
+endif()
+
if(WITH_OPENSUBDIV)
list(APPEND INC_SYS
${OPENSUBDIV_INCLUDE_DIRS}
diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc
index ba8cf8debe9..59e81938e79 100644
--- a/source/blender/blenkernel/intern/DerivedMesh.cc
+++ b/source/blender/blenkernel/intern/DerivedMesh.cc
@@ -360,7 +360,7 @@ void DM_init(DerivedMesh *dm,
dm->needsFree = 1;
dm->dirty = (DMDirtyFlag)0;
- /* Don't use CustomData_reset(...); because we don't want to touch custom-data. */
+ /* Don't use #CustomData_reset because we don't want to touch custom-data. */
copy_vn_i(dm->vertData.typemap, CD_NUMTYPES, -1);
copy_vn_i(dm->edgeData.typemap, CD_NUMTYPES, -1);
copy_vn_i(dm->faceData.typemap, CD_NUMTYPES, -1);
@@ -816,15 +816,14 @@ static void mesh_calc_modifier_final_normals(const Mesh *mesh_input,
if (!CustomData_has_layer(&mesh_final->pdata, CD_NORMAL)) {
float(*polynors)[3] = (float(*)[3])CustomData_add_layer(
&mesh_final->pdata, CD_NORMAL, CD_CALLOC, nullptr, mesh_final->totpoly);
- BKE_mesh_calc_normals_poly(mesh_final->mvert,
- nullptr,
- mesh_final->totvert,
- mesh_final->mloop,
- mesh_final->mpoly,
- mesh_final->totloop,
- mesh_final->totpoly,
- polynors,
- false);
+ BKE_mesh_calc_normals_poly_and_vertex(mesh_final->mvert,
+ mesh_final->totvert,
+ mesh_final->mloop,
+ mesh_final->totloop,
+ mesh_final->mpoly,
+ mesh_final->totpoly,
+ polynors,
+ nullptr);
}
}
@@ -1536,15 +1535,14 @@ static void editbmesh_calc_modifier_final_normals(Mesh *mesh_final,
if (!CustomData_has_layer(&mesh_final->pdata, CD_NORMAL)) {
float(*polynors)[3] = (float(*)[3])CustomData_add_layer(
&mesh_final->pdata, CD_NORMAL, CD_CALLOC, nullptr, mesh_final->totpoly);
- BKE_mesh_calc_normals_poly(mesh_final->mvert,
- nullptr,
- mesh_final->totvert,
- mesh_final->mloop,
- mesh_final->mpoly,
- mesh_final->totloop,
- mesh_final->totpoly,
- polynors,
- false);
+ BKE_mesh_calc_normals_poly_and_vertex(mesh_final->mvert,
+ mesh_final->totvert,
+ mesh_final->mloop,
+ mesh_final->totloop,
+ mesh_final->mpoly,
+ mesh_final->totpoly,
+ polynors,
+ nullptr);
}
}
diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c
index d55f023d209..981815f400a 100644
--- a/source/blender/blenkernel/intern/action.c
+++ b/source/blender/blenkernel/intern/action.c
@@ -186,22 +186,21 @@ static void action_foreach_id(ID *id, LibraryForeachIDData *data)
static void action_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
bAction *act = (bAction *)id;
- if (act->id.us > 0 || BLO_write_is_undo(writer)) {
- BLO_write_id_struct(writer, bAction, id_address, &act->id);
- BKE_id_blend_write(writer, &act->id);
- BKE_fcurve_blend_write(writer, &act->curves);
+ BLO_write_id_struct(writer, bAction, id_address, &act->id);
+ BKE_id_blend_write(writer, &act->id);
- LISTBASE_FOREACH (bActionGroup *, grp, &act->groups) {
- BLO_write_struct(writer, bActionGroup, grp);
- }
+ BKE_fcurve_blend_write(writer, &act->curves);
- LISTBASE_FOREACH (TimeMarker *, marker, &act->markers) {
- BLO_write_struct(writer, TimeMarker, marker);
- }
+ LISTBASE_FOREACH (bActionGroup *, grp, &act->groups) {
+ BLO_write_struct(writer, bActionGroup, grp);
+ }
- BKE_previewimg_blend_write(writer, act->preview);
+ LISTBASE_FOREACH (TimeMarker *, marker, &act->markers) {
+ BLO_write_struct(writer, TimeMarker, marker);
}
+
+ BKE_previewimg_blend_write(writer, act->preview);
}
static void action_blend_read_data(BlendDataReader *reader, ID *id)
diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c
index fae75762cde..92b0db5b214 100644
--- a/source/blender/blenkernel/intern/anim_sys.c
+++ b/source/blender/blenkernel/intern/anim_sys.c
@@ -426,7 +426,7 @@ bool BKE_animsys_rna_path_resolve(PointerRNA *ptr,
}
/* less than 1.0 evaluates to false, use epsilon to avoid float error */
-#define ANIMSYS_FLOAT_AS_BOOL(value) ((value) > ((1.0f - FLT_EPSILON)))
+#define ANIMSYS_FLOAT_AS_BOOL(value) ((value) > (1.0f - FLT_EPSILON))
bool BKE_animsys_read_from_rna_path(PathResolvedRNA *anim_rna, float *r_value)
{
@@ -2625,7 +2625,7 @@ static void animsys_create_action_track_strip(const AnimData *adt,
bAction *action = adt->action;
- if ((adt->flag & ADT_NLA_EDIT_ON)) {
+ if (adt->flag & ADT_NLA_EDIT_ON) {
action = adt->tmpact;
}
@@ -2671,7 +2671,7 @@ static void animsys_create_action_track_strip(const AnimData *adt,
static bool is_nlatrack_evaluatable(const AnimData *adt, const NlaTrack *nlt)
{
/* Skip disabled tracks unless it contains the tweaked strip. */
- const bool contains_tweak_strip = (adt->flag & ADT_NLA_EDIT_ON) &&
+ const bool contains_tweak_strip = (adt->flag & ADT_NLA_EDIT_ON) && adt->act_track &&
(nlt->index == adt->act_track->index);
if ((nlt->flag & NLATRACK_DISABLED) && !contains_tweak_strip) {
return false;
diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c
index b8ed519e8d1..87320c88b1b 100644
--- a/source/blender/blenkernel/intern/armature.c
+++ b/source/blender/blenkernel/intern/armature.c
@@ -212,25 +212,24 @@ static void write_bone(BlendWriter *writer, Bone *bone)
static void armature_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
bArmature *arm = (bArmature *)id;
- if (arm->id.us > 0 || BLO_write_is_undo(writer)) {
- /* Clean up, important in undo case to reduce false detection of changed datablocks. */
- arm->bonehash = NULL;
- arm->edbo = NULL;
- /* Must always be cleared (armatures don't have their own edit-data). */
- arm->needs_flush_to_id = 0;
- arm->act_edbone = NULL;
- BLO_write_id_struct(writer, bArmature, id_address, &arm->id);
- BKE_id_blend_write(writer, &arm->id);
+ /* Clean up, important in undo case to reduce false detection of changed datablocks. */
+ arm->bonehash = NULL;
+ arm->edbo = NULL;
+ /* Must always be cleared (armatures don't have their own edit-data). */
+ arm->needs_flush_to_id = 0;
+ arm->act_edbone = NULL;
- if (arm->adt) {
- BKE_animdata_blend_write(writer, arm->adt);
- }
+ BLO_write_id_struct(writer, bArmature, id_address, &arm->id);
+ BKE_id_blend_write(writer, &arm->id);
- /* Direct data */
- LISTBASE_FOREACH (Bone *, bone, &arm->bonebase) {
- write_bone(writer, bone);
- }
+ if (arm->adt) {
+ BKE_animdata_blend_write(writer, arm->adt);
+ }
+
+ /* Direct data */
+ LISTBASE_FOREACH (Bone *, bone, &arm->bonebase) {
+ write_bone(writer, bone);
}
}
@@ -1882,7 +1881,7 @@ void BKE_bone_parent_transform_invert(struct BoneParentTransform *bpt)
{
invert_m4(bpt->rotscale_mat);
invert_m4(bpt->loc_mat);
- invert_v3(bpt->post_scale);
+ invert_v3_safe(bpt->post_scale);
}
void BKE_bone_parent_transform_combine(const struct BoneParentTransform *in1,
@@ -2663,7 +2662,7 @@ void BKE_pose_rebuild(Main *bmain, Object *ob, bArmature *arm, const bool do_id_
}
}
- /* printf("rebuild pose %s, %d bones\n", ob->id.name, counter); */
+ // printf("rebuild pose %s, %d bones\n", ob->id.name, counter);
/* synchronize protected layers with proxy */
/* HACK! To preserve 2.7x behavior that you always can pose even locked bones,
diff --git a/source/blender/blenkernel/intern/armature_test.cc b/source/blender/blenkernel/intern/armature_test.cc
index 47853deec3e..99eb064d061 100644
--- a/source/blender/blenkernel/intern/armature_test.cc
+++ b/source/blender/blenkernel/intern/armature_test.cc
@@ -180,7 +180,7 @@ class BKE_armature_find_selected_bones_test : public testing::Test {
BLI_addtail(&arm.bonebase, &bone2); // bone2 is root bone
BLI_addtail(&bone2.childbase, &bone3); // bone3 has bone2 as parent
- // Make sure the armature & its bones are visible, to make them selectable.
+ /* Make sure the armature & its bones are visible, to make them selectable. */
arm.layer = bone1.layer = bone2.layer = bone3.layer = 1;
}
};
@@ -200,8 +200,8 @@ TEST_F(BKE_armature_find_selected_bones_test, some_bones_selected)
EXPECT_EQ(seen_bones[0], &bone1);
EXPECT_EQ(seen_bones[1], &bone3);
- EXPECT_FALSE(result.all_bones_selected); // Bone 2 was not selected.
- EXPECT_FALSE(result.no_bones_selected); // Bones 1 and 3 were selected.
+ EXPECT_FALSE(result.all_bones_selected); /* Bone 2 was not selected. */
+ EXPECT_FALSE(result.no_bones_selected); /* Bones 1 and 3 were selected. */
}
TEST_F(BKE_armature_find_selected_bones_test, no_bones_selected)
diff --git a/source/blender/blenkernel/intern/autoexec.c b/source/blender/blenkernel/intern/autoexec.c
index 07b096af941..3aec646b024 100644
--- a/source/blender/blenkernel/intern/autoexec.c
+++ b/source/blender/blenkernel/intern/autoexec.c
@@ -56,7 +56,7 @@ bool BKE_autoexec_match(const char *path)
if (path_cmp->path[0] == '\0') {
/* pass */
}
- else if ((path_cmp->flag & USER_PATHCMP_GLOB)) {
+ else if (path_cmp->flag & USER_PATHCMP_GLOB) {
if (fnmatch(path_cmp->path, path, fnmatch_flags) == 0) {
return true;
}
diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c
index 61827be08e5..1c5d8804280 100644
--- a/source/blender/blenkernel/intern/blendfile.c
+++ b/source/blender/blenkernel/intern/blendfile.c
@@ -660,10 +660,6 @@ UserDef *BKE_blendfile_userdef_from_defaults(void)
BKE_studiolight_default(userdef->light_param, userdef->light_ambient);
BKE_preferences_asset_library_default_add(userdef);
- /* Enable asset browser features by default for alpha testing.
- * BLO_sanitize_experimental_features_userpref_blend() will disable it again for non-alpha
- * builds. */
- userdef->experimental.use_asset_browser = true;
return userdef;
}
diff --git a/source/blender/blenkernel/intern/boids.c b/source/blender/blenkernel/intern/boids.c
index 9caf416cd0c..a7257133821 100644
--- a/source/blender/blenkernel/intern/boids.c
+++ b/source/blender/blenkernel/intern/boids.c
@@ -342,10 +342,7 @@ static bool rule_avoid_collision(BoidRule *rule,
}
}
}
- if (ptn) {
- MEM_freeN(ptn);
- ptn = NULL;
- }
+ MEM_SAFE_FREE(ptn);
/* check boids in other systems */
for (pt = bbd->sim->psys->targets.first; pt; pt = pt->next) {
@@ -401,10 +398,7 @@ static bool rule_avoid_collision(BoidRule *rule,
}
}
- if (ptn) {
- MEM_freeN(ptn);
- ptn = NULL;
- }
+ MEM_SAFE_FREE(ptn);
}
}
@@ -435,10 +429,7 @@ static bool rule_separate(BoidRule *UNUSED(rule),
len = ptn[1].dist;
ret = 1;
}
- if (ptn) {
- MEM_freeN(ptn);
- ptn = NULL;
- }
+ MEM_SAFE_FREE(ptn);
/* check other boid systems */
for (pt = bbd->sim->psys->targets.first; pt; pt = pt->next) {
@@ -457,10 +448,7 @@ static bool rule_separate(BoidRule *UNUSED(rule),
ret = true;
}
- if (ptn) {
- MEM_freeN(ptn);
- ptn = NULL;
- }
+ MEM_SAFE_FREE(ptn);
}
}
return ret;
@@ -723,10 +711,7 @@ static bool rule_fight(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, Part
f_strength += bbd->part->boids->strength * health;
- if (ptn) {
- MEM_freeN(ptn);
- ptn = NULL;
- }
+ MEM_SAFE_FREE(ptn);
/* add other friendlies and calculate enemy strength and find closest enemy */
for (pt = bbd->sim->psys->targets.first; pt; pt = pt->next) {
@@ -755,10 +740,7 @@ static bool rule_fight(BoidRule *rule, BoidBrainData *bbd, BoidValues *val, Part
f_strength += epsys->part->boids->strength * health;
}
- if (ptn) {
- MEM_freeN(ptn);
- ptn = NULL;
- }
+ MEM_SAFE_FREE(ptn);
}
}
/* decide action if enemy presence found */
diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c
index abf7bab7612..7b81187be21 100644
--- a/source/blender/blenkernel/intern/brush.c
+++ b/source/blender/blenkernel/intern/brush.c
@@ -202,49 +202,48 @@ static void brush_foreach_id(ID *id, LibraryForeachIDData *data)
static void brush_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
Brush *brush = (Brush *)id;
- if (brush->id.us > 0 || BLO_write_is_undo(writer)) {
- BLO_write_id_struct(writer, Brush, id_address, &brush->id);
- BKE_id_blend_write(writer, &brush->id);
- if (brush->curve) {
- BKE_curvemapping_blend_write(writer, brush->curve);
- }
+ BLO_write_id_struct(writer, Brush, id_address, &brush->id);
+ BKE_id_blend_write(writer, &brush->id);
- if (brush->gpencil_settings) {
- BLO_write_struct(writer, BrushGpencilSettings, brush->gpencil_settings);
+ if (brush->curve) {
+ BKE_curvemapping_blend_write(writer, brush->curve);
+ }
- if (brush->gpencil_settings->curve_sensitivity) {
- BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_sensitivity);
- }
- if (brush->gpencil_settings->curve_strength) {
- BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_strength);
- }
- if (brush->gpencil_settings->curve_jitter) {
- BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_jitter);
- }
- if (brush->gpencil_settings->curve_rand_pressure) {
- BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_pressure);
- }
- if (brush->gpencil_settings->curve_rand_strength) {
- BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_strength);
- }
- if (brush->gpencil_settings->curve_rand_uv) {
- BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_uv);
- }
- if (brush->gpencil_settings->curve_rand_hue) {
- BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_hue);
- }
- if (brush->gpencil_settings->curve_rand_saturation) {
- BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_saturation);
- }
- if (brush->gpencil_settings->curve_rand_value) {
- BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_value);
- }
+ if (brush->gpencil_settings) {
+ BLO_write_struct(writer, BrushGpencilSettings, brush->gpencil_settings);
+
+ if (brush->gpencil_settings->curve_sensitivity) {
+ BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_sensitivity);
+ }
+ if (brush->gpencil_settings->curve_strength) {
+ BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_strength);
+ }
+ if (brush->gpencil_settings->curve_jitter) {
+ BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_jitter);
+ }
+ if (brush->gpencil_settings->curve_rand_pressure) {
+ BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_pressure);
+ }
+ if (brush->gpencil_settings->curve_rand_strength) {
+ BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_strength);
+ }
+ if (brush->gpencil_settings->curve_rand_uv) {
+ BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_uv);
+ }
+ if (brush->gpencil_settings->curve_rand_hue) {
+ BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_hue);
+ }
+ if (brush->gpencil_settings->curve_rand_saturation) {
+ BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_saturation);
}
- if (brush->gradient) {
- BLO_write_struct(writer, ColorBand, brush->gradient);
+ if (brush->gpencil_settings->curve_rand_value) {
+ BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_value);
}
}
+ if (brush->gradient) {
+ BLO_write_struct(writer, ColorBand, brush->gradient);
+ }
}
static void brush_blend_read_data(BlendDataReader *reader, ID *id)
@@ -660,10 +659,7 @@ static void brush_gpencil_curvemap_reset(CurveMap *cuma, int tot, int preset)
break;
}
- if (cuma->table) {
- MEM_freeN(cuma->table);
- cuma->table = NULL;
- }
+ MEM_SAFE_FREE(cuma->table);
}
void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type)
diff --git a/source/blender/blenkernel/intern/cachefile.c b/source/blender/blenkernel/intern/cachefile.c
index eaba5d33a20..87b1584d422 100644
--- a/source/blender/blenkernel/intern/cachefile.c
+++ b/source/blender/blenkernel/intern/cachefile.c
@@ -49,12 +49,18 @@
#include "DEG_depsgraph_query.h"
+#include "RE_engine.h"
+
#include "BLO_read_write.h"
#ifdef WITH_ALEMBIC
# include "ABC_alembic.h"
#endif
+#ifdef WITH_USD
+# include "usd.h"
+#endif
+
static void cachefile_handle_free(CacheFile *cache_file);
static void cache_file_init_data(ID *id)
@@ -91,19 +97,18 @@ static void cache_file_free_data(ID *id)
static void cache_file_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
CacheFile *cache_file = (CacheFile *)id;
- if (cache_file->id.us > 0 || BLO_write_is_undo(writer)) {
- /* Clean up, important in undo case to reduce false detection of changed datablocks. */
- BLI_listbase_clear(&cache_file->object_paths);
- cache_file->handle = NULL;
- memset(cache_file->handle_filepath, 0, sizeof(cache_file->handle_filepath));
- cache_file->handle_readers = NULL;
- BLO_write_id_struct(writer, CacheFile, id_address, &cache_file->id);
- BKE_id_blend_write(writer, &cache_file->id);
+ /* Clean up, important in undo case to reduce false detection of changed datablocks. */
+ BLI_listbase_clear(&cache_file->object_paths);
+ cache_file->handle = NULL;
+ memset(cache_file->handle_filepath, 0, sizeof(cache_file->handle_filepath));
+ cache_file->handle_readers = NULL;
+
+ BLO_write_id_struct(writer, CacheFile, id_address, &cache_file->id);
+ BKE_id_blend_write(writer, &cache_file->id);
- if (cache_file->adt) {
- BKE_animdata_blend_write(writer, cache_file->adt);
- }
+ if (cache_file->adt) {
+ BKE_animdata_blend_write(writer, cache_file->adt);
}
}
@@ -166,15 +171,30 @@ void BKE_cachefile_reader_open(CacheFile *cache_file,
Object *object,
const char *object_path)
{
-#ifdef WITH_ALEMBIC
+#if defined(WITH_ALEMBIC) || defined(WITH_USD)
+
BLI_assert(cache_file->id.tag & LIB_TAG_COPIED_ON_WRITE);
if (cache_file->handle == NULL) {
return;
}
- /* Open Alembic cache reader. */
- *reader = CacheReader_open_alembic_object(cache_file->handle, *reader, object, object_path);
+ switch (cache_file->type) {
+ case CACHEFILE_TYPE_ALEMBIC:
+# ifdef WITH_ALEMBIC
+ /* Open Alembic cache reader. */
+ *reader = CacheReader_open_alembic_object(cache_file->handle, *reader, object, object_path);
+# endif
+ break;
+ case CACHEFILE_TYPE_USD:
+# ifdef WITH_USD
+ /* Open USD cache reader. */
+ *reader = CacheReader_open_usd_object(cache_file->handle, *reader, object, object_path);
+# endif
+ break;
+ case CACHE_FILE_TYPE_INVALID:
+ break;
+ }
/* Multiple modifiers and constraints can call this function concurrently. */
BLI_spin_lock(&spin);
@@ -197,16 +217,30 @@ void BKE_cachefile_reader_open(CacheFile *cache_file,
void BKE_cachefile_reader_free(CacheFile *cache_file, struct CacheReader **reader)
{
-#ifdef WITH_ALEMBIC
+#if defined(WITH_ALEMBIC) || defined(WITH_USD)
/* Multiple modifiers and constraints can call this function concurrently, and
* cachefile_handle_free() can also be called at the same time. */
BLI_spin_lock(&spin);
if (*reader != NULL) {
if (cache_file) {
BLI_assert(cache_file->id.tag & LIB_TAG_COPIED_ON_WRITE);
+
+ switch (cache_file->type) {
+ case CACHEFILE_TYPE_ALEMBIC:
+# ifdef WITH_ALEMBIC
+ ABC_CacheReader_free(*reader);
+# endif
+ break;
+ case CACHEFILE_TYPE_USD:
+# ifdef WITH_USD
+ USD_CacheReader_free(*reader);
+# endif
+ break;
+ case CACHE_FILE_TYPE_INVALID:
+ break;
+ }
}
- CacheReader_free(*reader);
*reader = NULL;
if (cache_file && cache_file->handle_readers) {
@@ -221,7 +255,8 @@ void BKE_cachefile_reader_free(CacheFile *cache_file, struct CacheReader **reade
static void cachefile_handle_free(CacheFile *cache_file)
{
-#ifdef WITH_ALEMBIC
+#if defined(WITH_ALEMBIC) || defined(WITH_USD)
+
/* Free readers in all modifiers and constraints that use the handle, before
* we free the handle itself. */
BLI_spin_lock(&spin);
@@ -230,7 +265,21 @@ static void cachefile_handle_free(CacheFile *cache_file)
GSET_ITER (gs_iter, cache_file->handle_readers) {
struct CacheReader **reader = BLI_gsetIterator_getKey(&gs_iter);
if (*reader != NULL) {
- CacheReader_free(*reader);
+ switch (cache_file->type) {
+ case CACHEFILE_TYPE_ALEMBIC:
+# ifdef WITH_ALEMBIC
+ ABC_CacheReader_free(*reader);
+# endif
+ break;
+ case CACHEFILE_TYPE_USD:
+# ifdef WITH_USD
+ USD_CacheReader_free(*reader);
+# endif
+ break;
+ case CACHE_FILE_TYPE_INVALID:
+ break;
+ }
+
*reader = NULL;
}
}
@@ -242,7 +291,22 @@ static void cachefile_handle_free(CacheFile *cache_file)
/* Free handle. */
if (cache_file->handle) {
- ABC_free_handle(cache_file->handle);
+
+ switch (cache_file->type) {
+ case CACHEFILE_TYPE_ALEMBIC:
+# ifdef WITH_ALEMBIC
+ ABC_free_handle(cache_file->handle);
+# endif
+ break;
+ case CACHEFILE_TYPE_USD:
+# ifdef WITH_USD
+ USD_free_handle(cache_file->handle);
+# endif
+ break;
+ case CACHE_FILE_TYPE_INVALID:
+ break;
+ }
+
cache_file->handle = NULL;
}
@@ -289,12 +353,22 @@ void BKE_cachefile_eval(Main *bmain, Depsgraph *depsgraph, CacheFile *cache_file
BLI_freelistN(&cache_file->object_paths);
#ifdef WITH_ALEMBIC
- cache_file->handle = ABC_create_handle(bmain, filepath, &cache_file->object_paths);
- BLI_strncpy(cache_file->handle_filepath, filepath, FILE_MAX);
+ if (BLI_path_extension_check_glob(filepath, "*abc")) {
+ cache_file->type = CACHEFILE_TYPE_ALEMBIC;
+ cache_file->handle = ABC_create_handle(bmain, filepath, &cache_file->object_paths);
+ BLI_strncpy(cache_file->handle_filepath, filepath, FILE_MAX);
+ }
+#endif
+#ifdef WITH_USD
+ if (BLI_path_extension_check_glob(filepath, "*.usd;*.usda;*.usdc")) {
+ cache_file->type = CACHEFILE_TYPE_USD;
+ cache_file->handle = USD_create_handle(bmain, filepath, &cache_file->object_paths);
+ BLI_strncpy(cache_file->handle_filepath, filepath, FILE_MAX);
+ }
#endif
if (DEG_is_active(depsgraph)) {
- /* Flush object paths back to original datablock for UI. */
+ /* Flush object paths back to original data-block for UI. */
CacheFile *cache_file_orig = (CacheFile *)DEG_get_original_id(&cache_file->id);
BLI_freelistN(&cache_file_orig->object_paths);
BLI_duplicatelist(&cache_file_orig->object_paths, &cache_file->object_paths);
@@ -336,3 +410,25 @@ float BKE_cachefile_time_offset(const CacheFile *cache_file, const float time, c
const float frame = (cache_file->override_frame ? cache_file->frame : time);
return cache_file->is_sequence ? frame : frame / fps - time_offset;
}
+
+/**
+ * Determine whether the #CacheFile should use a render engine procedural. If so, data is not read
+ * from the file and bounding boxes are used to represent the objects in the Scene.
+ * Render engines will receive the bounding box as a placeholder but can instead
+ * load the data directly if they support it.
+ */
+bool BKE_cache_file_uses_render_procedural(const CacheFile *cache_file,
+ Scene *scene,
+ const int dag_eval_mode)
+{
+ RenderEngineType *render_engine_type = RE_engines_find(scene->r.engine);
+
+ if (cache_file->type != CACHEFILE_TYPE_ALEMBIC ||
+ !RE_engine_supports_alembic_procedural(render_engine_type, scene)) {
+ return false;
+ }
+
+ /* The render time procedural is only enabled during viewport rendering. */
+ const bool is_final_render = (eEvaluationMode)dag_eval_mode == DAG_EVAL_RENDER;
+ return cache_file->use_render_procedural && !is_final_render;
+}
diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c
index 5172b067eba..46b079fb42e 100644
--- a/source/blender/blenkernel/intern/camera.c
+++ b/source/blender/blenkernel/intern/camera.c
@@ -122,18 +122,17 @@ static void camera_foreach_id(ID *id, LibraryForeachIDData *data)
static void camera_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
Camera *cam = (Camera *)id;
- if (cam->id.us > 0 || BLO_write_is_undo(writer)) {
- /* write LibData */
- BLO_write_id_struct(writer, Camera, id_address, &cam->id);
- BKE_id_blend_write(writer, &cam->id);
- if (cam->adt) {
- BKE_animdata_blend_write(writer, cam->adt);
- }
+ /* write LibData */
+ BLO_write_id_struct(writer, Camera, id_address, &cam->id);
+ BKE_id_blend_write(writer, &cam->id);
- LISTBASE_FOREACH (CameraBGImage *, bgpic, &cam->bg_images) {
- BLO_write_struct(writer, CameraBGImage, bgpic);
- }
+ if (cam->adt) {
+ BKE_animdata_blend_write(writer, cam->adt);
+ }
+
+ LISTBASE_FOREACH (CameraBGImage *, bgpic, &cam->bg_images) {
+ BLO_write_struct(writer, CameraBGImage, bgpic);
}
}
diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c
index 0fa58a74f2b..080a7c90c46 100644
--- a/source/blender/blenkernel/intern/cloth.c
+++ b/source/blender/blenkernel/intern/cloth.c
@@ -42,6 +42,7 @@
#include "BKE_cloth.h"
#include "BKE_effect.h"
#include "BKE_global.h"
+#include "BKE_lib_id.h"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_modifier.h"
@@ -447,11 +448,7 @@ void cloth_free_modifier(ClothModifierData *clmd)
SIM_cloth_solver_free(clmd);
/* Free the verts. */
- if (cloth->verts != NULL) {
- MEM_freeN(cloth->verts);
- }
-
- cloth->verts = NULL;
+ MEM_SAFE_FREE(cloth->verts);
cloth->mvert_num = 0;
/* Free the springs. */
@@ -529,11 +526,7 @@ void cloth_free_modifier_extern(ClothModifierData *clmd)
SIM_cloth_solver_free(clmd);
/* Free the verts. */
- if (cloth->verts != NULL) {
- MEM_freeN(cloth->verts);
- }
-
- cloth->verts = NULL;
+ MEM_SAFE_FREE(cloth->verts);
cloth->mvert_num = 0;
/* Free the springs. */
@@ -1582,7 +1575,7 @@ static bool cloth_build_springs(ClothModifierData *clmd, Mesh *mesh)
BLI_edgeset_free(existing_vert_pairs);
free_bvhtree_from_mesh(&treedata);
if (tmp_mesh) {
- BKE_mesh_free(tmp_mesh);
+ BKE_id_free(NULL, &tmp_mesh->id);
}
return false;
}
@@ -1591,7 +1584,7 @@ static bool cloth_build_springs(ClothModifierData *clmd, Mesh *mesh)
BLI_edgeset_free(existing_vert_pairs);
free_bvhtree_from_mesh(&treedata);
if (tmp_mesh) {
- BKE_mesh_free(tmp_mesh);
+ BKE_id_free(NULL, &tmp_mesh->id);
}
BLI_rng_free(rng);
}
diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c
index b90214f1814..d36e9b67d00 100644
--- a/source/blender/blenkernel/intern/collection.c
+++ b/source/blender/blenkernel/intern/collection.c
@@ -214,20 +214,19 @@ void BKE_collection_blend_write_nolib(BlendWriter *writer, Collection *collectio
static void collection_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
Collection *collection = (Collection *)id;
- if (collection->id.us > 0 || BLO_write_is_undo(writer)) {
- /* Clean up, important in undo case to reduce false detection of changed data-blocks. */
- collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE;
- collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE_INSTANCED;
- collection->tag = 0;
- BLI_listbase_clear(&collection->object_cache);
- BLI_listbase_clear(&collection->object_cache_instanced);
- BLI_listbase_clear(&collection->parents);
- /* write LibData */
- BLO_write_id_struct(writer, Collection, id_address, &collection->id);
+ /* Clean up, important in undo case to reduce false detection of changed data-blocks. */
+ collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE;
+ collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE_INSTANCED;
+ collection->tag = 0;
+ BLI_listbase_clear(&collection->object_cache);
+ BLI_listbase_clear(&collection->object_cache_instanced);
+ BLI_listbase_clear(&collection->parents);
- BKE_collection_blend_write_nolib(writer, collection);
- }
+ /* write LibData */
+ BLO_write_id_struct(writer, Collection, id_address, &collection->id);
+
+ BKE_collection_blend_write_nolib(writer, collection);
}
#ifdef USE_COLLECTION_COMPAT_28
@@ -508,7 +507,7 @@ void BKE_collection_add_from_collection(Main *bmain,
* \{ */
/** Free (or release) any data used by this collection (does not free the collection itself). */
-void BKE_collection_free(Collection *collection)
+void BKE_collection_free_data(Collection *collection)
{
BKE_libblock_free_data(&collection->id, false);
collection_free_data(&collection->id);
@@ -806,10 +805,10 @@ static void collection_object_cache_fill(ListBase *lb,
/* Only collection flags are checked here currently, object restrict flag is checked
* in FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN since it can be animated
* without updating the cache. */
- if (((child_restrict & COLLECTION_RESTRICT_VIEWPORT) == 0)) {
+ if (((child_restrict & COLLECTION_HIDE_VIEWPORT) == 0)) {
base->flag |= BASE_ENABLED_VIEWPORT;
}
- if (((child_restrict & COLLECTION_RESTRICT_RENDER) == 0)) {
+ if (((child_restrict & COLLECTION_HIDE_RENDER) == 0)) {
base->flag |= BASE_ENABLED_RENDER;
}
}
@@ -1755,7 +1754,7 @@ static bool collection_objects_select(ViewLayer *view_layer, Collection *collect
{
bool changed = false;
- if (collection->flag & COLLECTION_RESTRICT_SELECT) {
+ if (collection->flag & COLLECTION_HIDE_SELECT) {
return false;
}
diff --git a/source/blender/blenkernel/intern/colortools.c b/source/blender/blenkernel/intern/colortools.c
index a9f0f69b855..f2c2e552a9f 100644
--- a/source/blender/blenkernel/intern/colortools.c
+++ b/source/blender/blenkernel/intern/colortools.c
@@ -1716,22 +1716,10 @@ void BKE_scopes_update(Scopes *scopes,
void BKE_scopes_free(Scopes *scopes)
{
- if (scopes->waveform_1) {
- MEM_freeN(scopes->waveform_1);
- scopes->waveform_1 = NULL;
- }
- if (scopes->waveform_2) {
- MEM_freeN(scopes->waveform_2);
- scopes->waveform_2 = NULL;
- }
- if (scopes->waveform_3) {
- MEM_freeN(scopes->waveform_3);
- scopes->waveform_3 = NULL;
- }
- if (scopes->vecscope) {
- MEM_freeN(scopes->vecscope);
- scopes->vecscope = NULL;
- }
+ MEM_SAFE_FREE(scopes->waveform_1);
+ MEM_SAFE_FREE(scopes->waveform_2);
+ MEM_SAFE_FREE(scopes->waveform_3);
+ MEM_SAFE_FREE(scopes->vecscope);
}
void BKE_scopes_new(Scopes *scopes)
diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c
index 47df31e3a2c..30aa22387d0 100644
--- a/source/blender/blenkernel/intern/constraint.c
+++ b/source/blender/blenkernel/intern/constraint.c
@@ -95,6 +95,10 @@
# include "ABC_alembic.h"
#endif
+#ifdef WITH_USD
+# include "usd.h"
+#endif
+
/* ---------------------------------------------------------------------------- */
/* Useful macros for testing various common flag combinations */
@@ -2978,6 +2982,16 @@ static void actcon_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *targ
if (VALID_CONS_TARGET(ct) || data->flag & ACTCON_USE_EVAL_TIME) {
switch (data->mix_mode) {
+ /* Simple matrix multiplication. */
+ case ACTCON_MIX_BEFORE_FULL:
+ mul_m4_m4m4(cob->matrix, ct->matrix, cob->matrix);
+ break;
+
+ case ACTCON_MIX_AFTER_FULL:
+ mul_m4_m4m4(cob->matrix, cob->matrix, ct->matrix);
+ break;
+
+ /* Aligned Inherit Scale emulation. */
case ACTCON_MIX_BEFORE:
mul_m4_m4m4_aligned_scale(cob->matrix, ct->matrix, cob->matrix);
break;
@@ -2986,8 +3000,13 @@ static void actcon_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *targ
mul_m4_m4m4_aligned_scale(cob->matrix, cob->matrix, ct->matrix);
break;
- case ACTCON_MIX_AFTER_FULL:
- mul_m4_m4m4(cob->matrix, cob->matrix, ct->matrix);
+ /* Fully separate handling of channels. */
+ case ACTCON_MIX_BEFORE_SPLIT:
+ mul_m4_m4m4_split_channels(cob->matrix, ct->matrix, cob->matrix);
+ break;
+
+ case ACTCON_MIX_AFTER_SPLIT:
+ mul_m4_m4m4_split_channels(cob->matrix, cob->matrix, ct->matrix);
break;
default:
@@ -4612,9 +4631,7 @@ static void splineik_free(bConstraint *con)
bSplineIKConstraint *data = con->data;
/* binding array */
- if (data->points) {
- MEM_freeN(data->points);
- }
+ MEM_SAFE_FREE(data->points);
}
static void splineik_copy(bConstraint *con, bConstraint *srccon)
@@ -5403,7 +5420,7 @@ static void transformcache_id_looper(bConstraint *con, ConstraintIDFunc func, vo
static void transformcache_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *targets)
{
-#ifdef WITH_ALEMBIC
+#if defined(WITH_ALEMBIC) || defined(WITH_USD)
bTransformCacheConstraint *data = con->data;
Scene *scene = cob->scene;
@@ -5413,6 +5430,11 @@ static void transformcache_evaluate(bConstraint *con, bConstraintOb *cob, ListBa
return;
}
+ /* Do not process data if using a render time procedural. */
+ if (BKE_cache_file_uses_render_procedural(cache_file, scene, DEG_get_mode(cob->depsgraph))) {
+ return;
+ }
+
const float frame = DEG_get_ctime(cob->depsgraph);
const float time = BKE_cachefile_time_offset(cache_file, frame, FPS);
@@ -5421,7 +5443,20 @@ static void transformcache_evaluate(bConstraint *con, bConstraintOb *cob, ListBa
BKE_cachefile_reader_open(cache_file, &data->reader, cob->ob, data->object_path);
}
- ABC_get_transform(data->reader, cob->matrix, time, cache_file->scale);
+ switch (cache_file->type) {
+ case CACHEFILE_TYPE_ALEMBIC:
+# ifdef WITH_ALEMBIC
+ ABC_get_transform(data->reader, cob->matrix, time, cache_file->scale);
+# endif
+ break;
+ case CACHEFILE_TYPE_USD:
+# ifdef WITH_USD
+ USD_get_transform(data->reader, cob->matrix, time * FPS, cache_file->scale);
+# endif
+ break;
+ case CACHE_FILE_TYPE_INVALID:
+ break;
+ }
#else
UNUSED_VARS(con, cob);
#endif
@@ -5647,6 +5682,111 @@ bool BKE_constraint_remove_ex(ListBase *list, Object *ob, bConstraint *con, bool
return false;
}
+/* Apply the specified constraint in the given constraint stack */
+bool BKE_constraint_apply_for_object(Depsgraph *depsgraph,
+ Scene *scene,
+ Object *ob,
+ bConstraint *con)
+{
+ if (!con) {
+ return false;
+ }
+
+ const float ctime = BKE_scene_frame_get(scene);
+
+ bConstraint *new_con = BKE_constraint_duplicate_ex(con, 0, !ID_IS_LINKED(ob));
+ ListBase single_con = {new_con, new_con};
+
+ bConstraintOb *cob = BKE_constraints_make_evalob(
+ depsgraph, scene, ob, NULL, CONSTRAINT_OBTYPE_OBJECT);
+ /* Undo the effect of the current constraint stack evaluation. */
+ mul_m4_m4m4(cob->matrix, ob->constinv, cob->matrix);
+
+ /* Evaluate single constraint. */
+ BKE_constraints_solve(depsgraph, &single_con, cob, ctime);
+ /* Copy transforms back. This will leave the object in a bad state
+ * as ob->constinv will be wrong until next evaluation. */
+ BKE_constraints_clear_evalob(cob);
+
+ /* Free the copied constraint. */
+ BKE_constraint_free_data(new_con);
+ BLI_freelinkN(&single_con, new_con);
+
+ /* Apply transform from matrix. */
+ BKE_object_apply_mat4(ob, ob->obmat, true, true);
+
+ return true;
+}
+
+bool BKE_constraint_apply_and_remove_for_object(Depsgraph *depsgraph,
+ Scene *scene,
+ ListBase /*bConstraint*/ *constraints,
+ Object *ob,
+ bConstraint *con)
+{
+ if (!BKE_constraint_apply_for_object(depsgraph, scene, ob, con)) {
+ return false;
+ }
+
+ return BKE_constraint_remove_ex(constraints, ob, con, true);
+}
+
+bool BKE_constraint_apply_for_pose(
+ Depsgraph *depsgraph, Scene *scene, Object *ob, bPoseChannel *pchan, bConstraint *con)
+{
+ if (!con) {
+ return false;
+ }
+
+ const float ctime = BKE_scene_frame_get(scene);
+
+ bConstraint *new_con = BKE_constraint_duplicate_ex(con, 0, !ID_IS_LINKED(ob));
+ ListBase single_con;
+ single_con.first = new_con;
+ single_con.last = new_con;
+
+ float vec[3];
+ copy_v3_v3(vec, pchan->pose_mat[3]);
+
+ bConstraintOb *cob = BKE_constraints_make_evalob(
+ depsgraph, scene, ob, pchan, CONSTRAINT_OBTYPE_BONE);
+ /* Undo the effects of currently applied constraints. */
+ mul_m4_m4m4(cob->matrix, pchan->constinv, cob->matrix);
+ /* Evaluate single constraint. */
+ BKE_constraints_solve(depsgraph, &single_con, cob, ctime);
+ BKE_constraints_clear_evalob(cob);
+
+ /* Free the copied constraint. */
+ BKE_constraint_free_data(new_con);
+ BLI_freelinkN(&single_con, new_con);
+
+ /* Prevent constraints breaking a chain. */
+ if (pchan->bone->flag & BONE_CONNECTED) {
+ copy_v3_v3(pchan->pose_mat[3], vec);
+ }
+
+ /* Apply transform from matrix. */
+ float mat[4][4];
+ BKE_armature_mat_pose_to_bone(pchan, pchan->pose_mat, mat);
+ BKE_pchan_apply_mat4(pchan, mat, true);
+
+ return true;
+}
+
+bool BKE_constraint_apply_and_remove_for_pose(Depsgraph *depsgraph,
+ Scene *scene,
+ ListBase /*bConstraint*/ *constraints,
+ Object *ob,
+ bConstraint *con,
+ bPoseChannel *pchan)
+{
+ if (!BKE_constraint_apply_for_pose(depsgraph, scene, ob, pchan, con)) {
+ return false;
+ }
+
+ return BKE_constraint_remove_ex(constraints, ob, con, true);
+}
+
void BKE_constraint_panel_expand(bConstraint *con)
{
con->ui_expand_flag |= UI_PANEL_DATA_EXPAND_ROOT;
@@ -5752,6 +5892,17 @@ static bConstraint *add_new_constraint(Object *ob,
}
break;
}
+ case CONSTRAINT_TYPE_ACTION: {
+ /* The Before or Split modes require computing in local space, but
+ * for objects the Local space doesn't make sense (T78462, D6095 etc).
+ * So only default to Before (Split) if the constraint is on a bone. */
+ if (pchan) {
+ bActionConstraint *data = con->data;
+ data->mix_mode = ACTCON_MIX_BEFORE_SPLIT;
+ con->ownspace = CONSTRAINT_SPACE_LOCAL;
+ }
+ break;
+ }
}
return con;
diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c
index dced945bea0..7763bb9ca08 100644
--- a/source/blender/blenkernel/intern/context.c
+++ b/source/blender/blenkernel/intern/context.c
@@ -1448,9 +1448,9 @@ int CTX_data_editable_gpencil_strokes(const bContext *C, ListBase *list)
return ctx_data_collection_get(C, "editable_gpencil_strokes", list);
}
-const AssetLibraryReference *CTX_wm_asset_library(const bContext *C)
+const AssetLibraryReference *CTX_wm_asset_library_ref(const bContext *C)
{
- return ctx_data_pointer_get(C, "asset_library");
+ return ctx_data_pointer_get(C, "asset_library_ref");
}
AssetHandle CTX_wm_asset_handle(const bContext *C, bool *r_is_valid)
diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c
index f1369254347..397838e6904 100644
--- a/source/blender/blenkernel/intern/curve.c
+++ b/source/blender/blenkernel/intern/curve.c
@@ -146,51 +146,50 @@ static void curve_foreach_id(ID *id, LibraryForeachIDData *data)
static void curve_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
Curve *cu = (Curve *)id;
- if (cu->id.us > 0 || BLO_write_is_undo(writer)) {
- /* Clean up, important in undo case to reduce false detection of changed datablocks. */
- cu->editnurb = NULL;
- cu->editfont = NULL;
- cu->batch_cache = NULL;
- /* write LibData */
- BLO_write_id_struct(writer, Curve, id_address, &cu->id);
- BKE_id_blend_write(writer, &cu->id);
+ /* Clean up, important in undo case to reduce false detection of changed datablocks. */
+ cu->editnurb = NULL;
+ cu->editfont = NULL;
+ cu->batch_cache = NULL;
+
+ /* write LibData */
+ BLO_write_id_struct(writer, Curve, id_address, &cu->id);
+ BKE_id_blend_write(writer, &cu->id);
- /* direct data */
- BLO_write_pointer_array(writer, cu->totcol, cu->mat);
- if (cu->adt) {
- BKE_animdata_blend_write(writer, cu->adt);
- }
+ /* direct data */
+ BLO_write_pointer_array(writer, cu->totcol, cu->mat);
+ if (cu->adt) {
+ BKE_animdata_blend_write(writer, cu->adt);
+ }
- if (cu->vfont) {
- BLO_write_raw(writer, cu->len + 1, cu->str);
- BLO_write_struct_array(writer, CharInfo, cu->len_char32 + 1, cu->strinfo);
- BLO_write_struct_array(writer, TextBox, cu->totbox, cu->tb);
+ if (cu->vfont) {
+ BLO_write_raw(writer, cu->len + 1, cu->str);
+ BLO_write_struct_array(writer, CharInfo, cu->len_char32 + 1, cu->strinfo);
+ BLO_write_struct_array(writer, TextBox, cu->totbox, cu->tb);
+ }
+ else {
+ /* is also the order of reading */
+ LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) {
+ BLO_write_struct(writer, Nurb, nu);
}
- else {
- /* is also the order of reading */
- LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) {
- BLO_write_struct(writer, Nurb, nu);
+ LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) {
+ if (nu->type == CU_BEZIER) {
+ BLO_write_struct_array(writer, BezTriple, nu->pntsu, nu->bezt);
}
- LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) {
- if (nu->type == CU_BEZIER) {
- BLO_write_struct_array(writer, BezTriple, nu->pntsu, nu->bezt);
+ else {
+ BLO_write_struct_array(writer, BPoint, nu->pntsu * nu->pntsv, nu->bp);
+ if (nu->knotsu) {
+ BLO_write_float_array(writer, KNOTSU(nu), nu->knotsu);
}
- else {
- BLO_write_struct_array(writer, BPoint, nu->pntsu * nu->pntsv, nu->bp);
- if (nu->knotsu) {
- BLO_write_float_array(writer, KNOTSU(nu), nu->knotsu);
- }
- if (nu->knotsv) {
- BLO_write_float_array(writer, KNOTSV(nu), nu->knotsv);
- }
+ if (nu->knotsv) {
+ BLO_write_float_array(writer, KNOTSV(nu), nu->knotsv);
}
}
}
+ }
- if (cu->bevel_profile != NULL) {
- BKE_curveprofile_blend_write(writer, cu->bevel_profile);
- }
+ if (cu->bevel_profile != NULL) {
+ BKE_curveprofile_blend_write(writer, cu->bevel_profile);
}
}
@@ -638,7 +637,7 @@ void BKE_nurb_free(Nurb *nu)
MEM_freeN(nu->knotsv);
}
nu->knotsv = NULL;
- /* if (nu->trim.first) freeNurblist(&(nu->trim)); */
+ // if (nu->trim.first) freeNurblist(&(nu->trim));
MEM_freeN(nu);
}
@@ -2331,17 +2330,21 @@ static void make_bevel_list_3D_minimum_twist(BevList *bl)
bevp1 = bevp2 + (bl->nr - 1);
bevp0 = bevp1 - 1;
- nr = bl->nr;
- while (nr--) {
+ /* The ordinal of the point being adjusted (bevp2). First point is 1. */
- if (nr + 3 > bl->nr) { /* first time and second time, otherwise first point adjusts last */
- vec_to_quat(bevp1->quat, bevp1->dir, 5, 1);
- }
- else {
- minimum_twist_between_two_points(bevp1, bevp0);
- }
+ /* First point is the reference, don't adjust.
+ * Skip this point in the following loop. */
+ if (bl->nr > 0) {
+ vec_to_quat(bevp2->quat, bevp2->dir, 5, 1);
- bevp0 = bevp1;
+ bevp0 = bevp1; /* bevp0 is unused */
+ bevp1 = bevp2;
+ bevp2++;
+ }
+ for (nr = 1; nr < bl->nr; nr++) {
+ minimum_twist_between_two_points(bevp2, bevp1);
+
+ bevp0 = bevp1; /* bevp0 is unused */
bevp1 = bevp2;
bevp2++;
}
@@ -5010,10 +5013,7 @@ bool BKE_nurb_type_convert(Nurb *nu,
MEM_freeN(nu->knotsu); /* python created nurbs have a knotsu of zero */
}
nu->knotsu = NULL;
- if (nu->knotsv) {
- MEM_freeN(nu->knotsv);
- }
- nu->knotsv = NULL;
+ MEM_SAFE_FREE(nu->knotsv);
}
else if (type == CU_BEZIER) { /* to Bezier */
nr = nu->pntsu / 3;
diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c
index 7aa9d1958eb..1a3200a9b6c 100644
--- a/source/blender/blenkernel/intern/customdata.c
+++ b/source/blender/blenkernel/intern/customdata.c
@@ -724,10 +724,7 @@ static void layerFree_grid_paint_mask(void *data, int count, int UNUSED(size))
GridPaintMask *gpm = data;
for (int i = 0; i < count; i++) {
- if (gpm[i].data) {
- MEM_freeN(gpm[i].data);
- }
- gpm[i].data = NULL;
+ MEM_SAFE_FREE(gpm[i].data);
gpm[i].level = 0;
}
}
@@ -4249,7 +4246,7 @@ void CustomData_blend_write_prepare(CustomData *data,
CustomDataLayer *layer = &data->layers[i];
if (layer->flag & CD_FLAG_NOCOPY) { /* Layers with this flag set are not written to file. */
data->totlayer--;
- /* CLOG_WARN(&LOG, "skipping layer %p (%s)", layer, layer->name); */
+ // CLOG_WARN(&LOG, "skipping layer %p (%s)", layer, layer->name);
}
else {
if (UNLIKELY((size_t)j >= write_layers_size)) {
diff --git a/source/blender/blenkernel/intern/data_transfer.c b/source/blender/blenkernel/intern/data_transfer.c
index 605061570b8..b83621e8b79 100644
--- a/source/blender/blenkernel/intern/data_transfer.c
+++ b/source/blender/blenkernel/intern/data_transfer.c
@@ -301,14 +301,12 @@ static void data_transfer_dtdata_type_preprocess(Mesh *me_src,
}
if (dirty_nors_dst || do_poly_nors_dst) {
BKE_mesh_calc_normals_poly(verts_dst,
- NULL,
num_verts_dst,
loops_dst,
- polys_dst,
num_loops_dst,
+ polys_dst,
num_polys_dst,
- poly_nors_dst,
- true);
+ poly_nors_dst);
}
/* Cache loop nors into a temp CDLayer. */
loop_nors_dst = CustomData_get_layer(ldata_dst, CD_NORMAL);
diff --git a/source/blender/blenkernel/intern/deform.c b/source/blender/blenkernel/intern/deform.c
index f7ef84728b6..13222747a52 100644
--- a/source/blender/blenkernel/intern/deform.c
+++ b/source/blender/blenkernel/intern/deform.c
@@ -894,10 +894,7 @@ void BKE_defvert_remove_group(MDeformVert *dvert, MDeformWeight *dw)
void BKE_defvert_clear(MDeformVert *dvert)
{
- if (dvert->dw) {
- MEM_freeN(dvert->dw);
- dvert->dw = NULL;
- }
+ MEM_SAFE_FREE(dvert->dw);
dvert->totweight = 0;
}
diff --git a/source/blender/blenkernel/intern/displist.cc b/source/blender/blenkernel/intern/displist.cc
index 99dc1db9d38..c97e07ad487 100644
--- a/source/blender/blenkernel/intern/displist.cc
+++ b/source/blender/blenkernel/intern/displist.cc
@@ -1003,7 +1003,7 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph,
modified = temp_mesh;
BKE_mesh_vert_coords_apply(modified, vertCos);
- BKE_mesh_calc_normals_mapping_simple(modified);
+ BKE_mesh_calc_normals(modified);
MEM_freeN(vertCos);
}
diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c
index 52996e3bcc7..8f94c407cae 100644
--- a/source/blender/blenkernel/intern/dynamicpaint.c
+++ b/source/blender/blenkernel/intern/dynamicpaint.c
@@ -844,10 +844,7 @@ static void surfaceGenerateGrid(struct DynamicPaintSurface *surface)
if (temp_s_num) {
MEM_freeN(temp_s_num);
}
- if (temp_t_index) {
- MEM_freeN(temp_t_index);
- }
- grid->temp_t_index = NULL;
+ MEM_SAFE_FREE(temp_t_index);
if (error || !grid->s_num) {
setError(surface->canvas, N_("Not enough free memory"));
@@ -988,10 +985,7 @@ void dynamicPaint_freeSurface(const DynamicPaintModifierData *pmd, DynamicPaintS
}
surface->pointcache = NULL;
- if (surface->effector_weights) {
- MEM_freeN(surface->effector_weights);
- }
- surface->effector_weights = NULL;
+ MEM_SAFE_FREE(surface->effector_weights);
BLI_remlink(&(surface->canvas->surfaces), surface);
dynamicPaint_freeSurfaceData(surface);
diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c
index fc1721eaf3a..334118ddf3f 100644
--- a/source/blender/blenkernel/intern/effect.c
+++ b/source/blender/blenkernel/intern/effect.c
@@ -861,7 +861,7 @@ static void get_effector_tot(
int totpart = eff->psys->totpart;
int amount = eff->psys->part->effector_amount;
- *step = (totpart > amount) ? totpart / amount : 1;
+ *step = (totpart > amount) ? (int)ceil((float)totpart / (float)amount) : 1;
}
}
else {
diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c
index 92fd220549a..799d6553682 100644
--- a/source/blender/blenkernel/intern/fluid.c
+++ b/source/blender/blenkernel/intern/fluid.c
@@ -4153,7 +4153,7 @@ static void BKE_fluid_modifier_process(
{
const int scene_framenr = (int)DEG_get_ctime(depsgraph);
- if ((fmd->type & MOD_FLUID_TYPE_FLOW)) {
+ if (fmd->type & MOD_FLUID_TYPE_FLOW) {
BKE_fluid_modifier_processFlow(fmd, depsgraph, scene, ob, me, scene_framenr);
}
else if (fmd->type & MOD_FLUID_TYPE_EFFEC) {
@@ -4766,20 +4766,14 @@ static void BKE_fluid_modifier_freeDomain(FluidModifierData *fmd)
BLI_rw_mutex_free(fmd->domain->fluid_mutex);
}
- if (fmd->domain->effector_weights) {
- MEM_freeN(fmd->domain->effector_weights);
- }
- fmd->domain->effector_weights = NULL;
+ MEM_SAFE_FREE(fmd->domain->effector_weights);
if (!(fmd->modifier.flag & eModifierFlag_SharedCaches)) {
BKE_ptcache_free_list(&(fmd->domain->ptcaches[0]));
fmd->domain->point_cache[0] = NULL;
}
- if (fmd->domain->mesh_velocities) {
- MEM_freeN(fmd->domain->mesh_velocities);
- }
- fmd->domain->mesh_velocities = NULL;
+ MEM_SAFE_FREE(fmd->domain->mesh_velocities);
if (fmd->domain->coba) {
MEM_freeN(fmd->domain->coba);
@@ -4798,10 +4792,7 @@ static void BKE_fluid_modifier_freeFlow(FluidModifierData *fmd)
}
fmd->flow->mesh = NULL;
- if (fmd->flow->verts_old) {
- MEM_freeN(fmd->flow->verts_old);
- }
- fmd->flow->verts_old = NULL;
+ MEM_SAFE_FREE(fmd->flow->verts_old);
fmd->flow->numverts = 0;
fmd->flow->flags &= ~FLUID_FLOW_NEEDS_UPDATE;
@@ -4818,10 +4809,7 @@ static void BKE_fluid_modifier_freeEffector(FluidModifierData *fmd)
}
fmd->effector->mesh = NULL;
- if (fmd->effector->verts_old) {
- MEM_freeN(fmd->effector->verts_old);
- }
- fmd->effector->verts_old = NULL;
+ MEM_SAFE_FREE(fmd->effector->verts_old);
fmd->effector->numverts = 0;
fmd->effector->flags &= ~FLUID_EFFECTOR_NEEDS_UPDATE;
@@ -4857,18 +4845,12 @@ static void BKE_fluid_modifier_reset_ex(struct FluidModifierData *fmd, bool need
fmd->domain->active_fields = 0;
}
else if (fmd->flow) {
- if (fmd->flow->verts_old) {
- MEM_freeN(fmd->flow->verts_old);
- }
- fmd->flow->verts_old = NULL;
+ MEM_SAFE_FREE(fmd->flow->verts_old);
fmd->flow->numverts = 0;
fmd->flow->flags &= ~FLUID_FLOW_NEEDS_UPDATE;
}
else if (fmd->effector) {
- if (fmd->effector->verts_old) {
- MEM_freeN(fmd->effector->verts_old);
- }
- fmd->effector->verts_old = NULL;
+ MEM_SAFE_FREE(fmd->effector->verts_old);
fmd->effector->numverts = 0;
fmd->effector->flags &= ~FLUID_EFFECTOR_NEEDS_UPDATE;
}
diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c
index d0b9aeefa55..c1765967238 100644
--- a/source/blender/blenkernel/intern/font.c
+++ b/source/blender/blenkernel/intern/font.c
@@ -126,23 +126,22 @@ static void vfont_blend_write(BlendWriter *writer, ID *id, const void *id_addres
{
VFont *vf = (VFont *)id;
const bool is_undo = BLO_write_is_undo(writer);
- if (vf->id.us > 0 || is_undo) {
- /* Clean up, important in undo case to reduce false detection of changed datablocks. */
- vf->data = NULL;
- vf->temp_pf = NULL;
-
- /* Do not store packed files in case this is a library override ID. */
- if (ID_IS_OVERRIDE_LIBRARY(vf) && !is_undo) {
- vf->packedfile = NULL;
- }
- /* write LibData */
- BLO_write_id_struct(writer, VFont, id_address, &vf->id);
- BKE_id_blend_write(writer, &vf->id);
+ /* Clean up, important in undo case to reduce false detection of changed datablocks. */
+ vf->data = NULL;
+ vf->temp_pf = NULL;
- /* direct data */
- BKE_packedfile_blend_write(writer, vf->packedfile);
+ /* Do not store packed files in case this is a library override ID. */
+ if (ID_IS_OVERRIDE_LIBRARY(vf) && !is_undo) {
+ vf->packedfile = NULL;
}
+
+ /* write LibData */
+ BLO_write_id_struct(writer, VFont, id_address, &vf->id);
+ BKE_id_blend_write(writer, &vf->id);
+
+ /* direct data */
+ BKE_packedfile_blend_write(writer, vf->packedfile);
}
static void vfont_blend_read_data(BlendDataReader *reader, ID *id)
@@ -337,13 +336,9 @@ VFont *BKE_vfont_load(Main *bmain, const char *filepath)
vfd = BLI_vfontdata_from_freetypefont(pf);
if (vfd) {
- vfont = BKE_libblock_alloc(bmain, ID_VF, filename, 0);
+ /* If there's a font name, use it for the ID name. */
+ vfont = BKE_libblock_alloc(bmain, ID_VF, vfd->name[0] ? vfd->name : filename, 0);
vfont->data = vfd;
-
- /* if there's a font name, use it for the ID name */
- if (vfd->name[0] != '\0') {
- BLI_strncpy(vfont->id.name + 2, vfd->name, sizeof(vfont->id.name) - 2);
- }
BLI_strncpy(vfont->filepath, filepath, sizeof(vfont->filepath));
/* if autopack is on store the packedfile in de font structure */
@@ -719,6 +714,13 @@ typedef struct VFontToCurveIter {
float max;
} bisect;
bool ok;
+ /**
+ * Wrap words that extends beyond the text-box width (enabled by default).
+ *
+ * Currently only disabled when scale-to-fit is enabled,
+ * so floating-point error doesn't cause unexpected wrapping, see T89241.
+ */
+ bool word_wrap;
int status;
} VFontToCurveIter;
@@ -781,6 +783,7 @@ static bool vfont_to_curve(Object *ob,
char32_t ascii;
bool ok = false;
const float font_size = cu->fsize * iter_data->scale_to_fit;
+ const bool word_wrap = iter_data->word_wrap;
const float xof_scale = cu->xof / font_size;
const float yof_scale = cu->yof / font_size;
int last_line = -1;
@@ -951,43 +954,59 @@ static bool vfont_to_curve(Object *ob,
twidth = char_width(cu, che, info);
- /* Calculate positions */
- if ((tb_scale.w != 0.0f) && (ct->dobreak == 0) &&
- (((xof - tb_scale.x) + twidth) > xof_scale + tb_scale.w)) {
- // CLOG_WARN(&LOG, "linewidth exceeded: %c%c%c...", mem[i], mem[i+1], mem[i+2]);
- for (j = i; j && (mem[j] != '\n') && (chartransdata[j].dobreak == 0); j--) {
- bool dobreak = false;
- if (ELEM(mem[j], ' ', '-')) {
- ct -= (i - (j - 1));
- cnr -= (i - (j - 1));
- if (mem[j] == ' ') {
- wsnr--;
+ /* Calculate positions. */
+
+ if ((tb_scale.w != 0.0f) && (ct->dobreak == 0)) { /* May need wrapping. */
+ const float x_available = xof_scale + tb_scale.w;
+ const float x_used = (xof - tb_scale.x) + twidth;
+
+ if (word_wrap == false) {
+ /* When scale to fit is used, don't do any wrapping.
+ *
+ * Floating precision error can cause the text to be slightly larger.
+ * Assert this is a small value as large values indicate incorrect
+ * calculations with scale-to-fit which shouldn't be ignored. See T89241. */
+ if (x_used > x_available) {
+ BLI_assert_msg(compare_ff_relative(x_used, x_available, FLT_EPSILON, 64),
+ "VFontToCurveIter.scale_to_fit not set correctly!");
+ }
+ }
+ else if (x_used > x_available) {
+ // CLOG_WARN(&LOG, "linewidth exceeded: %c%c%c...", mem[i], mem[i+1], mem[i+2]);
+ for (j = i; j && (mem[j] != '\n') && (chartransdata[j].dobreak == 0); j--) {
+ bool dobreak = false;
+ if (ELEM(mem[j], ' ', '-')) {
+ ct -= (i - (j - 1));
+ cnr -= (i - (j - 1));
+ if (mem[j] == ' ') {
+ wsnr--;
+ }
+ if (mem[j] == '-') {
+ wsnr++;
+ }
+ i = j - 1;
+ xof = ct->xof;
+ ct[1].dobreak = 1;
+ custrinfo[i + 1].flag |= CU_CHINFO_WRAP;
+ dobreak = true;
}
- if (mem[j] == '-') {
- wsnr++;
+ else if (chartransdata[j].dobreak) {
+ // CLOG_WARN(&LOG, "word too long: %c%c%c...", mem[j], mem[j+1], mem[j+2]);
+ ct->dobreak = 1;
+ custrinfo[i + 1].flag |= CU_CHINFO_WRAP;
+ ct -= 1;
+ cnr -= 1;
+ i--;
+ xof = ct->xof;
+ dobreak = true;
}
- i = j - 1;
- xof = ct->xof;
- ct[1].dobreak = 1;
- custrinfo[i + 1].flag |= CU_CHINFO_WRAP;
- dobreak = true;
- }
- else if (chartransdata[j].dobreak) {
- // CLOG_WARN(&LOG, "word too long: %c%c%c...", mem[j], mem[j+1], mem[j+2]);
- ct->dobreak = 1;
- custrinfo[i + 1].flag |= CU_CHINFO_WRAP;
- ct -= 1;
- cnr -= 1;
- i--;
- xof = ct->xof;
- dobreak = true;
- }
- if (dobreak) {
- if (tb_scale.h == 0.0f) {
- /* NOTE: If underlined text is truncated away, the extra space is also truncated. */
- custrinfo[i + 1].flag |= CU_CHINFO_OVERFLOW;
+ if (dobreak) {
+ if (tb_scale.h == 0.0f) {
+ /* NOTE: If underlined text is truncated away, the extra space is also truncated. */
+ custrinfo[i + 1].flag |= CU_CHINFO_OVERFLOW;
+ }
+ goto makebreak;
}
- goto makebreak;
}
}
}
@@ -1549,6 +1568,7 @@ static bool vfont_to_curve(Object *ob,
const float total_text_height = lnr * linedist;
iter_data->scale_to_fit = tb_scale.h / total_text_height;
iter_data->status = VFONT_TO_CURVE_SCALE_ONCE;
+ iter_data->word_wrap = false;
}
}
else if (tb_scale.h == 0.0f) {
@@ -1556,10 +1576,10 @@ static bool vfont_to_curve(Object *ob,
if (longest_line_length > tb_scale.w) {
/* We make sure longest line before it broke can fit here. */
float scale_to_fit = tb_scale.w / longest_line_length;
- scale_to_fit -= FLT_EPSILON;
iter_data->scale_to_fit = scale_to_fit;
iter_data->status = VFONT_TO_CURVE_SCALE_ONCE;
+ iter_data->word_wrap = false;
}
}
}
@@ -1620,6 +1640,7 @@ static bool vfont_to_curve(Object *ob,
else {
iter_data->scale_to_fit = iter_data->bisect.min;
iter_data->status = VFONT_TO_CURVE_SCALE_ONCE;
+ iter_data->word_wrap = false;
}
}
}
@@ -1689,6 +1710,7 @@ bool BKE_vfont_to_curve_ex(Object *ob,
VFontToCurveIter data = {
.iteraction = cu->totbox * FONT_TO_CURVE_SCALE_ITERATIONS,
.scale_to_fit = 1.0f,
+ .word_wrap = true,
.ok = true,
.status = VFONT_TO_CURVE_INIT,
};
diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc
index 90a97264c8f..32a65ab47bf 100644
--- a/source/blender/blenkernel/intern/geometry_set_instances.cc
+++ b/source/blender/blenkernel/intern/geometry_set_instances.cc
@@ -491,6 +491,10 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Span<GeometryInstanceGrou
}
}
+ /* A possible optimization is to only tag the normals dirty when there are transforms that change
+ * normals. */
+ BKE_mesh_normals_tag_dirty(new_mesh);
+
return new_mesh;
}
diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c
index 38397f8f307..9062fd2d39c 100644
--- a/source/blender/blenkernel/intern/gpencil.c
+++ b/source/blender/blenkernel/intern/gpencil.c
@@ -131,7 +131,7 @@ static void greasepencil_free_data(ID *id)
{
/* Really not ideal, but for now will do... In theory custom behaviors like not freeing cache
* should be handled through specific API, and not be part of the generic one. */
- BKE_gpencil_free((bGPdata *)id, true);
+ BKE_gpencil_free_data((bGPdata *)id, true);
}
static void greasepencil_foreach_id(ID *id, LibraryForeachIDData *data)
@@ -150,47 +150,46 @@ static void greasepencil_foreach_id(ID *id, LibraryForeachIDData *data)
static void greasepencil_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
bGPdata *gpd = (bGPdata *)id;
- if (gpd->id.us > 0 || BLO_write_is_undo(writer)) {
- /* Clean up, important in undo case to reduce false detection of changed data-blocks. */
- /* XXX not sure why the whole run-time data is not cleared in reading code,
- * for now mimicking it here. */
- gpd->runtime.sbuffer = NULL;
- gpd->runtime.sbuffer_used = 0;
- gpd->runtime.sbuffer_size = 0;
- gpd->runtime.tot_cp_points = 0;
- /* write gpd data block to file */
- BLO_write_id_struct(writer, bGPdata, id_address, &gpd->id);
- BKE_id_blend_write(writer, &gpd->id);
+ /* Clean up, important in undo case to reduce false detection of changed data-blocks. */
+ /* XXX not sure why the whole run-time data is not cleared in reading code,
+ * for now mimicking it here. */
+ gpd->runtime.sbuffer = NULL;
+ gpd->runtime.sbuffer_used = 0;
+ gpd->runtime.sbuffer_size = 0;
+ gpd->runtime.tot_cp_points = 0;
- if (gpd->adt) {
- BKE_animdata_blend_write(writer, gpd->adt);
- }
+ /* write gpd data block to file */
+ BLO_write_id_struct(writer, bGPdata, id_address, &gpd->id);
+ BKE_id_blend_write(writer, &gpd->id);
- BKE_defbase_blend_write(writer, &gpd->vertex_group_names);
+ if (gpd->adt) {
+ BKE_animdata_blend_write(writer, gpd->adt);
+ }
- BLO_write_pointer_array(writer, gpd->totcol, gpd->mat);
+ BKE_defbase_blend_write(writer, &gpd->vertex_group_names);
- /* write grease-pencil layers to file */
- BLO_write_struct_list(writer, bGPDlayer, &gpd->layers);
- LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
- /* Write mask list. */
- BLO_write_struct_list(writer, bGPDlayer_Mask, &gpl->mask_layers);
- /* write this layer's frames to file */
- BLO_write_struct_list(writer, bGPDframe, &gpl->frames);
- LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
- /* write strokes */
- BLO_write_struct_list(writer, bGPDstroke, &gpf->strokes);
- LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- BLO_write_struct_array(writer, bGPDspoint, gps->totpoints, gps->points);
- BLO_write_struct_array(writer, bGPDtriangle, gps->tot_triangles, gps->triangles);
- BKE_defvert_blend_write(writer, gps->totpoints, gps->dvert);
- if (gps->editcurve != NULL) {
- bGPDcurve *gpc = gps->editcurve;
- BLO_write_struct(writer, bGPDcurve, gpc);
- BLO_write_struct_array(
- writer, bGPDcurve_point, gpc->tot_curve_points, gpc->curve_points);
- }
+ BLO_write_pointer_array(writer, gpd->totcol, gpd->mat);
+
+ /* write grease-pencil layers to file */
+ BLO_write_struct_list(writer, bGPDlayer, &gpd->layers);
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ /* Write mask list. */
+ BLO_write_struct_list(writer, bGPDlayer_Mask, &gpl->mask_layers);
+ /* write this layer's frames to file */
+ BLO_write_struct_list(writer, bGPDframe, &gpl->frames);
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ /* write strokes */
+ BLO_write_struct_list(writer, bGPDstroke, &gpf->strokes);
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ BLO_write_struct_array(writer, bGPDspoint, gps->totpoints, gps->points);
+ BLO_write_struct_array(writer, bGPDtriangle, gps->tot_triangles, gps->triangles);
+ BKE_defvert_blend_write(writer, gps->totpoints, gps->dvert);
+ if (gps->editcurve != NULL) {
+ bGPDcurve *gpc = gps->editcurve;
+ BLO_write_struct(writer, bGPDcurve, gpc);
+ BLO_write_struct_array(
+ writer, bGPDcurve_point, gpc->tot_curve_points, gpc->curve_points);
}
}
}
@@ -496,7 +495,7 @@ void BKE_gpencil_free_layers(ListBase *list)
}
/** Free (or release) any data used by this grease pencil (does not free the gpencil itself). */
-void BKE_gpencil_free(bGPdata *gpd, bool free_all)
+void BKE_gpencil_free_data(bGPdata *gpd, bool free_all)
{
/* free layers */
BKE_gpencil_free_layers(&gpd->layers);
@@ -519,8 +518,9 @@ void BKE_gpencil_free(bGPdata *gpd, bool free_all)
*/
void BKE_gpencil_eval_delete(bGPdata *gpd_eval)
{
- BKE_gpencil_free(gpd_eval, true);
+ BKE_gpencil_free_data(gpd_eval, true);
BKE_libblock_free_data(&gpd_eval->id, false);
+ BLI_assert(!gpd_eval->id.py_instance); /* Or call #BKE_libblock_free_data_py. */
MEM_freeN(gpd_eval);
}
diff --git a/source/blender/blenkernel/intern/gpencil_geom.cc b/source/blender/blenkernel/intern/gpencil_geom.cc
index f8a07939096..0f218d6166c 100644
--- a/source/blender/blenkernel/intern/gpencil_geom.cc
+++ b/source/blender/blenkernel/intern/gpencil_geom.cc
@@ -31,6 +31,7 @@
#include "MEM_guardedalloc.h"
+#include "BLI_array_utils.h"
#include "BLI_blenlib.h"
#include "BLI_float3.hh"
#include "BLI_ghash.h"
@@ -2269,7 +2270,8 @@ static void gpencil_generate_edgeloops(Object *ob,
const int thickness,
const float offset,
const float matrix[4][4],
- const bool use_seams)
+ const bool use_seams,
+ const bool use_vgroups)
{
Mesh *me = (Mesh *)ob->data;
if (me->totedge == 0) {
@@ -2278,9 +2280,9 @@ static void gpencil_generate_edgeloops(Object *ob,
/* Arrays for all edge vertices (forward and backward) that form a edge loop.
* This is reused for each edge-loop to create gpencil stroke. */
- uint *stroke = (uint *)MEM_callocN(sizeof(uint) * me->totedge * 2, __func__);
- uint *stroke_fw = (uint *)MEM_callocN(sizeof(uint) * me->totedge, __func__);
- uint *stroke_bw = (uint *)MEM_callocN(sizeof(uint) * me->totedge, __func__);
+ uint *stroke = (uint *)MEM_mallocN(sizeof(uint) * me->totedge * 2, __func__);
+ uint *stroke_fw = (uint *)MEM_mallocN(sizeof(uint) * me->totedge, __func__);
+ uint *stroke_bw = (uint *)MEM_mallocN(sizeof(uint) * me->totedge, __func__);
/* Create array with all edges. */
GpEdge *gp_edges = (GpEdge *)MEM_callocN(sizeof(GpEdge) * me->totedge, __func__);
@@ -2311,11 +2313,6 @@ static void gpencil_generate_edgeloops(Object *ob,
bool pending = true;
int e = 0;
while (pending) {
- /* Clear arrays of stroke. */
- memset(stroke_fw, 0, sizeof(uint) * me->totedge);
- memset(stroke_bw, 0, sizeof(uint) * me->totedge);
- memset(stroke, 0, sizeof(uint) * me->totedge * 2);
-
gped = &gp_edges[e];
/* Look first unused edge. */
if (gped->flag != 0) {
@@ -2330,7 +2327,7 @@ static void gpencil_generate_edgeloops(Object *ob,
stroke_bw[0] = e;
gped->flag = 1;
- /* Hash used to avoid loop over same vertice. */
+ /* Hash used to avoid loop over same vertices. */
GHash *v_table = BLI_ghash_int_new(__func__);
/* Look forward edges. */
int totedges = gpencil_walk_edge(v_table, gp_edges, me->totedge, stroke_fw, e, angle, false);
@@ -2354,38 +2351,41 @@ static void gpencil_generate_edgeloops(Object *ob,
bGPDstroke *gps_stroke = BKE_gpencil_stroke_add(
gpf_stroke, MAX2(stroke_mat_index, 0), array_len + 1, thickness * thickness, false);
+ /* Create dvert data. */
+ MDeformVert *me_dvert = me->dvert;
+ if (use_vgroups && me_dvert) {
+ gps_stroke->dvert = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * (array_len + 1),
+ "gp_stroke_dverts");
+ }
+
/* Create first segment. */
float fpt[3];
- uint v = stroke[0];
- gped = &gp_edges[v];
- bGPDspoint *pt = &gps_stroke->points[0];
- mul_v3_v3fl(fpt, gped->n1, offset);
- add_v3_v3v3(&pt->x, gped->v1_co, fpt);
- mul_m4_v3(matrix, &pt->x);
-
- pt->pressure = 1.0f;
- pt->strength = 1.0f;
-
- pt = &gps_stroke->points[1];
- mul_v3_v3fl(fpt, gped->n2, offset);
- add_v3_v3v3(&pt->x, gped->v2_co, fpt);
- mul_m4_v3(matrix, &pt->x);
-
- pt->pressure = 1.0f;
- pt->strength = 1.0f;
-
- /* Add next segments. */
- for (int i = 1; i < array_len; i++) {
- v = stroke[i];
- gped = &gp_edges[v];
-
- pt = &gps_stroke->points[i + 1];
- mul_v3_v3fl(fpt, gped->n2, offset);
- add_v3_v3v3(&pt->x, gped->v2_co, fpt);
+ for (int i = 0; i < array_len + 1; i++) {
+ int vertex_index = i == 0 ? gp_edges[stroke[0]].v1 : gp_edges[stroke[i - 1]].v2;
+ MVert *mv = &me->mvert[vertex_index];
+
+ /* Add segment. */
+ bGPDspoint *pt = &gps_stroke->points[i];
+ normal_short_to_float_v3(fpt, mv->no);
+ mul_v3_v3fl(fpt, fpt, offset);
+ add_v3_v3v3(&pt->x, mv->co, fpt);
mul_m4_v3(matrix, &pt->x);
pt->pressure = 1.0f;
pt->strength = 1.0f;
+
+ /* Copy vertex groups from mesh. Assuming they already exist in the same order. */
+ if (use_vgroups && me_dvert) {
+ MDeformVert *dv = &gps_stroke->dvert[i];
+ MDeformVert *src_dv = &me_dvert[vertex_index];
+ dv->totweight = src_dv->totweight;
+ dv->dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * dv->totweight,
+ "gp_stroke_dverts_dw");
+ for (int j = 0; j < dv->totweight; j++) {
+ dv->dw[j].weight = src_dv->dw[j].weight;
+ dv->dw[j].def_nr = src_dv->dw[j].def_nr;
+ }
+ }
}
BKE_gpencil_stroke_geometry_update(gpd, gps_stroke);
@@ -2488,7 +2488,8 @@ bool BKE_gpencil_convert_mesh(Main *bmain,
const float matrix[4][4],
const int frame_offset,
const bool use_seams,
- const bool use_faces)
+ const bool use_faces,
+ const bool use_vgroups)
{
if (ELEM(nullptr, ob_gp, ob_mesh) || (ob_gp->type != OB_GPENCIL) || (ob_gp->data == nullptr)) {
return false;
@@ -2505,83 +2506,105 @@ bool BKE_gpencil_convert_mesh(Main *bmain,
char element_name[200];
/* Need at least an edge. */
- if (me_eval->totvert < 2) {
+ if (me_eval->totedge < 1) {
return false;
}
+ /* Create matching vertex groups. */
+ BKE_defgroup_copy_list(&gpd->vertex_group_names, &me_eval->vertex_group_names);
+ gpd->vertex_group_active_index = me_eval->vertex_group_active_index;
+
const float default_colors[2][4] = {{0.0f, 0.0f, 0.0f, 1.0f}, {0.7f, 0.7f, 0.7f, 1.0f}};
- /* Create stroke material. */
+ /* Lookup existing stroke material on gp object. */
make_element_name(ob_mesh->id.name + 2, "Stroke", 64, element_name);
int stroke_mat_index = gpencil_material_find_index_by_name(ob_gp, element_name);
if (stroke_mat_index == -1) {
+ /* Create new default stroke material as there is no existing material. */
gpencil_add_material(
bmain, ob_gp, element_name, default_colors[0], true, false, &stroke_mat_index);
}
/* Export faces as filled strokes. */
- if (use_faces) {
-
+ if (use_faces && mpoly_len > 0) {
/* Read all polygons and create fill for each. */
- if (mpoly_len > 0) {
- make_element_name(ob_mesh->id.name + 2, "Fills", 128, element_name);
- /* Create Layer and Frame. */
- bGPDlayer *gpl_fill = BKE_gpencil_layer_named_get(gpd, element_name);
- if (gpl_fill == nullptr) {
- gpl_fill = BKE_gpencil_layer_addnew(gpd, element_name, true, false);
- }
- bGPDframe *gpf_fill = BKE_gpencil_layer_frame_get(
- gpl_fill, CFRA + frame_offset, GP_GETFRAME_ADD_NEW);
- int i;
- for (i = 0; i < mpoly_len; i++) {
- const MPoly *mp = &mpoly[i];
-
- /* Find material. */
- int mat_idx = 0;
- Material *ma = BKE_object_material_get(ob_mesh, mp->mat_nr + 1);
- make_element_name(
- ob_mesh->id.name + 2, (ma != nullptr) ? ma->id.name + 2 : "Fill", 64, element_name);
- mat_idx = BKE_gpencil_material_find_index_by_name_prefix(ob_gp, element_name);
- if (mat_idx == -1) {
- float color[4];
- if (ma != nullptr) {
- copy_v3_v3(color, &ma->r);
- color[3] = 1.0f;
- }
- else {
- copy_v4_v4(color, default_colors[1]);
- }
- gpencil_add_material(bmain, ob_gp, element_name, color, false, true, &mat_idx);
+ make_element_name(ob_mesh->id.name + 2, "Fills", 128, element_name);
+ /* Create Layer and Frame. */
+ bGPDlayer *gpl_fill = BKE_gpencil_layer_named_get(gpd, element_name);
+ if (gpl_fill == nullptr) {
+ gpl_fill = BKE_gpencil_layer_addnew(gpd, element_name, true, false);
+ }
+ bGPDframe *gpf_fill = BKE_gpencil_layer_frame_get(
+ gpl_fill, CFRA + frame_offset, GP_GETFRAME_ADD_NEW);
+ int i;
+ for (i = 0; i < mpoly_len; i++) {
+ const MPoly *mp = &mpoly[i];
+
+ /* Find material. */
+ int mat_idx = 0;
+ Material *ma = BKE_object_material_get(ob_mesh, mp->mat_nr + 1);
+ make_element_name(
+ ob_mesh->id.name + 2, (ma != nullptr) ? ma->id.name + 2 : "Fill", 64, element_name);
+ mat_idx = BKE_gpencil_material_find_index_by_name_prefix(ob_gp, element_name);
+ if (mat_idx == -1) {
+ float color[4];
+ if (ma != nullptr) {
+ copy_v3_v3(color, &ma->r);
+ color[3] = 1.0f;
+ }
+ else {
+ copy_v4_v4(color, default_colors[1]);
}
+ gpencil_add_material(bmain, ob_gp, element_name, color, false, true, &mat_idx);
+ }
- bGPDstroke *gps_fill = BKE_gpencil_stroke_add(gpf_fill, mat_idx, mp->totloop, 10, false);
- gps_fill->flag |= GP_STROKE_CYCLIC;
+ bGPDstroke *gps_fill = BKE_gpencil_stroke_add(gpf_fill, mat_idx, mp->totloop, 10, false);
+ gps_fill->flag |= GP_STROKE_CYCLIC;
- /* Add points to strokes. */
- for (int j = 0; j < mp->totloop; j++) {
- const MLoop *ml = &mloop[mp->loopstart + j];
- const MVert *mv = &me_eval->mvert[ml->v];
+ /* Create dvert data. */
+ MDeformVert *me_dvert = me_eval->dvert;
+ if (use_vgroups && me_dvert) {
+ gps_fill->dvert = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * mp->totloop,
+ "gp_fill_dverts");
+ }
- bGPDspoint *pt = &gps_fill->points[j];
- copy_v3_v3(&pt->x, mv->co);
- mul_m4_v3(matrix, &pt->x);
- pt->pressure = 1.0f;
- pt->strength = 1.0f;
- }
- /* If has only 3 points subdivide. */
- if (mp->totloop == 3) {
- BKE_gpencil_stroke_subdivide(gpd, gps_fill, 1, GP_SUBDIV_SIMPLE);
+ /* Add points to strokes. */
+ for (int j = 0; j < mp->totloop; j++) {
+ const MLoop *ml = &mloop[mp->loopstart + j];
+ const MVert *mv = &me_eval->mvert[ml->v];
+
+ bGPDspoint *pt = &gps_fill->points[j];
+ copy_v3_v3(&pt->x, mv->co);
+ mul_m4_v3(matrix, &pt->x);
+ pt->pressure = 1.0f;
+ pt->strength = 1.0f;
+
+ /* Copy vertex groups from mesh. Assuming they already exist in the same order. */
+ if (use_vgroups && me_dvert) {
+ MDeformVert *dv = &gps_fill->dvert[j];
+ MDeformVert *src_dv = &me_dvert[ml->v];
+ dv->totweight = src_dv->totweight;
+ dv->dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * dv->totweight,
+ "gp_fill_dverts_dw");
+ for (int k = 0; k < dv->totweight; k++) {
+ dv->dw[k].weight = src_dv->dw[k].weight;
+ dv->dw[k].def_nr = src_dv->dw[k].def_nr;
+ }
}
-
- BKE_gpencil_stroke_geometry_update(gpd, gps_fill);
}
+ /* If has only 3 points subdivide. */
+ if (mp->totloop == 3) {
+ BKE_gpencil_stroke_subdivide(gpd, gps_fill, 1, GP_SUBDIV_SIMPLE);
+ }
+
+ BKE_gpencil_stroke_geometry_update(gpd, gps_fill);
}
}
/* Create stroke from edges. */
- make_element_name(ob_mesh->id.name + 2, "Lines", 128, element_name);
/* Create Layer and Frame. */
+ make_element_name(ob_mesh->id.name + 2, "Lines", 128, element_name);
bGPDlayer *gpl_stroke = BKE_gpencil_layer_named_get(gpd, element_name);
if (gpl_stroke == nullptr) {
gpl_stroke = BKE_gpencil_layer_addnew(gpd, element_name, true, false);
@@ -2589,8 +2612,16 @@ bool BKE_gpencil_convert_mesh(Main *bmain,
bGPDframe *gpf_stroke = BKE_gpencil_layer_frame_get(
gpl_stroke, CFRA + frame_offset, GP_GETFRAME_ADD_NEW);
- gpencil_generate_edgeloops(
- ob_eval, gpd, gpf_stroke, stroke_mat_index, angle, thickness, offset, matrix, use_seams);
+ gpencil_generate_edgeloops(ob_eval,
+ gpd,
+ gpf_stroke,
+ stroke_mat_index,
+ angle,
+ thickness,
+ offset,
+ matrix,
+ use_seams,
+ use_vgroups);
/* Tag for recalculation */
DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
@@ -2787,46 +2818,12 @@ void BKE_gpencil_stroke_set_random_color(bGPDstroke *gps)
/* Flip stroke. */
void BKE_gpencil_stroke_flip(bGPDstroke *gps)
{
- int end = gps->totpoints - 1;
+ /* Reverse points. */
+ BLI_array_reverse(gps->points, gps->totpoints);
- for (int i = 0; i < gps->totpoints / 2; i++) {
- bGPDspoint *point, *point2;
- bGPDspoint pt;
-
- /* save first point */
- point = &gps->points[i];
- pt.x = point->x;
- pt.y = point->y;
- pt.z = point->z;
- pt.flag = point->flag;
- pt.pressure = point->pressure;
- pt.strength = point->strength;
- pt.time = point->time;
- copy_v4_v4(pt.vert_color, point->vert_color);
-
- /* replace first point with last point */
- point2 = &gps->points[end];
- point->x = point2->x;
- point->y = point2->y;
- point->z = point2->z;
- point->flag = point2->flag;
- point->pressure = point2->pressure;
- point->strength = point2->strength;
- point->time = point2->time;
- copy_v4_v4(point->vert_color, point2->vert_color);
-
- /* replace last point with first saved before */
- point = &gps->points[end];
- point->x = pt.x;
- point->y = pt.y;
- point->z = pt.z;
- point->flag = pt.flag;
- point->pressure = pt.pressure;
- point->strength = pt.strength;
- point->time = pt.time;
- copy_v4_v4(point->vert_color, pt.vert_color);
-
- end--;
+ /* Reverse vertex groups if available. */
+ if (gps->dvert) {
+ BLI_array_reverse(gps->dvert, gps->totpoints);
}
}
diff --git a/source/blender/blenkernel/intern/hair.c b/source/blender/blenkernel/intern/hair.c
index 2894d6daf23..af7cc0acb57 100644
--- a/source/blender/blenkernel/intern/hair.c
+++ b/source/blender/blenkernel/intern/hair.c
@@ -114,32 +114,31 @@ static void hair_foreach_id(ID *id, LibraryForeachIDData *data)
static void hair_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
Hair *hair = (Hair *)id;
- if (hair->id.us > 0 || BLO_write_is_undo(writer)) {
- CustomDataLayer *players = NULL, players_buff[CD_TEMP_CHUNK_SIZE];
- CustomDataLayer *clayers = NULL, clayers_buff[CD_TEMP_CHUNK_SIZE];
- CustomData_blend_write_prepare(&hair->pdata, &players, players_buff, ARRAY_SIZE(players_buff));
- CustomData_blend_write_prepare(&hair->cdata, &clayers, clayers_buff, ARRAY_SIZE(clayers_buff));
-
- /* Write LibData */
- BLO_write_id_struct(writer, Hair, id_address, &hair->id);
- BKE_id_blend_write(writer, &hair->id);
-
- /* Direct data */
- CustomData_blend_write(writer, &hair->pdata, players, hair->totpoint, CD_MASK_ALL, &hair->id);
- CustomData_blend_write(writer, &hair->cdata, clayers, hair->totcurve, CD_MASK_ALL, &hair->id);
-
- BLO_write_pointer_array(writer, hair->totcol, hair->mat);
- if (hair->adt) {
- BKE_animdata_blend_write(writer, hair->adt);
- }
- /* Remove temporary data. */
- if (players && players != players_buff) {
- MEM_freeN(players);
- }
- if (clayers && clayers != clayers_buff) {
- MEM_freeN(clayers);
- }
+ CustomDataLayer *players = NULL, players_buff[CD_TEMP_CHUNK_SIZE];
+ CustomDataLayer *clayers = NULL, clayers_buff[CD_TEMP_CHUNK_SIZE];
+ CustomData_blend_write_prepare(&hair->pdata, &players, players_buff, ARRAY_SIZE(players_buff));
+ CustomData_blend_write_prepare(&hair->cdata, &clayers, clayers_buff, ARRAY_SIZE(clayers_buff));
+
+ /* Write LibData */
+ BLO_write_id_struct(writer, Hair, id_address, &hair->id);
+ BKE_id_blend_write(writer, &hair->id);
+
+ /* Direct data */
+ CustomData_blend_write(writer, &hair->pdata, players, hair->totpoint, CD_MASK_ALL, &hair->id);
+ CustomData_blend_write(writer, &hair->cdata, clayers, hair->totcurve, CD_MASK_ALL, &hair->id);
+
+ BLO_write_pointer_array(writer, hair->totcol, hair->mat);
+ if (hair->adt) {
+ BKE_animdata_blend_write(writer, hair->adt);
+ }
+
+ /* Remove temporary data. */
+ if (players && players != players_buff) {
+ MEM_freeN(players);
+ }
+ if (clayers && clayers != clayers_buff) {
+ MEM_freeN(clayers);
}
}
diff --git a/source/blender/blenkernel/intern/icons.cc b/source/blender/blenkernel/intern/icons.cc
index 12a0a1e3ae7..5a4b2448a73 100644
--- a/source/blender/blenkernel/intern/icons.cc
+++ b/source/blender/blenkernel/intern/icons.cc
@@ -1,4 +1,4 @@
-/*
+/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c
index f4ba1ff8b92..d87290e1eb4 100644
--- a/source/blender/blenkernel/intern/image.c
+++ b/source/blender/blenkernel/intern/image.c
@@ -229,12 +229,26 @@ static void image_blend_write(BlendWriter *writer, ID *id, const void *id_addres
{
Image *ima = (Image *)id;
const bool is_undo = BLO_write_is_undo(writer);
- if (ima->id.us > 0 || is_undo) {
- ImagePackedFile *imapf;
- BLI_assert(ima->packedfile == NULL);
+ /* Clear all data that isn't read to reduce false detection of changed image during memfile undo.
+ */
+ ima->lastused = 0;
+ ima->cache = NULL;
+ ima->gpuflag = 0;
+ BLI_listbase_clear(&ima->anims);
+ BLI_listbase_clear(&ima->gpu_refresh_areas);
+ for (int i = 0; i < 3; i++) {
+ for (int j = 0; j < 2; j++) {
+ ima->gputexture[i][j] = NULL;
+ }
+ }
+
+ ImagePackedFile *imapf;
+
+ BLI_assert(ima->packedfile == NULL);
+ if (!is_undo) {
/* Do not store packed files in case this is a library override ID. */
- if (ID_IS_OVERRIDE_LIBRARY(ima) && !is_undo) {
+ if (ID_IS_OVERRIDE_LIBRARY(ima)) {
BLI_listbase_clear(&ima->packedfiles);
}
else {
@@ -244,29 +258,29 @@ static void image_blend_write(BlendWriter *writer, ID *id, const void *id_addres
ima->packedfile = imapf->packedfile;
}
}
+ }
- /* write LibData */
- BLO_write_id_struct(writer, Image, id_address, &ima->id);
- BKE_id_blend_write(writer, &ima->id);
+ /* write LibData */
+ BLO_write_id_struct(writer, Image, id_address, &ima->id);
+ BKE_id_blend_write(writer, &ima->id);
- for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) {
- BLO_write_struct(writer, ImagePackedFile, imapf);
- BKE_packedfile_blend_write(writer, imapf->packedfile);
- }
+ for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) {
+ BLO_write_struct(writer, ImagePackedFile, imapf);
+ BKE_packedfile_blend_write(writer, imapf->packedfile);
+ }
- BKE_previewimg_blend_write(writer, ima->preview);
+ BKE_previewimg_blend_write(writer, ima->preview);
- LISTBASE_FOREACH (ImageView *, iv, &ima->views) {
- BLO_write_struct(writer, ImageView, iv);
- }
- BLO_write_struct(writer, Stereo3dFormat, ima->stereo3d_format);
+ LISTBASE_FOREACH (ImageView *, iv, &ima->views) {
+ BLO_write_struct(writer, ImageView, iv);
+ }
+ BLO_write_struct(writer, Stereo3dFormat, ima->stereo3d_format);
- BLO_write_struct_list(writer, ImageTile, &ima->tiles);
+ BLO_write_struct_list(writer, ImageTile, &ima->tiles);
- ima->packedfile = NULL;
+ ima->packedfile = NULL;
- BLO_write_struct_list(writer, RenderSlot, &ima->renderslots);
- }
+ BLO_write_struct_list(writer, RenderSlot, &ima->renderslots);
}
static void image_blend_read_data(BlendDataReader *reader, ID *id)
@@ -300,6 +314,7 @@ static void image_blend_read_data(BlendDataReader *reader, ID *id)
LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
tile->ok = IMA_OK;
}
+ ima->lastused = 0;
ima->gpuflag = 0;
BLI_listbase_clear(&ima->gpu_refresh_areas);
}
@@ -519,7 +534,7 @@ void BKE_image_free_buffers(Image *ima)
}
/** Free (or release) any data used by this image (does not free the image itself). */
-void BKE_image_free(Image *ima)
+void BKE_image_free_data(Image *ima)
{
image_free_data(&ima->id);
}
@@ -670,24 +685,27 @@ bool BKE_image_has_opengl_texture(Image *ima)
return false;
}
+static int image_get_tile_number_from_iuser(Image *ima, const ImageUser *iuser)
+{
+ BLI_assert(ima != NULL && ima->tiles.first);
+ ImageTile *tile = ima->tiles.first;
+ return (iuser && iuser->tile) ? iuser->tile : tile->tile_number;
+}
+
ImageTile *BKE_image_get_tile(Image *ima, int tile_number)
{
if (ima == NULL) {
return NULL;
}
- /* Verify valid tile range. */
- if ((tile_number != 0) && (tile_number < 1001 || tile_number > IMA_UDIM_MAX)) {
- return NULL;
- }
-
- /* Tile number 0 is a special case and refers to the first tile, typically
+ /* Tiles 0 and 1001 are a special case and refer to the first tile, typically
* coming from non-UDIM-aware code. */
if (ELEM(tile_number, 0, 1001)) {
return ima->tiles.first;
}
- if (ima->source != IMA_SRC_TILED) {
+ /* Must have a tiled image and a valid tile number at this point. */
+ if (ima->source != IMA_SRC_TILED || tile_number < 1001 || tile_number > IMA_UDIM_MAX) {
return NULL;
}
@@ -702,7 +720,7 @@ ImageTile *BKE_image_get_tile(Image *ima, int tile_number)
ImageTile *BKE_image_get_tile_from_iuser(Image *ima, const ImageUser *iuser)
{
- return BKE_image_get_tile(ima, (iuser && iuser->tile) ? iuser->tile : 1001);
+ return BKE_image_get_tile(ima, image_get_tile_number_from_iuser(ima, iuser));
}
int BKE_image_get_tile_from_pos(struct Image *ima,
@@ -1020,7 +1038,7 @@ Image *BKE_image_add_generated(Main *bmain,
int view_id;
const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME};
- /* STRNCPY(ima->filepath, name); */ /* don't do this, this writes in ain invalid filepath! */
+ // STRNCPY(ima->filepath, name); /* don't do this, this writes in ain invalid filepath! */
ima->gen_x = width;
ima->gen_y = height;
ima->gen_type = gen_type;
@@ -3803,8 +3821,8 @@ bool BKE_image_remove_tile(struct Image *ima, ImageTile *tile)
return false;
}
- if (tile == ima->tiles.first) {
- /* Can't remove first tile. */
+ if (BLI_listbase_is_single(&ima->tiles)) {
+ /* Can't remove the last remaining tile. */
return false;
}
@@ -3815,6 +3833,64 @@ bool BKE_image_remove_tile(struct Image *ima, ImageTile *tile)
return true;
}
+void BKE_image_reassign_tile(struct Image *ima, ImageTile *tile, int new_tile_number)
+{
+ if (ima == NULL || tile == NULL || ima->source != IMA_SRC_TILED) {
+ return;
+ }
+
+ if (new_tile_number < 1001 || new_tile_number > IMA_UDIM_MAX) {
+ return;
+ }
+
+ const int old_tile_number = tile->tile_number;
+ tile->tile_number = new_tile_number;
+
+ if (BKE_image_is_multiview(ima)) {
+ const int totviews = BLI_listbase_count(&ima->views);
+ for (int i = 0; i < totviews; i++) {
+ ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, i, old_tile_number);
+ image_remove_ibuf(ima, i, old_tile_number);
+ image_assign_ibuf(ima, ibuf, i, new_tile_number);
+ IMB_freeImBuf(ibuf);
+ }
+ }
+ else {
+ ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, 0, old_tile_number);
+ image_remove_ibuf(ima, 0, old_tile_number);
+ image_assign_ibuf(ima, ibuf, 0, new_tile_number);
+ IMB_freeImBuf(ibuf);
+ }
+
+ for (int eye = 0; eye < 2; eye++) {
+ /* Reallocate GPU tile array. */
+ if (ima->gputexture[TEXTARGET_2D_ARRAY][eye] != NULL) {
+ GPU_texture_free(ima->gputexture[TEXTARGET_2D_ARRAY][eye]);
+ ima->gputexture[TEXTARGET_2D_ARRAY][eye] = NULL;
+ }
+ if (ima->gputexture[TEXTARGET_TILE_MAPPING][eye] != NULL) {
+ GPU_texture_free(ima->gputexture[TEXTARGET_TILE_MAPPING][eye]);
+ ima->gputexture[TEXTARGET_TILE_MAPPING][eye] = NULL;
+ }
+ }
+}
+
+static int tile_sort_cb(const void *a, const void *b)
+{
+ const ImageTile *tile_a = a;
+ const ImageTile *tile_b = b;
+ return (tile_a->tile_number > tile_b->tile_number) ? 1 : 0;
+}
+
+void BKE_image_sort_tiles(struct Image *ima)
+{
+ if (ima == NULL || ima->source != IMA_SRC_TILED) {
+ return;
+ }
+
+ BLI_listbase_sort(&ima->tiles, tile_sort_cb);
+}
+
bool BKE_image_fill_tile(struct Image *ima,
ImageTile *tile,
int width,
@@ -4890,7 +4966,7 @@ static void image_get_entry_and_index(Image *ima, ImageUser *iuser, int *r_entry
}
}
else if (ima->source == IMA_SRC_TILED) {
- frame = (iuser && iuser->tile) ? iuser->tile : 1001;
+ frame = image_get_tile_number_from_iuser(ima, iuser);
}
*r_entry = frame;
@@ -4955,7 +5031,7 @@ static ImBuf *image_get_cached_ibuf(Image *ima, ImageUser *iuser, int *r_entry,
}
else if (ima->source == IMA_SRC_TILED) {
if (ELEM(ima->type, IMA_TYPE_IMAGE, IMA_TYPE_MULTILAYER)) {
- entry = (iuser && iuser->tile) ? iuser->tile : 1001;
+ entry = image_get_tile_number_from_iuser(ima, iuser);
ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry);
if ((ima->type == IMA_TYPE_IMAGE) && ibuf != NULL) {
@@ -5507,7 +5583,7 @@ void BKE_image_user_file_path(ImageUser *iuser, Image *ima, char *filepath)
index = iuser ? iuser->framenr : ima->lastframe;
}
else {
- index = (iuser && iuser->tile) ? iuser->tile : 1001;
+ index = image_get_tile_number_from_iuser(ima, iuser);
}
BLI_path_sequence_decode(filepath, head, tail, &numlen);
diff --git a/source/blender/blenkernel/intern/image_gpu.c b/source/blender/blenkernel/intern/image_gpu.c
index bb7495437bb..d179dd40c33 100644
--- a/source/blender/blenkernel/intern/image_gpu.c
+++ b/source/blender/blenkernel/intern/image_gpu.c
@@ -108,8 +108,9 @@ static GPUTexture *gpu_texture_create_tile_mapping(Image *ima, const int multivi
float array_w = GPU_texture_width(tilearray);
float array_h = GPU_texture_height(tilearray);
+ /* Determine maximum tile number. */
+ BKE_image_sort_tiles(ima);
ImageTile *last_tile = (ImageTile *)ima->tiles.last;
- /* Tiles are sorted by number. */
int max_tile = last_tile->tile_number - 1001;
/* create image */
diff --git a/source/blender/blenkernel/intern/image_save.c b/source/blender/blenkernel/intern/image_save.c
index 360bad3e786..f93ede517a9 100644
--- a/source/blender/blenkernel/intern/image_save.c
+++ b/source/blender/blenkernel/intern/image_save.c
@@ -404,11 +404,13 @@ bool BKE_image_save(
if (ima->source == IMA_SRC_TILED) {
/* Verify filepath for tiles images. */
- if (BLI_path_sequence_decode(opts->filepath, NULL, NULL, NULL) != 1001) {
+ ImageTile *first_tile = ima->tiles.first;
+ if (BLI_path_sequence_decode(opts->filepath, NULL, NULL, NULL) != first_tile->tile_number) {
BKE_reportf(reports,
RPT_ERROR,
- "When saving a tiled image, the path '%s' must contain the UDIM tag 1001",
- opts->filepath);
+ "When saving a tiled image, the path '%s' must contain the UDIM tile number %d",
+ opts->filepath,
+ first_tile->tile_number);
return false;
}
@@ -430,9 +432,14 @@ bool BKE_image_save(
BLI_path_sequence_decode(filepath, head, tail, &numlen);
/* Save all other tiles. */
- LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
- /* Tile 1001 was already saved before the loop. */
- if (tile->tile_number == 1001 || !ok) {
+ int index;
+ LISTBASE_FOREACH_INDEX (ImageTile *, tile, &ima->tiles, index) {
+ /* First tile was already saved before the loop. */
+ if (index == 0) {
+ continue;
+ }
+
+ if (!ok) {
continue;
}
diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c
index 0f8c9bad798..f79058dcf21 100644
--- a/source/blender/blenkernel/intern/key.c
+++ b/source/blender/blenkernel/intern/key.c
@@ -114,27 +114,26 @@ static void shapekey_blend_write(BlendWriter *writer, ID *id, const void *id_add
{
Key *key = (Key *)id;
const bool is_undo = BLO_write_is_undo(writer);
- if (key->id.us > 0 || is_undo) {
- /* write LibData */
- BLO_write_id_struct(writer, Key, id_address, &key->id);
- BKE_id_blend_write(writer, &key->id);
-
- if (key->adt) {
- BKE_animdata_blend_write(writer, key->adt);
- }
-
- /* direct data */
- LISTBASE_FOREACH (KeyBlock *, kb, &key->block) {
- KeyBlock tmp_kb = *kb;
- /* Do not store actual geometry data in case this is a library override ID. */
- if (ID_IS_OVERRIDE_LIBRARY(key) && !is_undo) {
- tmp_kb.totelem = 0;
- tmp_kb.data = NULL;
- }
- BLO_write_struct_at_address(writer, KeyBlock, kb, &tmp_kb);
- if (tmp_kb.data != NULL) {
- BLO_write_raw(writer, tmp_kb.totelem * key->elemsize, tmp_kb.data);
- }
+
+ /* write LibData */
+ BLO_write_id_struct(writer, Key, id_address, &key->id);
+ BKE_id_blend_write(writer, &key->id);
+
+ if (key->adt) {
+ BKE_animdata_blend_write(writer, key->adt);
+ }
+
+ /* direct data */
+ LISTBASE_FOREACH (KeyBlock *, kb, &key->block) {
+ KeyBlock tmp_kb = *kb;
+ /* Do not store actual geometry data in case this is a library override ID. */
+ if (ID_IS_OVERRIDE_LIBRARY(key) && !is_undo) {
+ tmp_kb.totelem = 0;
+ tmp_kb.data = NULL;
+ }
+ BLO_write_struct_at_address(writer, KeyBlock, kb, &tmp_kb);
+ if (tmp_kb.data != NULL) {
+ BLO_write_raw(writer, tmp_kb.totelem * key->elemsize, tmp_kb.data);
}
}
}
@@ -246,7 +245,7 @@ typedef struct WeightsArrayCache {
} WeightsArrayCache;
/** Free (or release) any data used by this shapekey (does not free the key itself). */
-void BKE_key_free(Key *key)
+void BKE_key_free_data(Key *key)
{
shapekey_free_data(&key->id);
}
@@ -2281,15 +2280,8 @@ void BKE_keyblock_mesh_calc_normals(struct KeyBlock *kb,
r_polynors = MEM_mallocN(sizeof(float[3]) * me.totpoly, __func__);
free_polynors = true;
}
- BKE_mesh_calc_normals_poly(me.mvert,
- r_vertnors,
- me.totvert,
- me.mloop,
- me.mpoly,
- me.totloop,
- me.totpoly,
- r_polynors,
- false);
+ BKE_mesh_calc_normals_poly_and_vertex(
+ me.mvert, me.totvert, me.mloop, me.totloop, me.mpoly, me.totpoly, r_polynors, r_vertnors);
if (r_loopnors) {
short(*clnors)[2] = CustomData_get_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL); /* May be NULL. */
diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c
index 9875d776d33..e804f32e5a6 100644
--- a/source/blender/blenkernel/intern/lattice.c
+++ b/source/blender/blenkernel/intern/lattice.c
@@ -137,26 +137,25 @@ static void lattice_foreach_id(ID *id, LibraryForeachIDData *data)
static void lattice_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
Lattice *lt = (Lattice *)id;
- if (lt->id.us > 0 || BLO_write_is_undo(writer)) {
- /* Clean up, important in undo case to reduce false detection of changed datablocks. */
- lt->editlatt = NULL;
- lt->batch_cache = NULL;
-
- /* write LibData */
- BLO_write_id_struct(writer, Lattice, id_address, &lt->id);
- BKE_id_blend_write(writer, &lt->id);
-
- /* write animdata */
- if (lt->adt) {
- BKE_animdata_blend_write(writer, lt->adt);
- }
- /* direct data */
- BLO_write_struct_array(writer, BPoint, lt->pntsu * lt->pntsv * lt->pntsw, lt->def);
+ /* Clean up, important in undo case to reduce false detection of changed datablocks. */
+ lt->editlatt = NULL;
+ lt->batch_cache = NULL;
+
+ /* write LibData */
+ BLO_write_id_struct(writer, Lattice, id_address, &lt->id);
+ BKE_id_blend_write(writer, &lt->id);
- BKE_defbase_blend_write(writer, &lt->vertex_group_names);
- BKE_defvert_blend_write(writer, lt->pntsu * lt->pntsv * lt->pntsw, lt->dvert);
+ /* write animdata */
+ if (lt->adt) {
+ BKE_animdata_blend_write(writer, lt->adt);
}
+
+ /* direct data */
+ BLO_write_struct_array(writer, BPoint, lt->pntsu * lt->pntsv * lt->pntsw, lt->def);
+
+ BKE_defbase_blend_write(writer, &lt->vertex_group_names);
+ BKE_defvert_blend_write(writer, lt->pntsu * lt->pntsv * lt->pntsw, lt->dvert);
}
static void lattice_blend_read_data(BlendDataReader *reader, ID *id)
diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c
index ae1863f0a47..b489675cd74 100644
--- a/source/blender/blenkernel/intern/layer.c
+++ b/source/blender/blenkernel/intern/layer.c
@@ -602,7 +602,7 @@ static bool layer_collection_hidden(ViewLayer *view_layer, LayerCollection *lc)
}
/* Check visiblilty restriction flags */
- if (lc->flag & LAYER_COLLECTION_HIDE || lc->collection->flag & COLLECTION_RESTRICT_VIEWPORT) {
+ if (lc->flag & LAYER_COLLECTION_HIDE || lc->collection->flag & COLLECTION_HIDE_VIEWPORT) {
return true;
}
@@ -1005,22 +1005,22 @@ static void layer_collection_objects_sync(ViewLayer *view_layer,
BLI_addtail(r_lb_new_object_bases, base);
}
- if ((collection_restrict & COLLECTION_RESTRICT_VIEWPORT) == 0) {
+ if ((collection_restrict & COLLECTION_HIDE_VIEWPORT) == 0) {
base->flag_from_collection |= (BASE_ENABLED_VIEWPORT | BASE_VISIBLE_DEPSGRAPH);
if ((layer_restrict & LAYER_COLLECTION_HIDE) == 0) {
base->flag_from_collection |= BASE_VISIBLE_VIEWLAYER;
}
- if (((collection_restrict & COLLECTION_RESTRICT_SELECT) == 0)) {
+ if (((collection_restrict & COLLECTION_HIDE_SELECT) == 0)) {
base->flag_from_collection |= BASE_SELECTABLE;
}
}
- if ((collection_restrict & COLLECTION_RESTRICT_RENDER) == 0) {
+ if ((collection_restrict & COLLECTION_HIDE_RENDER) == 0) {
base->flag_from_collection |= BASE_ENABLED_RENDER;
}
/* Holdout and indirect only */
- if (layer->flag & LAYER_COLLECTION_HOLDOUT) {
+ if ((layer->flag & LAYER_COLLECTION_HOLDOUT) || (base->object->visibility_flag & OB_HOLDOUT)) {
base->flag_from_collection |= BASE_HOLDOUT;
}
if (layer->flag & LAYER_COLLECTION_INDIRECT_ONLY) {
@@ -1150,11 +1150,11 @@ static void layer_collection_sync(ViewLayer *view_layer,
/* We separate restrict viewport and visible view layer because a layer collection can be
* hidden in the view layer yet (locally) visible in a viewport (if it is not restricted). */
- if (child_collection_restrict & COLLECTION_RESTRICT_VIEWPORT) {
- child_layer->runtime_flag |= LAYER_COLLECTION_RESTRICT_VIEWPORT;
+ if (child_collection_restrict & COLLECTION_HIDE_VIEWPORT) {
+ child_layer->runtime_flag |= LAYER_COLLECTION_HIDE_VIEWPORT;
}
- if (((child_layer->runtime_flag & LAYER_COLLECTION_RESTRICT_VIEWPORT) == 0) &&
+ if (((child_layer->runtime_flag & LAYER_COLLECTION_HIDE_VIEWPORT) == 0) &&
((child_layer_restrict & LAYER_COLLECTION_HIDE) == 0)) {
child_layer->runtime_flag |= LAYER_COLLECTION_VISIBLE_VIEW_LAYER;
}
@@ -1174,6 +1174,52 @@ static void layer_collection_sync(ViewLayer *view_layer,
parent_local_collections_bits);
}
+#ifndef NDEBUG
+static bool view_layer_objects_base_cache_validate(ViewLayer *view_layer, LayerCollection *layer)
+{
+ bool is_valid = true;
+
+ if (layer == NULL) {
+ layer = view_layer->layer_collections.first;
+ }
+
+ /* Only check for a collection's objects if its layer is not excluded. */
+ if ((layer->flag & LAYER_COLLECTION_EXCLUDE) == 0) {
+ LISTBASE_FOREACH (CollectionObject *, cob, &layer->collection->gobject) {
+ if (cob->ob == NULL) {
+ continue;
+ }
+ if (BLI_ghash_lookup(view_layer->object_bases_hash, cob->ob) == NULL) {
+ CLOG_FATAL(
+ &LOG,
+ "Object '%s' from collection '%s' has no entry in view layer's object bases cache",
+ cob->ob->id.name + 2,
+ layer->collection->id.name + 2);
+ is_valid = false;
+ break;
+ }
+ }
+ }
+
+ if (is_valid) {
+ LISTBASE_FOREACH (LayerCollection *, layer_child, &layer->layer_collections) {
+ if (!view_layer_objects_base_cache_validate(view_layer, layer_child)) {
+ is_valid = false;
+ break;
+ }
+ }
+ }
+
+ return is_valid;
+}
+#else
+static bool view_layer_objects_base_cache_validate(ViewLayer *UNUSED(view_layer),
+ LayerCollection *UNUSED(layer))
+{
+ return true;
+}
+#endif
+
/**
* Update view layer collection tree from collections used in the scene.
* This is used when collections are removed or added, both while editing
@@ -1240,6 +1286,12 @@ void BKE_layer_collection_sync(const Scene *scene, ViewLayer *view_layer)
}
if (base->object) {
+ /* Those asserts are commented, since they are too expensive to perform even in debug, as
+ * this layer resync function currently gets called way too often. */
+#if 0
+ BLI_assert(BLI_findindex(&new_object_bases, base) == -1);
+ BLI_assert(BLI_findptr(&new_object_bases, base->object, offsetof(Base, object)) == NULL);
+#endif
BLI_ghash_remove(view_layer->object_bases_hash, base->object, NULL, NULL);
}
}
@@ -1247,6 +1299,8 @@ void BKE_layer_collection_sync(const Scene *scene, ViewLayer *view_layer)
BLI_freelistN(&view_layer->object_bases);
view_layer->object_bases = new_object_bases;
+ view_layer_objects_base_cache_validate(view_layer, NULL);
+
LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
BKE_base_eval_flags(base);
}
@@ -1333,7 +1387,7 @@ void BKE_main_collection_sync_remap(const Main *bmain)
*/
bool BKE_layer_collection_objects_select(ViewLayer *view_layer, LayerCollection *lc, bool deselect)
{
- if (lc->collection->flag & COLLECTION_RESTRICT_SELECT) {
+ if (lc->collection->flag & COLLECTION_HIDE_SELECT) {
return false;
}
@@ -1369,7 +1423,7 @@ bool BKE_layer_collection_objects_select(ViewLayer *view_layer, LayerCollection
bool BKE_layer_collection_has_selected_objects(ViewLayer *view_layer, LayerCollection *lc)
{
- if (lc->collection->flag & COLLECTION_RESTRICT_SELECT) {
+ if (lc->collection->flag & COLLECTION_HIDE_SELECT) {
return false;
}
@@ -1457,7 +1511,7 @@ bool BKE_object_is_visible_in_viewport(const View3D *v3d, const struct Object *o
{
BLI_assert(v3d != NULL);
- if (ob->restrictflag & OB_RESTRICT_VIEWPORT) {
+ if (ob->visibility_flag & OB_HIDE_VIEWPORT) {
return false;
}
@@ -2146,14 +2200,14 @@ void BKE_base_eval_flags(Base *base)
base->flag |= (base->flag_from_collection & g_base_collection_flags);
/* Apply object restrictions. */
- const int object_restrict = base->object->restrictflag;
- if (object_restrict & OB_RESTRICT_VIEWPORT) {
+ const int object_restrict = base->object->visibility_flag;
+ if (object_restrict & OB_HIDE_VIEWPORT) {
base->flag &= ~BASE_ENABLED_VIEWPORT;
}
- if (object_restrict & OB_RESTRICT_RENDER) {
+ if (object_restrict & OB_HIDE_RENDER) {
base->flag &= ~BASE_ENABLED_RENDER;
}
- if (object_restrict & OB_RESTRICT_SELECT) {
+ if (object_restrict & OB_HIDE_SELECT) {
base->flag &= ~BASE_SELECTABLE;
}
diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c
index 62d29188c5a..0f880d16358 100644
--- a/source/blender/blenkernel/intern/lib_id.c
+++ b/source/blender/blenkernel/intern/lib_id.c
@@ -73,6 +73,7 @@
#include "BKE_rigidbody.h"
#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_build.h"
#include "RNA_access.h"
@@ -141,7 +142,8 @@ static int lib_id_clear_library_data_users_update_cb(LibraryIDLinkCallbackData *
{
ID *id = cb_data->user_data;
if (*cb_data->id_pointer == id) {
- DEG_id_tag_update_ex(cb_data->bmain, cb_data->id_owner, ID_RECALC_TAG_FOR_UNDO);
+ DEG_id_tag_update_ex(
+ cb_data->bmain, cb_data->id_owner, ID_RECALC_TAG_FOR_UNDO | ID_RECALC_COPY_ON_WRITE);
return IDWALK_RET_STOP_ITER;
}
return IDWALK_RET_NOP;
@@ -193,6 +195,8 @@ static void lib_id_clear_library_data_ex(Main *bmain, ID *id)
if (key != NULL) {
lib_id_clear_library_data_ex(bmain, &key->id);
}
+
+ DEG_relations_tag_update(bmain);
}
void BKE_lib_id_clear_library_data(Main *bmain, ID *id)
@@ -1317,14 +1321,6 @@ void *BKE_libblock_copy(Main *bmain, const ID *id)
return idn;
}
-/* XXX TODO: get rid of this useless wrapper at some point... */
-void *BKE_libblock_copy_for_localize(const ID *id)
-{
- ID *idn;
- BKE_libblock_copy_ex(NULL, id, &idn, LIB_ID_COPY_LOCALIZE | LIB_ID_COPY_NO_ANIMDATA);
- return idn;
-}
-
/* ***************** ID ************************ */
ID *BKE_libblock_find_name(struct Main *bmain, const short type, const char *name)
{
diff --git a/source/blender/blenkernel/intern/lib_id_delete.c b/source/blender/blenkernel/intern/lib_id_delete.c
index a9407860c06..43afac5a376 100644
--- a/source/blender/blenkernel/intern/lib_id_delete.c
+++ b/source/blender/blenkernel/intern/lib_id_delete.c
@@ -142,14 +142,7 @@ void BKE_id_free_ex(Main *bmain, void *idv, int flag, const bool use_flag_from_i
DEG_id_type_tag(bmain, type);
}
-#ifdef WITH_PYTHON
-# ifdef WITH_PYTHON_SAFETY
- BPY_id_release(id);
-# endif
- if (id->py_instance) {
- BPY_DECREF_RNA_INVALIDATE(id->py_instance);
- }
-#endif
+ BKE_libblock_free_data_py(id);
Key *key = ((flag & LIB_ID_FREE_NO_MAIN) == 0) ? BKE_key_from_id(id) : NULL;
@@ -406,3 +399,29 @@ size_t BKE_id_multi_tagged_delete(Main *bmain)
{
return id_delete(bmain, true);
}
+
+/* -------------------------------------------------------------------- */
+/** \name Python Data Handling
+ * \{ */
+
+/**
+ * In most cases #BKE_id_free_ex handles this, when lower level functions are called directly
+ * this function will need to be called too, if Python has access to the data.
+ *
+ * ID data-blocks such as #Material.nodetree are not stored in #Main.
+ */
+void BKE_libblock_free_data_py(ID *id)
+{
+#ifdef WITH_PYTHON
+# ifdef WITH_PYTHON_SAFETY
+ BPY_id_release(id);
+# endif
+ if (id->py_instance) {
+ BPY_DECREF_RNA_INVALIDATE(id->py_instance);
+ }
+#else
+ UNUSED_VARS(id);
+#endif
+}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c
index 8e67547b719..8083585b594 100644
--- a/source/blender/blenkernel/intern/lib_override.c
+++ b/source/blender/blenkernel/intern/lib_override.c
@@ -571,7 +571,7 @@ static void lib_override_linked_group_tag_recursive(LibOverrideGroupTagData *dat
* would use one of those.
* NOTE: missing IDs (aka placeholders) are never overridden. */
if (ELEM(GS(to_id->name), ID_OB, ID_GR)) {
- if ((to_id->tag & LIB_TAG_MISSING)) {
+ if (to_id->tag & LIB_TAG_MISSING) {
to_id->tag |= missing_tag;
}
else {
@@ -604,7 +604,7 @@ static void lib_override_linked_group_tag(LibOverrideGroupTagData *data)
const bool is_resync = data->is_resync;
BLI_assert(!data->is_override);
- if ((id_root->tag & LIB_TAG_MISSING)) {
+ if (id_root->tag & LIB_TAG_MISSING) {
id_root->tag |= data->missing_tag;
}
else {
@@ -654,7 +654,7 @@ static void lib_override_linked_group_tag(LibOverrideGroupTagData *data)
if (instantiating_collection == NULL &&
instantiating_collection_override_candidate != NULL) {
- if ((instantiating_collection_override_candidate->id.tag & LIB_TAG_MISSING)) {
+ if (instantiating_collection_override_candidate->id.tag & LIB_TAG_MISSING) {
instantiating_collection_override_candidate->id.tag |= data->missing_tag;
}
else {
@@ -730,7 +730,7 @@ static void lib_override_overrides_group_tag(LibOverrideGroupTagData *data)
BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root));
BLI_assert(data->is_override);
- if ((id_root->override_library->reference->tag & LIB_TAG_MISSING)) {
+ if (id_root->override_library->reference->tag & LIB_TAG_MISSING) {
id_root->tag |= data->missing_tag;
}
else {
@@ -855,8 +855,8 @@ static void lib_override_library_create_post_process(Main *bmain,
default_instantiating_collection = BKE_collection_add(
bmain, (Collection *)id_root, "OVERRIDE_HIDDEN");
/* Hide the collection from viewport and render. */
- default_instantiating_collection->flag |= COLLECTION_RESTRICT_VIEWPORT |
- COLLECTION_RESTRICT_RENDER;
+ default_instantiating_collection->flag |= COLLECTION_HIDE_VIEWPORT |
+ COLLECTION_HIDE_RENDER;
break;
}
case ID_OB: {
@@ -1020,7 +1020,7 @@ bool BKE_lib_override_library_resync(Main *bmain,
if (id_root_reference->tag & LIB_TAG_MISSING) {
BKE_reportf(reports != NULL ? reports->reports : NULL,
RPT_ERROR,
- "impossible to resync data-block %s and its dependencies, as its linked reference "
+ "Impossible to resync data-block %s and its dependencies, as its linked reference "
"is missing",
id_root->name + 2);
return false;
@@ -1599,6 +1599,17 @@ static void lib_override_library_main_resync_on_library_indirect_level(
(!ID_IS_LINKED(id) && library_indirect_level != 0)) {
continue;
}
+
+ /* We cannot resync a scene that is currently active. */
+ if (id == &scene->id) {
+ id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
+ BKE_reportf(reports->reports,
+ RPT_WARNING,
+ "Scene '%s' was not resynced as it is the currently active one",
+ scene->id.name + 2);
+ continue;
+ }
+
Library *library = id->lib;
int level = 0;
@@ -1620,7 +1631,7 @@ static void lib_override_library_main_resync_on_library_indirect_level(
CLOG_INFO(&LOG, 2, "\tSuccess: %d", success);
if (success) {
reports->count.resynced_lib_overrides++;
- if (library_indirect_level > 0 &&
+ if (library_indirect_level > 0 && reports->do_resynced_lib_overrides_libraries_list &&
BLI_linklist_index(reports->resynced_lib_overrides_libraries, library) < 0) {
BLI_linklist_prepend(&reports->resynced_lib_overrides_libraries, library);
reports->resynced_lib_overrides_libraries_count++;
@@ -1731,8 +1742,7 @@ void BKE_lib_override_library_main_resync(Main *bmain,
override_resync_residual_storage = BKE_collection_add(
bmain, scene->master_collection, OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME);
/* Hide the collection from viewport and render. */
- override_resync_residual_storage->flag |= COLLECTION_RESTRICT_VIEWPORT |
- COLLECTION_RESTRICT_RENDER;
+ override_resync_residual_storage->flag |= COLLECTION_HIDE_VIEWPORT | COLLECTION_HIDE_RENDER;
}
/* Necessary to improve performances, and prevent layers matching override sub-collections to be
diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c
index 977e53c8474..9400458376d 100644
--- a/source/blender/blenkernel/intern/lib_query.c
+++ b/source/blender/blenkernel/intern/lib_query.c
@@ -88,8 +88,8 @@ bool BKE_lib_query_foreachid_process(LibraryForeachIDData *data, ID **id_pp, int
/* Update the callback flags with some extra information regarding overrides: all 'loopback',
* 'internal', 'embedded' etc. ID pointers are never overridable. */
- if (cb_flag & (IDWALK_CB_INTERNAL | IDWALK_CB_EMBEDDED | IDWALK_CB_LOOPBACK |
- IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) {
+ if (cb_flag &
+ (IDWALK_CB_INTERNAL | IDWALK_CB_LOOPBACK | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) {
cb_flag |= IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE;
}
diff --git a/source/blender/blenkernel/intern/light.c b/source/blender/blenkernel/intern/light.c
index d91d80ac683..c2b71b85973 100644
--- a/source/blender/blenkernel/intern/light.c
+++ b/source/blender/blenkernel/intern/light.c
@@ -136,27 +136,26 @@ static void light_foreach_id(ID *id, LibraryForeachIDData *data)
static void light_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
Light *la = (Light *)id;
- if (la->id.us > 0 || BLO_write_is_undo(writer)) {
- /* write LibData */
- BLO_write_id_struct(writer, Light, id_address, &la->id);
- BKE_id_blend_write(writer, &la->id);
- if (la->adt) {
- BKE_animdata_blend_write(writer, la->adt);
- }
+ /* write LibData */
+ BLO_write_id_struct(writer, Light, id_address, &la->id);
+ BKE_id_blend_write(writer, &la->id);
- if (la->curfalloff) {
- BKE_curvemapping_blend_write(writer, la->curfalloff);
- }
+ if (la->adt) {
+ BKE_animdata_blend_write(writer, la->adt);
+ }
- /* Node-tree is integral part of lights, no libdata. */
- if (la->nodetree) {
- BLO_write_struct(writer, bNodeTree, la->nodetree);
- ntreeBlendWrite(writer, la->nodetree);
- }
+ if (la->curfalloff) {
+ BKE_curvemapping_blend_write(writer, la->curfalloff);
+ }
- BKE_previewimg_blend_write(writer, la->preview);
+ /* Node-tree is integral part of lights, no libdata. */
+ if (la->nodetree) {
+ BLO_write_struct(writer, bNodeTree, la->nodetree);
+ ntreeBlendWrite(writer, la->nodetree);
}
+
+ BKE_previewimg_blend_write(writer, la->preview);
}
static void light_blend_read_data(BlendDataReader *reader, ID *id)
diff --git a/source/blender/blenkernel/intern/lightprobe.c b/source/blender/blenkernel/intern/lightprobe.c
index b09aed82921..15733af8ef0 100644
--- a/source/blender/blenkernel/intern/lightprobe.c
+++ b/source/blender/blenkernel/intern/lightprobe.c
@@ -60,14 +60,13 @@ static void lightprobe_foreach_id(ID *id, LibraryForeachIDData *data)
static void lightprobe_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
LightProbe *prb = (LightProbe *)id;
- if (prb->id.us > 0 || BLO_write_is_undo(writer)) {
- /* write LibData */
- BLO_write_id_struct(writer, LightProbe, id_address, &prb->id);
- BKE_id_blend_write(writer, &prb->id);
-
- if (prb->adt) {
- BKE_animdata_blend_write(writer, prb->adt);
- }
+
+ /* write LibData */
+ BLO_write_id_struct(writer, LightProbe, id_address, &prb->id);
+ BKE_id_blend_write(writer, &prb->id);
+
+ if (prb->adt) {
+ BKE_animdata_blend_write(writer, prb->adt);
}
}
diff --git a/source/blender/blenkernel/intern/linestyle.c b/source/blender/blenkernel/intern/linestyle.c
index 26d9ab7a8c7..19030fca38b 100644
--- a/source/blender/blenkernel/intern/linestyle.c
+++ b/source/blender/blenkernel/intern/linestyle.c
@@ -457,28 +457,27 @@ static void write_linestyle_geometry_modifiers(BlendWriter *writer, ListBase *mo
static void linestyle_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
FreestyleLineStyle *linestyle = (FreestyleLineStyle *)id;
- if (linestyle->id.us > 0 || BLO_write_is_undo(writer)) {
- BLO_write_id_struct(writer, FreestyleLineStyle, id_address, &linestyle->id);
- BKE_id_blend_write(writer, &linestyle->id);
- if (linestyle->adt) {
- BKE_animdata_blend_write(writer, linestyle->adt);
- }
+ BLO_write_id_struct(writer, FreestyleLineStyle, id_address, &linestyle->id);
+ BKE_id_blend_write(writer, &linestyle->id);
- write_linestyle_color_modifiers(writer, &linestyle->color_modifiers);
- write_linestyle_alpha_modifiers(writer, &linestyle->alpha_modifiers);
- write_linestyle_thickness_modifiers(writer, &linestyle->thickness_modifiers);
- write_linestyle_geometry_modifiers(writer, &linestyle->geometry_modifiers);
- for (int a = 0; a < MAX_MTEX; a++) {
- if (linestyle->mtex[a]) {
- BLO_write_struct(writer, MTex, linestyle->mtex[a]);
- }
- }
- if (linestyle->nodetree) {
- BLO_write_struct(writer, bNodeTree, linestyle->nodetree);
- ntreeBlendWrite(writer, linestyle->nodetree);
+ if (linestyle->adt) {
+ BKE_animdata_blend_write(writer, linestyle->adt);
+ }
+
+ write_linestyle_color_modifiers(writer, &linestyle->color_modifiers);
+ write_linestyle_alpha_modifiers(writer, &linestyle->alpha_modifiers);
+ write_linestyle_thickness_modifiers(writer, &linestyle->thickness_modifiers);
+ write_linestyle_geometry_modifiers(writer, &linestyle->geometry_modifiers);
+ for (int a = 0; a < MAX_MTEX; a++) {
+ if (linestyle->mtex[a]) {
+ BLO_write_struct(writer, MTex, linestyle->mtex[a]);
}
}
+ if (linestyle->nodetree) {
+ BLO_write_struct(writer, bNodeTree, linestyle->nodetree);
+ ntreeBlendWrite(writer, linestyle->nodetree);
+ }
}
static void direct_link_linestyle_color_modifier(BlendDataReader *reader,
diff --git a/source/blender/blenkernel/intern/mask.c b/source/blender/blenkernel/intern/mask.c
index 34dd38164c2..a93fcb6e8e0 100644
--- a/source/blender/blenkernel/intern/mask.c
+++ b/source/blender/blenkernel/intern/mask.c
@@ -101,48 +101,47 @@ static void mask_foreach_id(ID *id, LibraryForeachIDData *data)
static void mask_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
Mask *mask = (Mask *)id;
- if (mask->id.us > 0 || BLO_write_is_undo(writer)) {
- MaskLayer *masklay;
- BLO_write_id_struct(writer, Mask, id_address, &mask->id);
- BKE_id_blend_write(writer, &mask->id);
+ MaskLayer *masklay;
- if (mask->adt) {
- BKE_animdata_blend_write(writer, mask->adt);
- }
+ BLO_write_id_struct(writer, Mask, id_address, &mask->id);
+ BKE_id_blend_write(writer, &mask->id);
- for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
- MaskSpline *spline;
- MaskLayerShape *masklay_shape;
+ if (mask->adt) {
+ BKE_animdata_blend_write(writer, mask->adt);
+ }
- BLO_write_struct(writer, MaskLayer, masklay);
+ for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
+ MaskSpline *spline;
+ MaskLayerShape *masklay_shape;
- for (spline = masklay->splines.first; spline; spline = spline->next) {
- int i;
+ BLO_write_struct(writer, MaskLayer, masklay);
- void *points_deform = spline->points_deform;
- spline->points_deform = NULL;
+ for (spline = masklay->splines.first; spline; spline = spline->next) {
+ int i;
- BLO_write_struct(writer, MaskSpline, spline);
- BLO_write_struct_array(writer, MaskSplinePoint, spline->tot_point, spline->points);
+ void *points_deform = spline->points_deform;
+ spline->points_deform = NULL;
- spline->points_deform = points_deform;
+ BLO_write_struct(writer, MaskSpline, spline);
+ BLO_write_struct_array(writer, MaskSplinePoint, spline->tot_point, spline->points);
- for (i = 0; i < spline->tot_point; i++) {
- MaskSplinePoint *point = &spline->points[i];
+ spline->points_deform = points_deform;
- if (point->tot_uw) {
- BLO_write_struct_array(writer, MaskSplinePointUW, point->tot_uw, point->uw);
- }
+ for (i = 0; i < spline->tot_point; i++) {
+ MaskSplinePoint *point = &spline->points[i];
+
+ if (point->tot_uw) {
+ BLO_write_struct_array(writer, MaskSplinePointUW, point->tot_uw, point->uw);
}
}
+ }
- for (masklay_shape = masklay->splines_shapes.first; masklay_shape;
- masklay_shape = masklay_shape->next) {
- BLO_write_struct(writer, MaskLayerShape, masklay_shape);
- BLO_write_float_array(
- writer, masklay_shape->tot_vert * MASK_OBJECT_SHAPE_ELEM_SIZE, masklay_shape->data);
- }
+ for (masklay_shape = masklay->splines_shapes.first; masklay_shape;
+ masklay_shape = masklay_shape->next) {
+ BLO_write_struct(writer, MaskLayerShape, masklay_shape);
+ BLO_write_float_array(
+ writer, masklay_shape->tot_vert * MASK_OBJECT_SHAPE_ELEM_SIZE, masklay_shape->data);
}
}
}
@@ -429,7 +428,7 @@ MaskLayer *BKE_mask_layer_copy(const MaskLayer *masklay)
masklay_new->blend_flag = masklay->blend_flag;
masklay_new->flag = masklay->flag;
masklay_new->falloff = masklay->falloff;
- masklay_new->restrictflag = masklay->restrictflag;
+ masklay_new->visibility_flag = masklay->visibility_flag;
for (spline = masklay->splines.first; spline; spline = spline->next) {
MaskSpline *spline_new = BKE_mask_spline_copy(spline);
@@ -2092,7 +2091,7 @@ void BKE_mask_clipboard_copy_from_layer(MaskLayer *mask_layer)
MaskSpline *spline;
/* Nothing to do if selection if disabled for the given layer. */
- if (mask_layer->restrictflag & MASK_RESTRICT_SELECT) {
+ if (mask_layer->visibility_flag & MASK_HIDE_SELECT) {
return;
}
diff --git a/source/blender/blenkernel/intern/mask_rasterize.c b/source/blender/blenkernel/intern/mask_rasterize.c
index 81c161a4a7d..e04e5fceec6 100644
--- a/source/blender/blenkernel/intern/mask_rasterize.c
+++ b/source/blender/blenkernel/intern/mask_rasterize.c
@@ -106,7 +106,7 @@
/* for debugging add... */
#ifndef NDEBUG
-/* printf("%u %u %u %u\n", _t[0], _t[1], _t[2], _t[3]); \ */
+// printf("%u %u %u %u\n", _t[0], _t[1], _t[2], _t[3]);
# define FACE_ASSERT(face, vert_max) \
{ \
unsigned int *_t = face; \
@@ -292,10 +292,10 @@ static void maskrasterize_spline_differentiate_point_outset(float (*diff_feather
co_curr = diff_points[k_curr];
co_next = diff_points[k_next];
- /* sub_v2_v2v2(d_prev, co_prev, co_curr); */ /* precalc */
+ // sub_v2_v2v2(d_prev, co_prev, co_curr); /* precalc */
sub_v2_v2v2(d_next, co_curr, co_next);
- /* normalize_v2(d_prev); */ /* precalc */
+ // normalize_v2(d_prev); /* precalc */
normalize_v2(d_next);
if ((do_test == false) ||
@@ -619,7 +619,7 @@ void BKE_maskrasterize_handle_init(MaskRasterHandle *mr_handle,
unsigned int tot_boundary_found = 0;
#endif
- if (masklay->restrictflag & MASK_RESTRICT_RENDER) {
+ if (masklay->visibility_flag & MASK_HIDE_RENDER) {
/* skip the layer */
mr_handle->layers_tot--;
masklay_index--;
@@ -1213,7 +1213,7 @@ void BKE_maskrasterize_handle_init(MaskRasterHandle *mr_handle,
layer->falloff = masklay->falloff;
}
- /* printf("tris %d, feather tris %d\n", sf_tri_tot, tot_feather_quads); */
+ // printf("tris %d, feather tris %d\n", sf_tri_tot, tot_feather_quads);
}
/* add trianges */
diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c
index 4f0b2a718ed..13b5bca5638 100644
--- a/source/blender/blenkernel/intern/material.c
+++ b/source/blender/blenkernel/intern/material.c
@@ -179,31 +179,30 @@ static void material_foreach_id(ID *id, LibraryForeachIDData *data)
static void material_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
Material *ma = (Material *)id;
- if (ma->id.us > 0 || BLO_write_is_undo(writer)) {
- /* Clean up, important in undo case to reduce false detection of changed datablocks. */
- ma->texpaintslot = NULL;
- BLI_listbase_clear(&ma->gpumaterial);
- /* write LibData */
- BLO_write_id_struct(writer, Material, id_address, &ma->id);
- BKE_id_blend_write(writer, &ma->id);
+ /* Clean up, important in undo case to reduce false detection of changed datablocks. */
+ ma->texpaintslot = NULL;
+ BLI_listbase_clear(&ma->gpumaterial);
- if (ma->adt) {
- BKE_animdata_blend_write(writer, ma->adt);
- }
+ /* write LibData */
+ BLO_write_id_struct(writer, Material, id_address, &ma->id);
+ BKE_id_blend_write(writer, &ma->id);
- /* nodetree is integral part of material, no libdata */
- if (ma->nodetree) {
- BLO_write_struct(writer, bNodeTree, ma->nodetree);
- ntreeBlendWrite(writer, ma->nodetree);
- }
+ if (ma->adt) {
+ BKE_animdata_blend_write(writer, ma->adt);
+ }
- BKE_previewimg_blend_write(writer, ma->preview);
+ /* nodetree is integral part of material, no libdata */
+ if (ma->nodetree) {
+ BLO_write_struct(writer, bNodeTree, ma->nodetree);
+ ntreeBlendWrite(writer, ma->nodetree);
+ }
- /* grease pencil settings */
- if (ma->gp_style) {
- BLO_write_struct(writer, MaterialGPencilStyle, ma->gp_style);
- }
+ BKE_previewimg_blend_write(writer, ma->preview);
+
+ /* grease pencil settings */
+ if (ma->gp_style) {
+ BLO_write_struct(writer, MaterialGPencilStyle, ma->gp_style);
}
}
@@ -1802,6 +1801,7 @@ void BKE_material_copybuf_free(void)
{
if (matcopybuf.nodetree) {
ntreeFreeLocalTree(matcopybuf.nodetree);
+ BLI_assert(!matcopybuf.nodetree->id.py_instance); /* Or call #BKE_libblock_free_data_py. */
MEM_freeN(matcopybuf.nodetree);
matcopybuf.nodetree = NULL;
}
diff --git a/source/blender/blenkernel/intern/mball.c b/source/blender/blenkernel/intern/mball.c
index 6a2b56306d6..45cf0f17840 100644
--- a/source/blender/blenkernel/intern/mball.c
+++ b/source/blender/blenkernel/intern/mball.c
@@ -119,28 +119,27 @@ static void metaball_foreach_id(ID *id, LibraryForeachIDData *data)
static void metaball_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
MetaBall *mb = (MetaBall *)id;
- if (mb->id.us > 0 || BLO_write_is_undo(writer)) {
- /* Clean up, important in undo case to reduce false detection of changed datablocks. */
- BLI_listbase_clear(&mb->disp);
- mb->editelems = NULL;
- /* Must always be cleared (meta's don't have their own edit-data). */
- mb->needs_flush_to_id = 0;
- mb->lastelem = NULL;
- mb->batch_cache = NULL;
-
- /* write LibData */
- BLO_write_id_struct(writer, MetaBall, id_address, &mb->id);
- BKE_id_blend_write(writer, &mb->id);
-
- /* direct data */
- BLO_write_pointer_array(writer, mb->totcol, mb->mat);
- if (mb->adt) {
- BKE_animdata_blend_write(writer, mb->adt);
- }
- LISTBASE_FOREACH (MetaElem *, ml, &mb->elems) {
- BLO_write_struct(writer, MetaElem, ml);
- }
+ /* Clean up, important in undo case to reduce false detection of changed datablocks. */
+ BLI_listbase_clear(&mb->disp);
+ mb->editelems = NULL;
+ /* Must always be cleared (meta's don't have their own edit-data). */
+ mb->needs_flush_to_id = 0;
+ mb->lastelem = NULL;
+ mb->batch_cache = NULL;
+
+ /* write LibData */
+ BLO_write_id_struct(writer, MetaBall, id_address, &mb->id);
+ BKE_id_blend_write(writer, &mb->id);
+
+ /* direct data */
+ BLO_write_pointer_array(writer, mb->totcol, mb->mat);
+ if (mb->adt) {
+ BKE_animdata_blend_write(writer, mb->adt);
+ }
+
+ LISTBASE_FOREACH (MetaElem *, ml, &mb->elems) {
+ BLO_write_struct(writer, MetaElem, ml);
}
}
@@ -289,7 +288,7 @@ void BKE_mball_texspace_calc(Object *ob)
bb = ob->runtime.bb;
/* Weird one, this. */
- /* INIT_MINMAX(min, max); */
+ // INIT_MINMAX(min, max);
(min)[0] = (min)[1] = (min)[2] = 1.0e30f;
(max)[0] = (max)[1] = (max)[2] = -1.0e30f;
diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c
index 8d74002ad79..eb8e6aad736 100644
--- a/source/blender/blenkernel/intern/mesh.c
+++ b/source/blender/blenkernel/intern/mesh.c
@@ -179,95 +179,90 @@ static void mesh_blend_write(BlendWriter *writer, ID *id, const void *id_address
{
Mesh *mesh = (Mesh *)id;
const bool is_undo = BLO_write_is_undo(writer);
- if (mesh->id.us > 0 || is_undo) {
- CustomDataLayer *vlayers = NULL, vlayers_buff[CD_TEMP_CHUNK_SIZE];
- CustomDataLayer *elayers = NULL, elayers_buff[CD_TEMP_CHUNK_SIZE];
- CustomDataLayer *flayers = NULL, flayers_buff[CD_TEMP_CHUNK_SIZE];
- CustomDataLayer *llayers = NULL, llayers_buff[CD_TEMP_CHUNK_SIZE];
- CustomDataLayer *players = NULL, players_buff[CD_TEMP_CHUNK_SIZE];
-
- /* cache only - don't write */
- mesh->mface = NULL;
- mesh->totface = 0;
- memset(&mesh->fdata, 0, sizeof(mesh->fdata));
- memset(&mesh->runtime, 0, sizeof(mesh->runtime));
- flayers = flayers_buff;
-
- /* Do not store actual geometry data in case this is a library override ID. */
- if (ID_IS_OVERRIDE_LIBRARY(mesh) && !is_undo) {
- mesh->mvert = NULL;
- mesh->totvert = 0;
- memset(&mesh->vdata, 0, sizeof(mesh->vdata));
- vlayers = vlayers_buff;
-
- mesh->medge = NULL;
- mesh->totedge = 0;
- memset(&mesh->edata, 0, sizeof(mesh->edata));
- elayers = elayers_buff;
-
- mesh->mloop = NULL;
- mesh->totloop = 0;
- memset(&mesh->ldata, 0, sizeof(mesh->ldata));
- llayers = llayers_buff;
-
- mesh->mpoly = NULL;
- mesh->totpoly = 0;
- memset(&mesh->pdata, 0, sizeof(mesh->pdata));
- players = players_buff;
- }
- else {
- CustomData_blend_write_prepare(
- &mesh->vdata, &vlayers, vlayers_buff, ARRAY_SIZE(vlayers_buff));
- CustomData_blend_write_prepare(
- &mesh->edata, &elayers, elayers_buff, ARRAY_SIZE(elayers_buff));
- CustomData_blend_write_prepare(
- &mesh->ldata, &llayers, llayers_buff, ARRAY_SIZE(llayers_buff));
- CustomData_blend_write_prepare(
- &mesh->pdata, &players, players_buff, ARRAY_SIZE(players_buff));
- }
- BLO_write_id_struct(writer, Mesh, id_address, &mesh->id);
- BKE_id_blend_write(writer, &mesh->id);
+ CustomDataLayer *vlayers = NULL, vlayers_buff[CD_TEMP_CHUNK_SIZE];
+ CustomDataLayer *elayers = NULL, elayers_buff[CD_TEMP_CHUNK_SIZE];
+ CustomDataLayer *flayers = NULL, flayers_buff[CD_TEMP_CHUNK_SIZE];
+ CustomDataLayer *llayers = NULL, llayers_buff[CD_TEMP_CHUNK_SIZE];
+ CustomDataLayer *players = NULL, players_buff[CD_TEMP_CHUNK_SIZE];
- /* direct data */
- if (mesh->adt) {
- BKE_animdata_blend_write(writer, mesh->adt);
- }
+ /* cache only - don't write */
+ mesh->mface = NULL;
+ mesh->totface = 0;
+ memset(&mesh->fdata, 0, sizeof(mesh->fdata));
+ memset(&mesh->runtime, 0, sizeof(mesh->runtime));
+ flayers = flayers_buff;
+
+ /* Do not store actual geometry data in case this is a library override ID. */
+ if (ID_IS_OVERRIDE_LIBRARY(mesh) && !is_undo) {
+ mesh->mvert = NULL;
+ mesh->totvert = 0;
+ memset(&mesh->vdata, 0, sizeof(mesh->vdata));
+ vlayers = vlayers_buff;
+
+ mesh->medge = NULL;
+ mesh->totedge = 0;
+ memset(&mesh->edata, 0, sizeof(mesh->edata));
+ elayers = elayers_buff;
+
+ mesh->mloop = NULL;
+ mesh->totloop = 0;
+ memset(&mesh->ldata, 0, sizeof(mesh->ldata));
+ llayers = llayers_buff;
+
+ mesh->mpoly = NULL;
+ mesh->totpoly = 0;
+ memset(&mesh->pdata, 0, sizeof(mesh->pdata));
+ players = players_buff;
+ }
+ else {
+ CustomData_blend_write_prepare(&mesh->vdata, &vlayers, vlayers_buff, ARRAY_SIZE(vlayers_buff));
+ CustomData_blend_write_prepare(&mesh->edata, &elayers, elayers_buff, ARRAY_SIZE(elayers_buff));
+ CustomData_blend_write_prepare(&mesh->ldata, &llayers, llayers_buff, ARRAY_SIZE(llayers_buff));
+ CustomData_blend_write_prepare(&mesh->pdata, &players, players_buff, ARRAY_SIZE(players_buff));
+ }
- BKE_defbase_blend_write(writer, &mesh->vertex_group_names);
+ BLO_write_id_struct(writer, Mesh, id_address, &mesh->id);
+ BKE_id_blend_write(writer, &mesh->id);
- BLO_write_pointer_array(writer, mesh->totcol, mesh->mat);
- BLO_write_raw(writer, sizeof(MSelect) * mesh->totselect, mesh->mselect);
+ /* direct data */
+ if (mesh->adt) {
+ BKE_animdata_blend_write(writer, mesh->adt);
+ }
+
+ BKE_defbase_blend_write(writer, &mesh->vertex_group_names);
- CustomData_blend_write(
- writer, &mesh->vdata, vlayers, mesh->totvert, CD_MASK_MESH.vmask, &mesh->id);
- CustomData_blend_write(
- writer, &mesh->edata, elayers, mesh->totedge, CD_MASK_MESH.emask, &mesh->id);
- /* fdata is really a dummy - written so slots align */
- CustomData_blend_write(
- writer, &mesh->fdata, flayers, mesh->totface, CD_MASK_MESH.fmask, &mesh->id);
- CustomData_blend_write(
- writer, &mesh->ldata, llayers, mesh->totloop, CD_MASK_MESH.lmask, &mesh->id);
- CustomData_blend_write(
- writer, &mesh->pdata, players, mesh->totpoly, CD_MASK_MESH.pmask, &mesh->id);
+ BLO_write_pointer_array(writer, mesh->totcol, mesh->mat);
+ BLO_write_raw(writer, sizeof(MSelect) * mesh->totselect, mesh->mselect);
- /* Free temporary data */
+ CustomData_blend_write(
+ writer, &mesh->vdata, vlayers, mesh->totvert, CD_MASK_MESH.vmask, &mesh->id);
+ CustomData_blend_write(
+ writer, &mesh->edata, elayers, mesh->totedge, CD_MASK_MESH.emask, &mesh->id);
+ /* fdata is really a dummy - written so slots align */
+ CustomData_blend_write(
+ writer, &mesh->fdata, flayers, mesh->totface, CD_MASK_MESH.fmask, &mesh->id);
+ CustomData_blend_write(
+ writer, &mesh->ldata, llayers, mesh->totloop, CD_MASK_MESH.lmask, &mesh->id);
+ CustomData_blend_write(
+ writer, &mesh->pdata, players, mesh->totpoly, CD_MASK_MESH.pmask, &mesh->id);
-/* Free custom-data layers, when not assigned a buffer value. */
+ /* Free temporary data */
+
+ /* Free custom-data layers, when not assigned a buffer value. */
#define CD_LAYERS_FREE(id) \
if (id && id != id##_buff) { \
MEM_freeN(id); \
} \
((void)0)
- CD_LAYERS_FREE(vlayers);
- CD_LAYERS_FREE(elayers);
- /* CD_LAYER_FREE(flayers); */ /* Never allocated. */
- CD_LAYERS_FREE(llayers);
- CD_LAYERS_FREE(players);
+ CD_LAYERS_FREE(vlayers);
+ CD_LAYERS_FREE(elayers);
+ // CD_LAYER_FREE(flayers); /* Never allocated. */
+ CD_LAYERS_FREE(llayers);
+ CD_LAYERS_FREE(players);
#undef CD_LAYERS_FREE
- }
}
static void mesh_blend_read_data(BlendDataReader *reader, ID *id)
@@ -389,6 +384,7 @@ enum {
MESHCMP_EDGEUNKNOWN,
MESHCMP_VERTCOMISMATCH,
MESHCMP_CDLAYERS_MISMATCH,
+ MESHCMP_ATTRIBUTE_VALUE_MISMATCH,
};
static const char *cmpcode_to_str(int code)
@@ -416,6 +412,8 @@ static const char *cmpcode_to_str(int code)
return "Vertex Coordinate Mismatch";
case MESHCMP_CDLAYERS_MISMATCH:
return "CustomData Layer Count Mismatch";
+ case MESHCMP_ATTRIBUTE_VALUE_MISMATCH:
+ return "Attribute Value Mismatch";
default:
return "Mesh Comparison Code Unknown";
}
@@ -423,171 +421,233 @@ static const char *cmpcode_to_str(int code)
/** Thresh is threshold for comparing vertices, UV's, vertex colors, weights, etc. */
static int customdata_compare(
- CustomData *c1, CustomData *c2, Mesh *m1, Mesh *m2, const float thresh)
+ CustomData *c1, CustomData *c2, const int total_length, Mesh *m1, Mesh *m2, const float thresh)
{
const float thresh_sq = thresh * thresh;
CustomDataLayer *l1, *l2;
- int i, i1 = 0, i2 = 0, tot, j;
-
- for (i = 0; i < c1->totlayer; i++) {
- if (ELEM(c1->layers[i].type,
- CD_MVERT,
- CD_MEDGE,
- CD_MPOLY,
- CD_MLOOPUV,
- CD_MLOOPCOL,
- CD_MDEFORMVERT)) {
- i1++;
+ int layer_count1 = 0, layer_count2 = 0, j;
+ const uint64_t cd_mask_non_generic = CD_MASK_MVERT | CD_MASK_MEDGE | CD_MASK_MPOLY |
+ CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_MDEFORMVERT;
+ const uint64_t cd_mask_all_attr = CD_MASK_PROP_ALL | cd_mask_non_generic;
+
+ for (int i = 0; i < c1->totlayer; i++) {
+ if (CD_TYPE_AS_MASK(c1->layers[i].type) & cd_mask_all_attr) {
+ layer_count1++;
}
}
- for (i = 0; i < c2->totlayer; i++) {
- if (ELEM(c2->layers[i].type,
- CD_MVERT,
- CD_MEDGE,
- CD_MPOLY,
- CD_MLOOPUV,
- CD_MLOOPCOL,
- CD_MDEFORMVERT)) {
- i2++;
+ for (int i = 0; i < c2->totlayer; i++) {
+ if (CD_TYPE_AS_MASK(c2->layers[i].type) & cd_mask_all_attr) {
+ layer_count2++;
}
}
- if (i1 != i2) {
+ if (layer_count1 != layer_count2) {
return MESHCMP_CDLAYERS_MISMATCH;
}
l1 = c1->layers;
l2 = c2->layers;
- tot = i1;
- i1 = 0;
- i2 = 0;
- for (i = 0; i < tot; i++) {
- while (
- i1 < c1->totlayer &&
- !ELEM(l1->type, CD_MVERT, CD_MEDGE, CD_MPOLY, CD_MLOOPUV, CD_MLOOPCOL, CD_MDEFORMVERT)) {
- i1++;
- l1++;
- }
-
- while (
- i2 < c2->totlayer &&
- !ELEM(l2->type, CD_MVERT, CD_MEDGE, CD_MPOLY, CD_MLOOPUV, CD_MLOOPCOL, CD_MDEFORMVERT)) {
- i2++;
- l2++;
- }
- if (l1->type == CD_MVERT) {
- MVert *v1 = l1->data;
- MVert *v2 = l2->data;
- int vtot = m1->totvert;
-
- for (j = 0; j < vtot; j++, v1++, v2++) {
- if (len_squared_v3v3(v1->co, v2->co) > thresh_sq) {
- return MESHCMP_VERTCOMISMATCH;
- }
- /* I don't care about normals, let's just do coordinates. */
+ for (int i1 = 0; i1 < c1->totlayer; i1++) {
+ l1 = c1->layers + i1;
+ for (int i2 = 0; i2 < c2->totlayer; i2++) {
+ l2 = c2->layers + i2;
+ if (l1->type != l2->type || !STREQ(l1->name, l2->name)) {
+ continue;
}
- }
+ /* At this point `l1` and `l2` have the same name and type, so they should be compared. */
- /* We're order-agnostic for edges here. */
- if (l1->type == CD_MEDGE) {
- MEdge *e1 = l1->data;
- MEdge *e2 = l2->data;
- int etot = m1->totedge;
- EdgeHash *eh = BLI_edgehash_new_ex(__func__, etot);
+ switch (l1->type) {
- for (j = 0; j < etot; j++, e1++) {
- BLI_edgehash_insert(eh, e1->v1, e1->v2, e1);
- }
+ case CD_MVERT: {
+ MVert *v1 = l1->data;
+ MVert *v2 = l2->data;
+ int vtot = m1->totvert;
- for (j = 0; j < etot; j++, e2++) {
- if (!BLI_edgehash_lookup(eh, e2->v1, e2->v2)) {
- return MESHCMP_EDGEUNKNOWN;
+ for (j = 0; j < vtot; j++, v1++, v2++) {
+ if (len_squared_v3v3(v1->co, v2->co) > thresh_sq) {
+ return MESHCMP_VERTCOMISMATCH;
+ }
+ /* I don't care about normals, let's just do coordinates. */
+ }
+ break;
}
- }
- BLI_edgehash_free(eh, NULL);
- }
- if (l1->type == CD_MPOLY) {
- MPoly *p1 = l1->data;
- MPoly *p2 = l2->data;
- int ptot = m1->totpoly;
+ /* We're order-agnostic for edges here. */
+ case CD_MEDGE: {
+ MEdge *e1 = l1->data;
+ MEdge *e2 = l2->data;
+ int etot = m1->totedge;
+ EdgeHash *eh = BLI_edgehash_new_ex(__func__, etot);
- for (j = 0; j < ptot; j++, p1++, p2++) {
- MLoop *lp1, *lp2;
- int k;
+ for (j = 0; j < etot; j++, e1++) {
+ BLI_edgehash_insert(eh, e1->v1, e1->v2, e1);
+ }
- if (p1->totloop != p2->totloop) {
- return MESHCMP_POLYMISMATCH;
+ for (j = 0; j < etot; j++, e2++) {
+ if (!BLI_edgehash_lookup(eh, e2->v1, e2->v2)) {
+ return MESHCMP_EDGEUNKNOWN;
+ }
+ }
+ BLI_edgehash_free(eh, NULL);
+ break;
}
-
- lp1 = m1->mloop + p1->loopstart;
- lp2 = m2->mloop + p2->loopstart;
-
- for (k = 0; k < p1->totloop; k++, lp1++, lp2++) {
- if (lp1->v != lp2->v) {
- return MESHCMP_POLYVERTMISMATCH;
+ case CD_MPOLY: {
+ MPoly *p1 = l1->data;
+ MPoly *p2 = l2->data;
+ int ptot = m1->totpoly;
+
+ for (j = 0; j < ptot; j++, p1++, p2++) {
+ MLoop *lp1, *lp2;
+ int k;
+
+ if (p1->totloop != p2->totloop) {
+ return MESHCMP_POLYMISMATCH;
+ }
+
+ lp1 = m1->mloop + p1->loopstart;
+ lp2 = m2->mloop + p2->loopstart;
+
+ for (k = 0; k < p1->totloop; k++, lp1++, lp2++) {
+ if (lp1->v != lp2->v) {
+ return MESHCMP_POLYVERTMISMATCH;
+ }
+ }
}
+ break;
}
- }
- }
- if (l1->type == CD_MLOOP) {
- MLoop *lp1 = l1->data;
- MLoop *lp2 = l2->data;
- int ltot = m1->totloop;
-
- for (j = 0; j < ltot; j++, lp1++, lp2++) {
- if (lp1->v != lp2->v) {
- return MESHCMP_LOOPMISMATCH;
+ case CD_MLOOP: {
+ MLoop *lp1 = l1->data;
+ MLoop *lp2 = l2->data;
+ int ltot = m1->totloop;
+
+ for (j = 0; j < ltot; j++, lp1++, lp2++) {
+ if (lp1->v != lp2->v) {
+ return MESHCMP_LOOPMISMATCH;
+ }
+ }
+ break;
}
- }
- }
- if (l1->type == CD_MLOOPUV) {
- MLoopUV *lp1 = l1->data;
- MLoopUV *lp2 = l2->data;
- int ltot = m1->totloop;
-
- for (j = 0; j < ltot; j++, lp1++, lp2++) {
- if (len_squared_v2v2(lp1->uv, lp2->uv) > thresh_sq) {
- return MESHCMP_LOOPUVMISMATCH;
+ case CD_MLOOPUV: {
+ MLoopUV *lp1 = l1->data;
+ MLoopUV *lp2 = l2->data;
+ int ltot = m1->totloop;
+
+ for (j = 0; j < ltot; j++, lp1++, lp2++) {
+ if (len_squared_v2v2(lp1->uv, lp2->uv) > thresh_sq) {
+ return MESHCMP_LOOPUVMISMATCH;
+ }
+ }
+ break;
}
- }
- }
-
- if (l1->type == CD_MLOOPCOL) {
- MLoopCol *lp1 = l1->data;
- MLoopCol *lp2 = l2->data;
- int ltot = m1->totloop;
-
- for (j = 0; j < ltot; j++, lp1++, lp2++) {
- if (abs(lp1->r - lp2->r) > thresh || abs(lp1->g - lp2->g) > thresh ||
- abs(lp1->b - lp2->b) > thresh || abs(lp1->a - lp2->a) > thresh) {
- return MESHCMP_LOOPCOLMISMATCH;
+ case CD_MLOOPCOL: {
+ MLoopCol *lp1 = l1->data;
+ MLoopCol *lp2 = l2->data;
+ int ltot = m1->totloop;
+
+ for (j = 0; j < ltot; j++, lp1++, lp2++) {
+ if (abs(lp1->r - lp2->r) > thresh || abs(lp1->g - lp2->g) > thresh ||
+ abs(lp1->b - lp2->b) > thresh || abs(lp1->a - lp2->a) > thresh) {
+ return MESHCMP_LOOPCOLMISMATCH;
+ }
+ }
+ break;
}
- }
- }
-
- if (l1->type == CD_MDEFORMVERT) {
- MDeformVert *dv1 = l1->data;
- MDeformVert *dv2 = l2->data;
- int dvtot = m1->totvert;
-
- for (j = 0; j < dvtot; j++, dv1++, dv2++) {
- int k;
- MDeformWeight *dw1 = dv1->dw, *dw2 = dv2->dw;
-
- if (dv1->totweight != dv2->totweight) {
- return MESHCMP_DVERT_TOTGROUPMISMATCH;
+ case CD_MDEFORMVERT: {
+ MDeformVert *dv1 = l1->data;
+ MDeformVert *dv2 = l2->data;
+ int dvtot = m1->totvert;
+
+ for (j = 0; j < dvtot; j++, dv1++, dv2++) {
+ int k;
+ MDeformWeight *dw1 = dv1->dw, *dw2 = dv2->dw;
+
+ if (dv1->totweight != dv2->totweight) {
+ return MESHCMP_DVERT_TOTGROUPMISMATCH;
+ }
+
+ for (k = 0; k < dv1->totweight; k++, dw1++, dw2++) {
+ if (dw1->def_nr != dw2->def_nr) {
+ return MESHCMP_DVERT_GROUPMISMATCH;
+ }
+ if (fabsf(dw1->weight - dw2->weight) > thresh) {
+ return MESHCMP_DVERT_WEIGHTMISMATCH;
+ }
+ }
+ }
+ break;
}
-
- for (k = 0; k < dv1->totweight; k++, dw1++, dw2++) {
- if (dw1->def_nr != dw2->def_nr) {
- return MESHCMP_DVERT_GROUPMISMATCH;
+ case CD_PROP_FLOAT: {
+ const float *l1_data = l1->data;
+ const float *l2_data = l2->data;
+
+ for (int i = 0; i < total_length; i++) {
+ if (fabsf(l1_data[i] - l2_data[i]) > thresh) {
+ return MESHCMP_ATTRIBUTE_VALUE_MISMATCH;
+ }
+ }
+ break;
+ }
+ case CD_PROP_FLOAT2: {
+ const float(*l1_data)[2] = l1->data;
+ const float(*l2_data)[2] = l2->data;
+
+ for (int i = 0; i < total_length; i++) {
+ if (len_squared_v2v2(l1_data[i], l2_data[i]) > thresh_sq) {
+ return MESHCMP_ATTRIBUTE_VALUE_MISMATCH;
+ }
+ }
+ break;
+ }
+ case CD_PROP_FLOAT3: {
+ const float(*l1_data)[3] = l1->data;
+ const float(*l2_data)[3] = l2->data;
+
+ for (int i = 0; i < total_length; i++) {
+ if (len_squared_v3v3(l1_data[i], l2_data[i]) > thresh_sq) {
+ return MESHCMP_ATTRIBUTE_VALUE_MISMATCH;
+ }
+ }
+ break;
+ }
+ case CD_PROP_INT32: {
+ const int *l1_data = l1->data;
+ const int *l2_data = l2->data;
+
+ for (int i = 0; i < total_length; i++) {
+ if (l1_data[i] != l2_data[i]) {
+ return MESHCMP_ATTRIBUTE_VALUE_MISMATCH;
+ }
}
- if (fabsf(dw1->weight - dw2->weight) > thresh) {
- return MESHCMP_DVERT_WEIGHTMISMATCH;
+ break;
+ }
+ case CD_PROP_BOOL: {
+ const bool *l1_data = l1->data;
+ const bool *l2_data = l2->data;
+
+ for (int i = 0; i < total_length; i++) {
+ if (l1_data[i] != l2_data[i]) {
+ return MESHCMP_ATTRIBUTE_VALUE_MISMATCH;
+ }
+ }
+ break;
+ }
+ case CD_PROP_COLOR: {
+ const MPropCol *l1_data = l1->data;
+ const MPropCol *l2_data = l2->data;
+
+ for (int i = 0; i < total_length; i++) {
+ for (j = 0; j < 4; j++) {
+ if (fabsf(l1_data[i].color[j] - l2_data[i].color[j]) > thresh) {
+ return MESHCMP_ATTRIBUTE_VALUE_MISMATCH;
+ }
+ }
}
+ break;
+ }
+ default: {
+ break;
}
}
}
@@ -626,19 +686,19 @@ const char *BKE_mesh_cmp(Mesh *me1, Mesh *me2, float thresh)
return "Number of loops don't match";
}
- if ((c = customdata_compare(&me1->vdata, &me2->vdata, me1, me2, thresh))) {
+ if ((c = customdata_compare(&me1->vdata, &me2->vdata, me1->totvert, me1, me2, thresh))) {
return cmpcode_to_str(c);
}
- if ((c = customdata_compare(&me1->edata, &me2->edata, me1, me2, thresh))) {
+ if ((c = customdata_compare(&me1->edata, &me2->edata, me1->totedge, me1, me2, thresh))) {
return cmpcode_to_str(c);
}
- if ((c = customdata_compare(&me1->ldata, &me2->ldata, me1, me2, thresh))) {
+ if ((c = customdata_compare(&me1->ldata, &me2->ldata, me1->totloop, me1, me2, thresh))) {
return cmpcode_to_str(c);
}
- if ((c = customdata_compare(&me1->pdata, &me2->pdata, me1, me2, thresh))) {
+ if ((c = customdata_compare(&me1->pdata, &me2->pdata, me1->totpoly, me1, me2, thresh))) {
return cmpcode_to_str(c);
}
@@ -801,8 +861,11 @@ bool BKE_mesh_has_custom_loop_normals(Mesh *me)
return CustomData_has_layer(&me->ldata, CD_CUSTOMLOOPNORMAL);
}
-/** Free (or release) any data used by this mesh (does not free the mesh itself). */
-void BKE_mesh_free(Mesh *me)
+/**
+ * Free (or release) any data used by this mesh (does not free the mesh itself).
+ * Only use for undo, in most cases `BKE_id_free(NULL, me)` should be used.
+ */
+void BKE_mesh_free_data_for_undo(Mesh *me)
{
mesh_free_data(&me->id);
}
@@ -890,7 +953,7 @@ Mesh *BKE_mesh_new_nomain(
NULL, ID_ME, BKE_idtype_idcode_to_name(ID_ME), LIB_ID_CREATE_LOCALIZE);
BKE_libblock_init_empty(&mesh->id);
- /* Don't use CustomData_reset(...); because we don't want to touch custom-data. */
+ /* Don't use #CustomData_reset because we don't want to touch custom-data. */
copy_vn_i(mesh->vdata.typemap, CD_NUMTYPES, -1);
copy_vn_i(mesh->edata.typemap, CD_NUMTYPES, -1);
copy_vn_i(mesh->fdata.typemap, CD_NUMTYPES, -1);
@@ -1018,7 +1081,7 @@ void BKE_mesh_eval_delete(struct Mesh *mesh_eval)
{
/* Evaluated mesh may point to edit mesh, but never owns it. */
mesh_eval->edit_mesh = NULL;
- BKE_mesh_free(mesh_eval);
+ mesh_free_data(&mesh_eval->id);
BKE_libblock_free_data(&mesh_eval->id, false);
MEM_freeN(mesh_eval);
}
@@ -1621,10 +1684,7 @@ void BKE_mesh_do_versions_cd_flag_init(Mesh *mesh)
void BKE_mesh_mselect_clear(Mesh *me)
{
- if (me->mselect) {
- MEM_freeN(me->mselect);
- me->mselect = NULL;
- }
+ MEM_SAFE_FREE(me->mselect);
me->totselect = 0;
}
@@ -1841,15 +1901,14 @@ void BKE_mesh_calc_normals_split_ex(Mesh *mesh, MLoopNorSpaceArray *r_lnors_spac
}
else {
polynors = MEM_malloc_arrayN(mesh->totpoly, sizeof(float[3]), __func__);
- BKE_mesh_calc_normals_poly(mesh->mvert,
- NULL,
- mesh->totvert,
- mesh->mloop,
- mesh->mpoly,
- mesh->totloop,
- mesh->totpoly,
- polynors,
- false);
+ BKE_mesh_calc_normals_poly_and_vertex(mesh->mvert,
+ mesh->totvert,
+ mesh->mloop,
+ mesh->totloop,
+ mesh->mpoly,
+ mesh->totpoly,
+ polynors,
+ NULL);
free_polynors = true;
}
diff --git a/source/blender/blenkernel/intern/mesh_convert.c b/source/blender/blenkernel/intern/mesh_convert.c
index 0e4fe91e577..4b1eb5b39ce 100644
--- a/source/blender/blenkernel/intern/mesh_convert.c
+++ b/source/blender/blenkernel/intern/mesh_convert.c
@@ -38,6 +38,7 @@
#include "BLI_utildefines.h"
#include "BKE_DerivedMesh.h"
+#include "BKE_deform.h"
#include "BKE_displist.h"
#include "BKE_editmesh.h"
#include "BKE_key.h"
@@ -271,8 +272,8 @@ static int mesh_nurbs_displist_to_mdata(const Curve *cu,
}
if (totvert == 0) {
- /* error("can't convert"); */
- /* Make Sure you check ob->data is a curve */
+ /* Make Sure you check ob->data is a curve. */
+ // error("can't convert");
return -1;
}
@@ -1665,6 +1666,10 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
/* skip the listbase */
MEMCPY_STRUCT_AFTER(mesh_dst, &tmp, id.prev);
+ BLI_freelistN(&mesh_dst->vertex_group_names);
+ BKE_defgroup_copy_list(&mesh_dst->vertex_group_names, &mesh_src->vertex_group_names);
+ mesh_dst->vertex_group_active_index = mesh_src->vertex_group_active_index;
+
if (take_ownership) {
if (alloctype == CD_ASSIGN) {
CustomData_free_typemask(&mesh_src->vdata, mesh_src->totvert, ~mask->vmask);
diff --git a/source/blender/blenkernel/intern/mesh_mirror.c b/source/blender/blenkernel/intern/mesh_mirror.c
index 9aeaa1ada52..b20d81e7b9c 100644
--- a/source/blender/blenkernel/intern/mesh_mirror.c
+++ b/source/blender/blenkernel/intern/mesh_mirror.c
@@ -393,15 +393,14 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd,
/* calculate custom normals into loop_normals, then mirror first half into second half */
- BKE_mesh_calc_normals_poly(result->mvert,
- NULL,
- result->totvert,
- result->mloop,
- result->mpoly,
- totloop,
- totpoly,
- poly_normals,
- false);
+ BKE_mesh_calc_normals_poly_and_vertex(result->mvert,
+ result->totvert,
+ result->mloop,
+ totloop,
+ result->mpoly,
+ totpoly,
+ poly_normals,
+ NULL);
BKE_mesh_normals_loop_split(result->mvert,
result->totvert,
diff --git a/source/blender/blenkernel/intern/mesh_normals.cc b/source/blender/blenkernel/intern/mesh_normals.cc
index f496d6eada1..9a761c6fa11 100644
--- a/source/blender/blenkernel/intern/mesh_normals.cc
+++ b/source/blender/blenkernel/intern/mesh_normals.cc
@@ -27,8 +27,6 @@
#include <climits>
-#include "CLG_log.h"
-
#include "MEM_guardedalloc.h"
#include "DNA_mesh_types.h"
@@ -50,6 +48,8 @@
#include "BKE_global.h"
#include "BKE_mesh.h"
+#include "atomic_ops.h"
+
// #define DEBUG_TIME
#ifdef DEBUG_TIME
@@ -57,319 +57,257 @@
# include "PIL_time_utildefines.h"
#endif
-static CLG_LogRef LOG = {"bke.mesh_normals"};
-
/* -------------------------------------------------------------------- */
-/** \name Mesh Normal Calculation
+/** \name Private Utility Functions
* \{ */
/**
- * Call when there are no polygons.
+ * A thread-safe version of #add_v3_v3 that uses a spin-lock.
+ *
+ * \note Avoid using this when the chance of contention is high.
*/
-static void mesh_calc_normals_vert_fallback(MVert *mverts, int numVerts)
+static void add_v3_v3_atomic(float r[3], const float a[3])
{
- for (int i = 0; i < numVerts; i++) {
- MVert *mv = &mverts[i];
- float no[3];
+#define FLT_EQ_NONAN(_fa, _fb) (*((const uint32_t *)&_fa) == *((const uint32_t *)&_fb))
- normalize_v3_v3(no, mv->co);
- normal_float_to_short_v3(mv->no, no);
+ float virtual_lock = r[0];
+ while (true) {
+ /* This loops until following conditions are met:
+ * - `r[0]` has same value as virtual_lock (i.e. it did not change since last try).
+ * - `r[0]` was not `FLT_MAX`, i.e. it was not locked by another thread. */
+ const float test_lock = atomic_cas_float(&r[0], virtual_lock, FLT_MAX);
+ if (_ATOMIC_LIKELY(FLT_EQ_NONAN(test_lock, virtual_lock) && (test_lock != FLT_MAX))) {
+ break;
+ }
+ virtual_lock = test_lock;
}
-}
+ virtual_lock += a[0];
+ r[1] += a[1];
+ r[2] += a[2];
-/* TODO(Sybren): we can probably rename this to BKE_mesh_calc_normals_mapping(),
- * and remove the function of the same name below, as that one doesn't seem to be
- * called anywhere. */
-void BKE_mesh_calc_normals_mapping_simple(struct Mesh *mesh)
-{
- const bool only_face_normals = CustomData_is_referenced_layer(&mesh->vdata, CD_MVERT);
-
- BKE_mesh_calc_normals_mapping_ex(mesh->mvert,
- mesh->totvert,
- mesh->mloop,
- mesh->mpoly,
- mesh->totloop,
- mesh->totpoly,
- nullptr,
- mesh->mface,
- mesh->totface,
- nullptr,
- nullptr,
- only_face_normals);
-}
+ /* Second atomic operation to 'release'
+ * our lock on that vector and set its first scalar value. */
+ /* Note that we do not need to loop here, since we 'locked' `r[0]`,
+ * nobody should have changed it in the mean time. */
+ virtual_lock = atomic_cas_float(&r[0], FLT_MAX, virtual_lock);
+ BLI_assert(virtual_lock == FLT_MAX);
-/* Calculate vertex and face normals, face normals are returned in *r_faceNors if non-nullptr
- * and vertex normals are stored in actual mverts.
- */
-void BKE_mesh_calc_normals_mapping(MVert *mverts,
- int numVerts,
- const MLoop *mloop,
- const MPoly *mpolys,
- int numLoops,
- int numPolys,
- float (*r_polyNors)[3],
- const MFace *mfaces,
- int numFaces,
- const int *origIndexFace,
- float (*r_faceNors)[3])
-{
- BKE_mesh_calc_normals_mapping_ex(mverts,
- numVerts,
- mloop,
- mpolys,
- numLoops,
- numPolys,
- r_polyNors,
- mfaces,
- numFaces,
- origIndexFace,
- r_faceNors,
- false);
+#undef FLT_EQ_NONAN
}
-/* extended version of 'BKE_mesh_calc_normals_poly' with option not to calc vertex normals */
-void BKE_mesh_calc_normals_mapping_ex(MVert *mverts,
- int numVerts,
- const MLoop *mloop,
- const MPoly *mpolys,
- int numLoops,
- int numPolys,
- float (*r_polyNors)[3],
- const MFace *mfaces,
- int numFaces,
- const int *origIndexFace,
- float (*r_faceNors)[3],
- const bool only_face_normals)
-{
- float(*pnors)[3] = r_polyNors, (*fnors)[3] = r_faceNors;
-
- if (numPolys == 0) {
- if (only_face_normals == false) {
- mesh_calc_normals_vert_fallback(mverts, numVerts);
- }
- return;
- }
- /* if we are not calculating verts and no verts were passes then we have nothing to do */
- if ((only_face_normals == true) && (r_polyNors == nullptr) && (r_faceNors == nullptr)) {
- CLOG_WARN(&LOG, "called with nothing to do");
- return;
- }
-
- if (!pnors) {
- pnors = (float(*)[3])MEM_calloc_arrayN((size_t)numPolys, sizeof(float[3]), __func__);
- }
- /* NO NEED TO ALLOC YET */
- /* if (!fnors) fnors = MEM_calloc_arrayN(numFaces, sizeof(float[3]), "face nors mesh.c"); */
+/** \} */
- if (only_face_normals == false) {
- /* vertex normals are optional, they require some extra calculations,
- * so make them optional */
- BKE_mesh_calc_normals_poly(
- mverts, nullptr, numVerts, mloop, mpolys, numLoops, numPolys, pnors, false);
- }
- else {
- /* only calc poly normals */
- const MPoly *mp = mpolys;
- for (int i = 0; i < numPolys; i++, mp++) {
- BKE_mesh_calc_poly_normal(mp, mloop + mp->loopstart, mverts, pnors[i]);
- }
- }
+/* -------------------------------------------------------------------- */
+/** \name Public Utility Functions
+ *
+ * Related to managing normals but not directly related to calculating normals.
+ * \{ */
- if (origIndexFace &&
- /* fnors == r_faceNors */ /* NO NEED TO ALLOC YET */
- fnors != nullptr &&
- numFaces) {
- const MFace *mf = mfaces;
- for (int i = 0; i < numFaces; i++, mf++, origIndexFace++) {
- if (*origIndexFace < numPolys) {
- copy_v3_v3(fnors[i], pnors[*origIndexFace]);
- }
- else {
- /* eek, we're not corresponding to polys */
- CLOG_ERROR(&LOG, "tessellation face indices are incorrect. normals may look bad.");
- }
- }
- }
+void BKE_mesh_normals_tag_dirty(Mesh *mesh)
+{
+ mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
+ mesh->runtime.cd_dirty_poly |= CD_MASK_NORMAL;
+}
- if (pnors != r_polyNors) {
- MEM_freeN(pnors);
- }
- /* if (fnors != r_faceNors) MEM_freeN(fnors); */ /* NO NEED TO ALLOC YET */
+/** \} */
- fnors = pnors = nullptr;
-}
+/* -------------------------------------------------------------------- */
+/** \name Mesh Normal Calculation (Polygons)
+ * \{ */
-struct MeshCalcNormalsData {
- const MPoly *mpolys;
+struct MeshCalcNormalsData_Poly {
+ const MVert *mvert;
const MLoop *mloop;
- MVert *mverts;
+ const MPoly *mpoly;
+
+ /** Polygon normal output. */
float (*pnors)[3];
- float (*lnors_weighted)[3];
- float (*vnors)[3];
};
-static void mesh_calc_normals_poly_cb(void *__restrict userdata,
+static void mesh_calc_normals_poly_fn(void *__restrict userdata,
const int pidx,
const TaskParallelTLS *__restrict UNUSED(tls))
{
- MeshCalcNormalsData *data = (MeshCalcNormalsData *)userdata;
- const MPoly *mp = &data->mpolys[pidx];
+ const MeshCalcNormalsData_Poly *data = (MeshCalcNormalsData_Poly *)userdata;
+ const MPoly *mp = &data->mpoly[pidx];
+ BKE_mesh_calc_poly_normal(mp, data->mloop + mp->loopstart, data->mvert, data->pnors[pidx]);
+}
+
+void BKE_mesh_calc_normals_poly(const MVert *mvert,
+ int UNUSED(mvert_len),
+ const MLoop *mloop,
+ int UNUSED(mloop_len),
+ const MPoly *mpoly,
+ int mpoly_len,
+ float (*r_poly_normals)[3])
+{
+ TaskParallelSettings settings;
+ BLI_parallel_range_settings_defaults(&settings);
+ settings.min_iter_per_thread = 1024;
+
+ BLI_assert((r_poly_normals != nullptr) || (mpoly_len == 0));
+
+ MeshCalcNormalsData_Poly data = {};
+ data.mpoly = mpoly;
+ data.mloop = mloop;
+ data.mvert = mvert;
+ data.pnors = r_poly_normals;
- BKE_mesh_calc_poly_normal(mp, data->mloop + mp->loopstart, data->mverts, data->pnors[pidx]);
+ BLI_task_parallel_range(0, mpoly_len, &data, mesh_calc_normals_poly_fn, &settings);
}
-static void mesh_calc_normals_poly_prepare_cb(void *__restrict userdata,
- const int pidx,
- const TaskParallelTLS *__restrict UNUSED(tls))
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Mesh Normal Calculation (Polygons & Vertices)
+ *
+ * Implement #BKE_mesh_calc_normals_poly_and_vertex,
+ *
+ * Take care making optimizations to this function as improvements to low-poly
+ * meshes can slow down high-poly meshes. For details on performance, see D11993.
+ * \{ */
+
+struct MeshCalcNormalsData_PolyAndVertex {
+ /** Write into vertex normals #MVert.no. */
+ MVert *mvert;
+ const MLoop *mloop;
+ const MPoly *mpoly;
+
+ /** Polygon normal output. */
+ float (*pnors)[3];
+ /** Vertex normal output (may be freed, copied into #MVert.no). */
+ float (*vnors)[3];
+};
+
+static void mesh_calc_normals_poly_and_vertex_accum_fn(
+ void *__restrict userdata, const int pidx, const TaskParallelTLS *__restrict UNUSED(tls))
{
- MeshCalcNormalsData *data = (MeshCalcNormalsData *)userdata;
- const MPoly *mp = &data->mpolys[pidx];
+ const MeshCalcNormalsData_PolyAndVertex *data = (MeshCalcNormalsData_PolyAndVertex *)userdata;
+ const MPoly *mp = &data->mpoly[pidx];
const MLoop *ml = &data->mloop[mp->loopstart];
- const MVert *mverts = data->mverts;
+ const MVert *mverts = data->mvert;
+ float(*vnors)[3] = data->vnors;
float pnor_temp[3];
float *pnor = data->pnors ? data->pnors[pidx] : pnor_temp;
- float(*lnors_weighted)[3] = data->lnors_weighted;
- const int nverts = mp->totloop;
- float(*edgevecbuf)[3] = (float(*)[3])BLI_array_alloca(edgevecbuf, (size_t)nverts);
+ const int i_end = mp->totloop - 1;
- /* Polygon Normal and edge-vector */
- /* inline version of #BKE_mesh_calc_poly_normal, also does edge-vectors */
+ /* Polygon Normal and edge-vector. */
+ /* Inline version of #BKE_mesh_calc_poly_normal, also does edge-vectors. */
{
- int i_prev = nverts - 1;
- const float *v_prev = mverts[ml[i_prev].v].co;
- const float *v_curr;
-
zero_v3(pnor);
/* Newell's Method */
- for (int i = 0; i < nverts; i++) {
- v_curr = mverts[ml[i].v].co;
- add_newell_cross_v3_v3v3(pnor, v_prev, v_curr);
-
- /* Unrelated to normalize, calculate edge-vector */
- sub_v3_v3v3(edgevecbuf[i_prev], v_prev, v_curr);
- normalize_v3(edgevecbuf[i_prev]);
- i_prev = i;
-
- v_prev = v_curr;
+ const float *v_curr = mverts[ml[i_end].v].co;
+ for (int i_next = 0; i_next <= i_end; i_next++) {
+ const float *v_next = mverts[ml[i_next].v].co;
+ add_newell_cross_v3_v3v3(pnor, v_curr, v_next);
+ v_curr = v_next;
}
if (UNLIKELY(normalize_v3(pnor) == 0.0f)) {
- pnor[2] = 1.0f; /* other axes set to 0.0 */
+ pnor[2] = 1.0f; /* Other axes set to zero. */
}
}
- /* accumulate angle weighted face normal */
- /* inline version of #accumulate_vertex_normals_poly_v3,
- * split between this threaded callback and #mesh_calc_normals_poly_accum_cb. */
+ /* Accumulate angle weighted face normal into the vertex normal. */
+ /* Inline version of #accumulate_vertex_normals_poly_v3. */
{
- const float *prev_edge = edgevecbuf[nverts - 1];
-
- for (int i = 0; i < nverts; i++) {
- const int lidx = mp->loopstart + i;
- const float *cur_edge = edgevecbuf[i];
-
- /* calculate angle between the two poly edges incident on
- * this vertex */
- const float fac = saacos(-dot_v3v3(cur_edge, prev_edge));
+ float edvec_prev[3], edvec_next[3], edvec_end[3];
+ const float *v_curr = mverts[ml[i_end].v].co;
+ sub_v3_v3v3(edvec_prev, mverts[ml[i_end - 1].v].co, v_curr);
+ normalize_v3(edvec_prev);
+ copy_v3_v3(edvec_end, edvec_prev);
+
+ for (int i_next = 0, i_curr = i_end; i_next <= i_end; i_curr = i_next++) {
+ const float *v_next = mverts[ml[i_next].v].co;
+
+ /* Skip an extra normalization by reusing the first calculated edge. */
+ if (i_next != i_end) {
+ sub_v3_v3v3(edvec_next, v_curr, v_next);
+ normalize_v3(edvec_next);
+ }
+ else {
+ copy_v3_v3(edvec_next, edvec_end);
+ }
- /* Store for later accumulation */
- mul_v3_v3fl(lnors_weighted[lidx], pnor, fac);
+ /* Calculate angle between the two poly edges incident on this vertex. */
+ const float fac = saacos(-dot_v3v3(edvec_prev, edvec_next));
+ const float vnor_add[3] = {pnor[0] * fac, pnor[1] * fac, pnor[2] * fac};
- prev_edge = cur_edge;
+ add_v3_v3_atomic(vnors[ml[i_curr].v], vnor_add);
+ v_curr = v_next;
+ copy_v3_v3(edvec_prev, edvec_next);
}
}
}
-static void mesh_calc_normals_poly_finalize_cb(void *__restrict userdata,
- const int vidx,
- const TaskParallelTLS *__restrict UNUSED(tls))
+static void mesh_calc_normals_poly_and_vertex_finalize_fn(
+ void *__restrict userdata, const int vidx, const TaskParallelTLS *__restrict UNUSED(tls))
{
- MeshCalcNormalsData *data = (MeshCalcNormalsData *)userdata;
+ MeshCalcNormalsData_PolyAndVertex *data = (MeshCalcNormalsData_PolyAndVertex *)userdata;
- MVert *mv = &data->mverts[vidx];
+ MVert *mv = &data->mvert[vidx];
float *no = data->vnors[vidx];
if (UNLIKELY(normalize_v3(no) == 0.0f)) {
- /* following Mesh convention; we use vertex coordinate itself for normal in this case */
+ /* Following Mesh convention; we use vertex coordinate itself for normal in this case. */
normalize_v3_v3(no, mv->co);
}
normal_float_to_short_v3(mv->no, no);
}
-void BKE_mesh_calc_normals_poly(MVert *mverts,
- float (*r_vertnors)[3],
- int numVerts,
- const MLoop *mloop,
- const MPoly *mpolys,
- int numLoops,
- int numPolys,
- float (*r_polynors)[3],
- const bool only_face_normals)
+void BKE_mesh_calc_normals_poly_and_vertex(MVert *mvert,
+ const int mvert_len,
+ const MLoop *mloop,
+ const int UNUSED(mloop_len),
+ const MPoly *mpoly,
+ const int mpoly_len,
+ float (*r_poly_normals)[3],
+ float (*r_vert_normals)[3])
{
- float(*pnors)[3] = r_polynors;
-
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
settings.min_iter_per_thread = 1024;
- if (only_face_normals) {
- BLI_assert((pnors != nullptr) || (numPolys == 0));
- BLI_assert(r_vertnors == nullptr);
-
- MeshCalcNormalsData data;
- data.mpolys = mpolys;
- data.mloop = mloop;
- data.mverts = mverts;
- data.pnors = pnors;
-
- BLI_task_parallel_range(0, numPolys, &data, mesh_calc_normals_poly_cb, &settings);
- return;
- }
-
- float(*vnors)[3] = r_vertnors;
- float(*lnors_weighted)[3] = (float(*)[3])MEM_malloc_arrayN(
- (size_t)numLoops, sizeof(*lnors_weighted), __func__);
+ float(*vnors)[3] = r_vert_normals;
bool free_vnors = false;
- /* first go through and calculate normals for all the polys */
+ /* First go through and calculate normals for all the polys. */
if (vnors == nullptr) {
- vnors = (float(*)[3])MEM_calloc_arrayN((size_t)numVerts, sizeof(*vnors), __func__);
+ vnors = (float(*)[3])MEM_calloc_arrayN((size_t)mvert_len, sizeof(*vnors), __func__);
free_vnors = true;
}
else {
- memset(vnors, 0, sizeof(*vnors) * (size_t)numVerts);
+ memset(vnors, 0, sizeof(*vnors) * (size_t)mvert_len);
}
- MeshCalcNormalsData data;
- data.mpolys = mpolys;
+ MeshCalcNormalsData_PolyAndVertex data = {};
+ data.mpoly = mpoly;
data.mloop = mloop;
- data.mverts = mverts;
- data.pnors = pnors;
- data.lnors_weighted = lnors_weighted;
+ data.mvert = mvert;
+ data.pnors = r_poly_normals;
data.vnors = vnors;
- /* Compute poly normals, and prepare weighted loop normals. */
- BLI_task_parallel_range(0, numPolys, &data, mesh_calc_normals_poly_prepare_cb, &settings);
-
- /* Actually accumulate weighted loop normals into vertex ones. */
- /* Unfortunately, not possible to thread that
- * (not in a reasonable, totally lock- and barrier-free fashion),
- * since several loops will point to the same vertex... */
- for (int lidx = 0; lidx < numLoops; lidx++) {
- add_v3_v3(vnors[mloop[lidx].v], data.lnors_weighted[lidx]);
- }
+ /* Compute poly normals (`pnors`), accumulating them into vertex normals (`vnors`). */
+ BLI_task_parallel_range(
+ 0, mpoly_len, &data, mesh_calc_normals_poly_and_vertex_accum_fn, &settings);
- /* Normalize and validate computed vertex normals. */
- BLI_task_parallel_range(0, numVerts, &data, mesh_calc_normals_poly_finalize_cb, &settings);
+ /* Normalize and validate computed vertex normals (`vnors`). */
+ BLI_task_parallel_range(
+ 0, mvert_len, &data, mesh_calc_normals_poly_and_vertex_finalize_fn, &settings);
if (free_vnors) {
MEM_freeN(vnors);
}
- MEM_freeN(lnors_weighted);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Mesh Normal Calculation
+ * \{ */
+
void BKE_mesh_ensure_normals(Mesh *mesh)
{
if (mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL) {
@@ -410,16 +348,26 @@ void BKE_mesh_ensure_normals_for_display(Mesh *mesh)
(size_t)mesh->totpoly, sizeof(*poly_nors), __func__);
}
- /* calculate poly/vert normals */
- BKE_mesh_calc_normals_poly(mesh->mvert,
- nullptr,
- mesh->totvert,
- mesh->mloop,
- mesh->mpoly,
- mesh->totloop,
- mesh->totpoly,
- poly_nors,
- !do_vert_normals);
+ /* Calculate poly/vert normals. */
+ if (do_vert_normals) {
+ BKE_mesh_calc_normals_poly_and_vertex(mesh->mvert,
+ mesh->totvert,
+ mesh->mloop,
+ mesh->totloop,
+ mesh->mpoly,
+ mesh->totpoly,
+ poly_nors,
+ nullptr);
+ }
+ else {
+ BKE_mesh_calc_normals_poly(mesh->mvert,
+ mesh->totvert,
+ mesh->mloop,
+ mesh->totloop,
+ mesh->mpoly,
+ mesh->totpoly,
+ poly_nors);
+ }
if (do_add_poly_nors_cddata) {
CustomData_add_layer(&mesh->pdata, CD_NORMAL, CD_ASSIGN, poly_nors, mesh->totpoly);
@@ -430,22 +378,23 @@ void BKE_mesh_ensure_normals_for_display(Mesh *mesh)
}
}
-/* Note that this does not update the CD_NORMAL layer,
- * but does update the normals in the CD_MVERT layer. */
+/**
+ * NOTE: this does not update the #CD_NORMAL layer,
+ * but does update the normals in the #CD_MVERT layer.
+ */
void BKE_mesh_calc_normals(Mesh *mesh)
{
#ifdef DEBUG_TIME
TIMEIT_START_AVERAGED(BKE_mesh_calc_normals);
#endif
- BKE_mesh_calc_normals_poly(mesh->mvert,
- nullptr,
- mesh->totvert,
- mesh->mloop,
- mesh->mpoly,
- mesh->totloop,
- mesh->totpoly,
- nullptr,
- false);
+ BKE_mesh_calc_normals_poly_and_vertex(mesh->mvert,
+ mesh->totvert,
+ mesh->mloop,
+ mesh->totloop,
+ mesh->mpoly,
+ mesh->totpoly,
+ nullptr,
+ nullptr);
#ifdef DEBUG_TIME
TIMEIT_END_AVERAGED(BKE_mesh_calc_normals);
#endif
@@ -488,7 +437,7 @@ void BKE_mesh_calc_normals_looptri(MVert *mverts,
mverts[vtri[2]].co);
}
- /* following Mesh convention; we use vertex coordinate itself for normal in this case */
+ /* Following Mesh convention; we use vertex coordinate itself for normal in this case. */
for (int i = 0; i < numVerts; i++) {
MVert *mv = &mverts[i];
float *no = tnorms[i];
@@ -628,11 +577,11 @@ void BKE_lnor_space_define(MLoopNorSpace *lnor_space,
BLI_stack_discard(edge_vectors);
nbr++;
}
- /* NOTE: In theory, this could be 'nbr > 2',
- * but there is one case where we only have two edges for two loops:
- * a smooth vertex with only two edges and two faces (our Monkey's nose has that, e.g.).
+ /* NOTE: In theory, this could be `nbr > 2`,
+ * but there is one case where we only have two edges for two loops:
+ * a smooth vertex with only two edges and two faces (our Monkey's nose has that, e.g.).
*/
- BLI_assert(nbr >= 2); /* This piece of code shall only be called for more than one loop... */
+ BLI_assert(nbr >= 2); /* This piece of code shall only be called for more than one loop. */
lnor_space->ref_alpha = alpha / (float)nbr;
}
else {
@@ -704,7 +653,7 @@ MINLINE float unit_short_to_float(const short val)
MINLINE short unit_float_to_short(const float val)
{
- /* Rounding... */
+ /* Rounding. */
return (short)floorf(val * (float)SHRT_MAX + 0.5f);
}
@@ -915,7 +864,7 @@ static void mesh_edges_sharp_tag(LoopSplitTaskDataCommon *data,
e2l[1] = INDEX_INVALID;
/* We want to avoid tagging edges as sharp when it is already defined as such by
- * other causes than angle threshold... */
+ * other causes than angle threshold. */
if (do_sharp_edges_tag && is_angle_sharp) {
BLI_BITMAP_SET(sharp_edges, ml_curr->e, true);
}
@@ -929,7 +878,7 @@ static void mesh_edges_sharp_tag(LoopSplitTaskDataCommon *data,
e2l[1] = INDEX_INVALID;
/* We want to avoid tagging edges as sharp when it is already defined as such by
- * other causes than angle threshold... */
+ * other causes than angle threshold. */
if (do_sharp_edges_tag) {
BLI_BITMAP_SET(sharp_edges, ml_curr->e, false);
}
@@ -981,7 +930,7 @@ void BKE_edges_sharp_from_angle_set(const struct MVert *mverts,
/* Simple mapping from a loop to its polygon index. */
int *loop_to_poly = (int *)MEM_malloc_arrayN((size_t)numLoops, sizeof(*loop_to_poly), __func__);
- LoopSplitTaskDataCommon common_data;
+ LoopSplitTaskDataCommon common_data = {};
common_data.mverts = mverts;
common_data.medges = medges;
common_data.mloops = mloops;
@@ -1011,14 +960,13 @@ void BKE_mesh_loop_manifold_fan_around_vert_next(const MLoop *mloops,
const MLoop *mlfan_next;
const MPoly *mpfan_next;
- /* Warning! This is rather complex!
+ /* WARNING: This is rather complex!
* We have to find our next edge around the vertex (fan mode).
* First we find the next loop, which is either previous or next to mlfan_curr_index, depending
* whether both loops using current edge are in the same direction or not, and whether
* mlfan_curr_index actually uses the vertex we are fanning around!
* mlfan_curr_index is the index of mlfan_next here, and mlfan_next is not the real next one
- * (i.e. not the future mlfan_curr)...
- */
+ * (i.e. not the future `mlfan_curr`). */
*r_mlfan_curr_index = (e2lfan_curr[0] == *r_mlfan_curr_index) ? e2lfan_curr[1] : e2lfan_curr[0];
*r_mpfan_curr_index = loop_to_poly[*r_mlfan_curr_index];
@@ -1043,7 +991,7 @@ void BKE_mesh_loop_manifold_fan_around_vert_next(const MLoop *mloops,
*r_mlfan_vert_index = *r_mlfan_curr_index;
}
*r_mlfan_curr = &mloops[*r_mlfan_curr_index];
- /* And now we are back in sync, mlfan_curr_index is the index of mlfan_curr! Pff! */
+ /* And now we are back in sync, mlfan_curr_index is the index of `mlfan_curr`! Pff! */
}
static void split_loop_nor_single_do(LoopSplitTaskDataCommon *common_data, LoopSplitTaskData *data)
@@ -1098,8 +1046,7 @@ static void split_loop_nor_single_do(LoopSplitTaskDataCommon *common_data, LoopS
normalize_v3(vec_prev);
BKE_lnor_space_define(lnor_space, *lnor, vec_curr, vec_prev, nullptr);
- /* We know there is only one loop in this space,
- * no need to create a linklist in this case... */
+ /* We know there is only one loop in this space, no need to create a link-list in this case. */
BKE_lnor_space_add_loop(lnors_spacearr, lnor_space, ml_curr_index, nullptr, true);
if (clnors_data) {
@@ -1135,24 +1082,24 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data, LoopSpli
BLI_Stack *edge_vectors = data->edge_vectors;
- /* Gah... We have to fan around current vertex, until we find the other non-smooth edge,
+ /* Sigh! we have to fan around current vertex, until we find the other non-smooth edge,
* and accumulate face normals into the vertex!
* Note in case this vertex has only one sharp edges, this is a waste because the normal is the
* same as the vertex normal, but I do not see any easy way to detect that (would need to count
* number of sharp edges per vertex, I doubt the additional memory usage would be worth it,
- * especially as it should not be a common case in real-life meshes anyway).
- */
+ * especially as it should not be a common case in real-life meshes anyway). */
const uint mv_pivot_index = ml_curr->v; /* The vertex we are "fanning" around! */
const MVert *mv_pivot = &mverts[mv_pivot_index];
- /* ml_curr would be mlfan_prev if we needed that one. */
+ /* `ml_curr` would be mlfan_prev if we needed that one. */
const MEdge *me_org = &medges[ml_curr->e];
const int *e2lfan_curr;
float vec_curr[3], vec_prev[3], vec_org[3];
const MLoop *mlfan_curr;
float lnor[3] = {0.0f, 0.0f, 0.0f};
- /* mlfan_vert_index: the loop of our current edge might not be the loop of our current vertex! */
+ /* `mlfan_vert_index` the loop of our current edge might not be the loop of our current vertex!
+ */
int mlfan_curr_index, mlfan_vert_index, mpfan_curr_index;
/* We validate clnors data on the fly - cheapest way to do! */
@@ -1189,14 +1136,14 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data, LoopSpli
}
}
- // printf("FAN: vert %d, start edge %d\n", mv_pivot_index, ml_curr->e);
+ // printf("FAN: vert %d, start edge %d\n", mv_pivot_index, ml_curr->e);
while (true) {
const MEdge *me_curr = &medges[mlfan_curr->e];
/* Compute edge vectors.
* NOTE: We could pre-compute those into an array, in the first iteration, instead of computing
* them twice (or more) here. However, time gained is not worth memory and time lost,
- * given the fact that this code should not be called that much in real-life meshes...
+ * given the fact that this code should not be called that much in real-life meshes.
*/
{
const MVert *mv_2 = (me_curr->v1 == mv_pivot_index) ? &mverts[me_curr->v2] :
@@ -1206,7 +1153,7 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data, LoopSpli
normalize_v3(vec_curr);
}
- // printf("\thandling edge %d / loop %d\n", mlfan_curr->e, mlfan_curr_index);
+ // printf("\thandling edge %d / loop %d\n", mlfan_curr->e, mlfan_curr_index);
{
/* Code similar to accumulate_vertex_normals_poly_v3. */
@@ -1246,9 +1193,8 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data, LoopSpli
if (IS_EDGE_SHARP(e2lfan_curr) || (me_curr == me_org)) {
/* Current edge is sharp and we have finished with this fan of faces around this vert,
- * or this vert is smooth, and we have completed a full turn around it.
- */
- // printf("FAN: Finished!\n");
+ * or this vert is smooth, and we have completed a full turn around it. */
+ // printf("FAN: Finished!\n");
break;
}
@@ -1389,12 +1335,13 @@ static bool loop_split_generator_check_cyclic_smooth_fan(const MLoop *mloops,
const uint mv_pivot_index = ml_curr->v; /* The vertex we are "fanning" around! */
const int *e2lfan_curr;
const MLoop *mlfan_curr;
- /* mlfan_vert_index: the loop of our current edge might not be the loop of our current vertex! */
+ /* `mlfan_vert_index` the loop of our current edge might not be the loop of our current vertex!
+ */
int mlfan_curr_index, mlfan_vert_index, mpfan_curr_index;
e2lfan_curr = e2l_prev;
if (IS_EDGE_SHARP(e2lfan_curr)) {
- /* Sharp loop, so not a cyclic smooth fan... */
+ /* Sharp loop, so not a cyclic smooth fan. */
return false;
}
@@ -1425,21 +1372,21 @@ static bool loop_split_generator_check_cyclic_smooth_fan(const MLoop *mloops,
e2lfan_curr = edge_to_loops[mlfan_curr->e];
if (IS_EDGE_SHARP(e2lfan_curr)) {
- /* Sharp loop/edge, so not a cyclic smooth fan... */
+ /* Sharp loop/edge, so not a cyclic smooth fan. */
return false;
}
- /* Smooth loop/edge... */
+ /* Smooth loop/edge. */
if (BLI_BITMAP_TEST(skip_loops, mlfan_vert_index)) {
if (mlfan_vert_index == ml_curr_index) {
/* We walked around a whole cyclic smooth fan without finding any already-processed loop,
- * means we can use initial ml_curr/ml_prev edge as start for this smooth fan. */
+ * means we can use initial `ml_curr` / `ml_prev` edge as start for this smooth fan. */
return true;
}
- /* ... already checked in some previous looping, we can abort. */
+ /* Already checked in some previous looping, we can abort. */
return false;
}
- /* ... we can skip it in future, and keep checking the smooth fan. */
+ /* We can skip it in future, and keep checking the smooth fan. */
BLI_BITMAP_ENABLE(skip_loops, mlfan_vert_index);
}
}
@@ -1501,7 +1448,7 @@ static void loop_split_generator(TaskPool *pool, LoopSplitTaskDataCommon *common
const int *e2l_prev = edge_to_loops[ml_prev->e];
#if 0
- printf("Checking loop %d / edge %u / vert %u (sharp edge: %d, skiploop: %d)...",
+ printf("Checking loop %d / edge %u / vert %u (sharp edge: %d, skiploop: %d)",
ml_curr_index,
ml_curr->e,
ml_curr->v,
@@ -1531,12 +1478,12 @@ static void loop_split_generator(TaskPool *pool, LoopSplitTaskDataCommon *common
ml_curr_index,
ml_prev_index,
mp_index))) {
- // printf("SKIPPING!\n");
+ // printf("SKIPPING!\n");
}
else {
LoopSplitTaskData *data, data_local;
- // printf("PROCESSING!\n");
+ // printf("PROCESSING!\n");
if (pool) {
if (data_idx == 0) {
@@ -1605,7 +1552,7 @@ static void loop_split_generator(TaskPool *pool, LoopSplitTaskDataCommon *common
}
}
- /* Last block of data... Since it is calloc'ed and we use first nullptr item as stopper,
+ /* Last block of data. Since it is calloc'ed and we use first nullptr item as stopper,
* everything is fine. */
if (pool && data_idx) {
BLI_task_pool_push(pool, loop_split_worker, data_buff, true, nullptr);
@@ -1652,8 +1599,7 @@ void BKE_mesh_normals_loop_split(const MVert *mverts,
* since we may want to use lnors even when mesh's 'autosmooth' is disabled
* (see e.g. mesh mapping code).
* As usual, we could handle that on case-by-case basis,
- * but simpler to keep it well confined here.
- */
+ * but simpler to keep it well confined here. */
int mp_index;
for (mp_index = 0; mp_index < numPolys; mp_index++) {
@@ -1736,7 +1682,7 @@ void BKE_mesh_normals_loop_split(const MVert *mverts,
mesh_edges_sharp_tag(&common_data, check_angle, split_angle, false);
if (numLoops < LOOP_SPLIT_TASK_BLOCK_SIZE * 8) {
- /* Not enough loops to be worth the whole threading overhead... */
+ /* Not enough loops to be worth the whole threading overhead. */
loop_split_generator(nullptr, &common_data);
}
else {
@@ -1791,13 +1737,12 @@ static void mesh_normals_loop_custom_set(const MVert *mverts,
short (*r_clnors_data)[2],
const bool use_vertices)
{
- /* We *may* make that poor BKE_mesh_normals_loop_split() even more complex by making it handling
+ /* We *may* make that poor #BKE_mesh_normals_loop_split() even more complex by making it handling
* that feature too, would probably be more efficient in absolute.
* However, this function *is not* performance-critical, since it is mostly expected to be called
- * by io addons when importing custom normals, and modifier
+ * by io add-ons when importing custom normals, and modifier
* (and perhaps from some editing tools later?).
- * So better to keep some simplicity here, and just call BKE_mesh_normals_loop_split() twice!
- */
+ * So better to keep some simplicity here, and just call #BKE_mesh_normals_loop_split() twice! */
MLoopNorSpaceArray lnors_spacearr = {nullptr};
BLI_bitmap *done_loops = BLI_BITMAP_NEW((size_t)numLoops, __func__);
float(*lnors)[3] = (float(*)[3])MEM_calloc_arrayN((size_t)numLoops, sizeof(*lnors), __func__);
@@ -1849,15 +1794,13 @@ static void mesh_normals_loop_custom_set(const MVert *mverts,
* This way, next time we run BKE_mesh_normals_loop_split(), we'll get lnor spacearr/smooth fans
* matching given custom lnors.
* Note this code *will never* unsharp edges! And quite obviously,
- * when we set custom normals per vertices, running this is absolutely useless.
- */
+ * when we set custom normals per vertices, running this is absolutely useless. */
if (!use_vertices) {
for (int i = 0; i < numLoops; i++) {
if (!lnors_spacearr.lspacearr[i]) {
/* This should not happen in theory, but in some rare case (probably ugly geometry)
* we can get some nullptr loopspacearr at this point. :/
- * Maybe we should set those loops' edges as sharp?
- */
+ * Maybe we should set those loops' edges as sharp? */
BLI_BITMAP_ENABLE(done_loops, i);
if (G.debug & G_DEBUG) {
printf("WARNING! Getting invalid nullptr loop space for loop %d!\n", i);
@@ -1867,12 +1810,12 @@ static void mesh_normals_loop_custom_set(const MVert *mverts,
if (!BLI_BITMAP_TEST(done_loops, i)) {
/* Notes:
- * * In case of mono-loop smooth fan, we have nothing to do.
- * * Loops in this linklist are ordered (in reversed order compared to how they were
+ * - In case of mono-loop smooth fan, we have nothing to do.
+ * - Loops in this linklist are ordered (in reversed order compared to how they were
* discovered by BKE_mesh_normals_loop_split(), but this is not a problem).
* Which means if we find a mismatching clnor,
* we know all remaining loops will have to be in a new, different smooth fan/lnor space.
- * * In smooth fan case, we compare each clnor against a ref one,
+ * - In smooth fan case, we compare each clnor against a ref one,
* to avoid small differences adding up into a real big one in the end!
*/
if (lnors_spacearr.lspacearr[i]->flags & MLNOR_SPACE_IS_SINGLE) {
@@ -1897,8 +1840,7 @@ static void mesh_normals_loop_custom_set(const MVert *mverts,
/* Current normal differs too much from org one, we have to tag the edge between
* previous loop's face and current's one as sharp.
* We know those two loops do not point to the same edge,
- * since we do not allow reversed winding in a same smooth fan.
- */
+ * since we do not allow reversed winding in a same smooth fan. */
const MPoly *mp = &mpolys[loop_to_poly[lidx]];
const MLoop *mlp =
&mloops[(lidx == mp->loopstart) ? mp->loopstart + mp->totloop - 1 : lidx - 1];
@@ -1970,8 +1912,7 @@ static void mesh_normals_loop_custom_set(const MVert *mverts,
if (BLI_BITMAP_TEST_BOOL(done_loops, i)) {
/* Note we accumulate and average all custom normals in current smooth fan,
* to avoid getting different clnors data (tiny differences in plain custom normals can
- * give rather huge differences in computed 2D factors).
- */
+ * give rather huge differences in computed 2D factors). */
LinkNode *loops = lnors_spacearr.lspacearr[i]->loops;
if (lnors_spacearr.lspacearr[i]->flags & MLNOR_SPACE_IS_SINGLE) {
BLI_assert(POINTER_AS_INT(loops) == i);
@@ -2087,15 +2028,14 @@ static void mesh_set_custom_normals(Mesh *mesh, float (*r_custom_nors)[3], const
bool free_polynors = false;
if (polynors == nullptr) {
polynors = (float(*)[3])MEM_mallocN(sizeof(float[3]) * (size_t)mesh->totpoly, __func__);
- BKE_mesh_calc_normals_poly(mesh->mvert,
- nullptr,
- mesh->totvert,
- mesh->mloop,
- mesh->mpoly,
- mesh->totloop,
- mesh->totpoly,
- polynors,
- false);
+ BKE_mesh_calc_normals_poly_and_vertex(mesh->mvert,
+ mesh->totvert,
+ mesh->mloop,
+ mesh->totloop,
+ mesh->mpoly,
+ mesh->totpoly,
+ polynors,
+ nullptr);
free_polynors = true;
}
diff --git a/source/blender/blenkernel/intern/mesh_remap.c b/source/blender/blenkernel/intern/mesh_remap.c
index c5e8858ea12..53a31cbbc7a 100644
--- a/source/blender/blenkernel/intern/mesh_remap.c
+++ b/source/blender/blenkernel/intern/mesh_remap.c
@@ -1379,14 +1379,12 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode,
}
if (dirty_nors_dst || do_poly_nors_dst) {
BKE_mesh_calc_normals_poly(verts_dst,
- NULL,
numverts_dst,
loops_dst,
- polys_dst,
numloops_dst,
+ polys_dst,
numpolys_dst,
- poly_nors_dst,
- true);
+ poly_nors_dst);
}
}
if (need_lnors_dst) {
@@ -2231,14 +2229,12 @@ void BKE_mesh_remap_calc_polys_from_mesh(const int mode,
}
if (dirty_nors_dst) {
BKE_mesh_calc_normals_poly(verts_dst,
- NULL,
numverts_dst,
loops_dst,
- polys_dst,
numloops_dst,
+ polys_dst,
numpolys_dst,
- poly_nors_dst,
- true);
+ poly_nors_dst);
}
}
diff --git a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc
index 8364b0ec024..9f5703a015d 100644
--- a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc
+++ b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc
@@ -33,6 +33,7 @@
#include "BLI_array.hh"
#include "BLI_float3.hh"
#include "BLI_index_range.hh"
+#include "BLI_span.hh"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
@@ -48,7 +49,9 @@
#include "bmesh_tools.h"
#ifdef WITH_OPENVDB
-# include "openvdb_capi.h"
+# include <openvdb/openvdb.h>
+# include <openvdb/tools/MeshToVolume.h>
+# include <openvdb/tools/VolumeToMesh.h>
#endif
#ifdef WITH_QUADRIFLOW
@@ -58,6 +61,8 @@
using blender::Array;
using blender::float3;
using blender::IndexRange;
+using blender::MutableSpan;
+using blender::Span;
#ifdef WITH_QUADRIFLOW
static Mesh *remesh_quadriflow(const Mesh *input_mesh,
@@ -192,91 +197,80 @@ Mesh *BKE_mesh_remesh_quadriflow(const Mesh *mesh,
}
#ifdef WITH_OPENVDB
-static struct OpenVDBLevelSet *remesh_voxel_level_set_create(const Mesh *mesh,
- struct OpenVDBTransform *transform)
+static openvdb::FloatGrid::Ptr remesh_voxel_level_set_create(const Mesh *mesh,
+ const float voxel_size)
{
- const MLoopTri *looptri = BKE_mesh_runtime_looptri_ensure(mesh);
- MVertTri *verttri = (MVertTri *)MEM_callocN(
- sizeof(*verttri) * BKE_mesh_runtime_looptri_len(mesh), "remesh_looptri");
- BKE_mesh_runtime_verttri_from_looptri(
- verttri, mesh->mloop, looptri, BKE_mesh_runtime_looptri_len(mesh));
+ Span<MLoop> mloop{mesh->mloop, mesh->totloop};
+ Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(mesh),
+ BKE_mesh_runtime_looptri_len(mesh)};
- const int totfaces = BKE_mesh_runtime_looptri_len(mesh);
- const int totverts = mesh->totvert;
- Array<float3> verts(totverts);
- Array<int> faces(totfaces * 3);
+ std::vector<openvdb::Vec3s> points(mesh->totvert);
+ std::vector<openvdb::Vec3I> triangles(looptris.size());
- for (const int i : IndexRange(totverts)) {
- verts[i] = mesh->mvert[i].co;
+ for (const int i : IndexRange(mesh->totvert)) {
+ const float3 co = mesh->mvert[i].co;
+ points[i] = openvdb::Vec3s(co.x, co.y, co.z);
}
- for (const int i : IndexRange(totfaces)) {
- MVertTri &vt = verttri[i];
- faces[i * 3] = vt.tri[0];
- faces[i * 3 + 1] = vt.tri[1];
- faces[i * 3 + 2] = vt.tri[2];
+ for (const int i : IndexRange(looptris.size())) {
+ const MLoopTri &loop_tri = looptris[i];
+ triangles[i] = openvdb::Vec3I(
+ mloop[loop_tri.tri[0]].v, mloop[loop_tri.tri[1]].v, mloop[loop_tri.tri[2]].v);
}
- struct OpenVDBLevelSet *level_set = OpenVDBLevelSet_create(false, nullptr);
- OpenVDBLevelSet_mesh_to_level_set(
- level_set, (const float *)verts.data(), faces.data(), totverts, totfaces, transform);
-
- MEM_freeN(verttri);
+ openvdb::math::Transform::Ptr transform = openvdb::math::Transform::createLinearTransform(
+ voxel_size);
+ openvdb::FloatGrid::Ptr grid = openvdb::tools::meshToLevelSet<openvdb::FloatGrid>(
+ *transform, points, triangles, 1.0f);
- return level_set;
+ return grid;
}
-static Mesh *remesh_voxel_volume_to_mesh(struct OpenVDBLevelSet *level_set,
- double isovalue,
- double adaptivity,
- bool relax_disoriented_triangles)
+static Mesh *remesh_voxel_volume_to_mesh(const openvdb::FloatGrid::Ptr level_set_grid,
+ const float isovalue,
+ const float adaptivity,
+ const bool relax_disoriented_triangles)
{
- struct OpenVDBVolumeToMeshData output_mesh;
- OpenVDBLevelSet_volume_to_mesh(
- level_set, &output_mesh, isovalue, adaptivity, relax_disoriented_triangles);
-
- Mesh *mesh = BKE_mesh_new_nomain(output_mesh.totvertices,
- 0,
- 0,
- (output_mesh.totquads * 4) + (output_mesh.tottriangles * 3),
- output_mesh.totquads + output_mesh.tottriangles);
-
- for (const int i : IndexRange(output_mesh.totvertices)) {
- copy_v3_v3(mesh->mvert[i].co, &output_mesh.vertices[i * 3]);
+ std::vector<openvdb::Vec3s> vertices;
+ std::vector<openvdb::Vec4I> quads;
+ std::vector<openvdb::Vec3I> tris;
+ openvdb::tools::volumeToMesh<openvdb::FloatGrid>(
+ *level_set_grid, vertices, tris, quads, isovalue, adaptivity, relax_disoriented_triangles);
+
+ Mesh *mesh = BKE_mesh_new_nomain(
+ vertices.size(), 0, 0, quads.size() * 4 + tris.size() * 3, quads.size() + tris.size());
+ MutableSpan<MVert> mverts{mesh->mvert, mesh->totvert};
+ MutableSpan<MLoop> mloops{mesh->mloop, mesh->totloop};
+ MutableSpan<MPoly> mpolys{mesh->mpoly, mesh->totpoly};
+
+ for (const int i : mverts.index_range()) {
+ copy_v3_v3(mverts[i].co, float3(vertices[i].x(), vertices[i].y(), vertices[i].z()));
}
- for (const int i : IndexRange(output_mesh.totquads)) {
- MPoly &poly = mesh->mpoly[i];
+ for (const int i : IndexRange(quads.size())) {
+ MPoly &poly = mpolys[i];
const int loopstart = i * 4;
poly.loopstart = loopstart;
poly.totloop = 4;
- mesh->mloop[loopstart].v = output_mesh.quads[loopstart];
- mesh->mloop[loopstart + 1].v = output_mesh.quads[loopstart + 1];
- mesh->mloop[loopstart + 2].v = output_mesh.quads[loopstart + 2];
- mesh->mloop[loopstart + 3].v = output_mesh.quads[loopstart + 3];
+ mloops[loopstart].v = quads[i][0];
+ mloops[loopstart + 1].v = quads[i][3];
+ mloops[loopstart + 2].v = quads[i][2];
+ mloops[loopstart + 3].v = quads[i][1];
}
- const int triangle_poly_start = output_mesh.totquads;
- const int triangle_loop_start = output_mesh.totquads * 4;
- for (const int i : IndexRange(output_mesh.tottriangles)) {
- MPoly &poly = mesh->mpoly[triangle_poly_start + i];
+ const int triangle_loop_start = quads.size() * 4;
+ for (const int i : IndexRange(tris.size())) {
+ MPoly &poly = mpolys[quads.size() + i];
const int loopstart = triangle_loop_start + i * 3;
poly.loopstart = loopstart;
poly.totloop = 3;
- mesh->mloop[loopstart].v = output_mesh.triangles[i * 3 + 2];
- mesh->mloop[loopstart + 1].v = output_mesh.triangles[i * 3 + 1];
- mesh->mloop[loopstart + 2].v = output_mesh.triangles[i * 3];
+ mloops[loopstart].v = tris[i][2];
+ mloops[loopstart + 1].v = tris[i][1];
+ mloops[loopstart + 2].v = tris[i][0];
}
BKE_mesh_calc_edges(mesh, false, false);
- BKE_mesh_calc_normals(mesh);
-
- MEM_freeN(output_mesh.quads);
- MEM_freeN(output_mesh.vertices);
-
- if (output_mesh.tottriangles > 0) {
- MEM_freeN(output_mesh.triangles);
- }
+ BKE_mesh_normals_tag_dirty(mesh);
return mesh;
}
@@ -288,14 +282,8 @@ Mesh *BKE_mesh_remesh_voxel(const Mesh *mesh,
const float isovalue)
{
#ifdef WITH_OPENVDB
- struct OpenVDBTransform *xform = OpenVDBTransform_create();
- OpenVDBTransform_create_linear_transform(xform, (double)voxel_size);
- struct OpenVDBLevelSet *level_set = remesh_voxel_level_set_create(mesh, xform);
- Mesh *new_mesh = remesh_voxel_volume_to_mesh(
- level_set, (double)isovalue, (double)adaptivity, false);
- OpenVDBLevelSet_free(level_set);
- OpenVDBTransform_free(xform);
- return new_mesh;
+ openvdb::FloatGrid::Ptr level_set = remesh_voxel_level_set_create(mesh, voxel_size);
+ return remesh_voxel_volume_to_mesh(level_set, isovalue, adaptivity, false);
#else
UNUSED_VARS(mesh, voxel_size, adaptivity, isovalue);
return nullptr;
@@ -304,7 +292,7 @@ Mesh *BKE_mesh_remesh_voxel(const Mesh *mesh,
void BKE_mesh_remesh_reproject_paint_mask(Mesh *target, Mesh *source)
{
- BVHTreeFromMesh bvhtree = {{nullptr}};
+ BVHTreeFromMesh bvhtree = {nullptr};
BKE_bvhtree_from_mesh_get(&bvhtree, source, BVHTREE_FROM_VERTS, 2);
MVert *target_verts = (MVert *)CustomData_get_layer(&target->vdata, CD_MVERT);
@@ -342,7 +330,7 @@ void BKE_mesh_remesh_reproject_paint_mask(Mesh *target, Mesh *source)
void BKE_remesh_reproject_sculpt_face_sets(Mesh *target, Mesh *source)
{
- BVHTreeFromMesh bvhtree = {{nullptr}};
+ BVHTreeFromMesh bvhtree = {nullptr};
const MPoly *target_polys = (const MPoly *)CustomData_get_layer(&target->pdata, CD_MPOLY);
const MVert *target_verts = (const MVert *)CustomData_get_layer(&target->vdata, CD_MVERT);
@@ -389,7 +377,7 @@ void BKE_remesh_reproject_sculpt_face_sets(Mesh *target, Mesh *source)
void BKE_remesh_reproject_vertex_paint(Mesh *target, const Mesh *source)
{
- BVHTreeFromMesh bvhtree = {{nullptr}};
+ 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);
diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c
index 2088c4268e6..821ca7b98b3 100644
--- a/source/blender/blenkernel/intern/modifier.c
+++ b/source/blender/blenkernel/intern/modifier.c
@@ -249,11 +249,11 @@ bool BKE_modifier_unique_name(ListBase *modifiers, ModifierData *md)
return false;
}
-bool BKE_modifier_depends_ontime(ModifierData *md)
+bool BKE_modifier_depends_ontime(Scene *scene, ModifierData *md, const int dag_eval_mode)
{
const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
- return mti->dependsOnTime && mti->dependsOnTime(md);
+ return mti->dependsOnTime && mti->dependsOnTime(scene, md, dag_eval_mode);
}
bool BKE_modifier_supports_mapping(ModifierData *md)
diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c
index f32b0c434c1..e507252307b 100644
--- a/source/blender/blenkernel/intern/movieclip.c
+++ b/source/blender/blenkernel/intern/movieclip.c
@@ -206,36 +206,35 @@ static void write_movieReconstruction(BlendWriter *writer,
static void movieclip_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
MovieClip *clip = (MovieClip *)id;
- if (clip->id.us > 0 || BLO_write_is_undo(writer)) {
- /* Clean up, important in undo case to reduce false detection of changed datablocks. */
- clip->anim = NULL;
- clip->tracking_context = NULL;
- clip->tracking.stats = NULL;
- MovieTracking *tracking = &clip->tracking;
- MovieTrackingObject *object;
+ /* Clean up, important in undo case to reduce false detection of changed datablocks. */
+ clip->anim = NULL;
+ clip->tracking_context = NULL;
+ clip->tracking.stats = NULL;
- BLO_write_id_struct(writer, MovieClip, id_address, &clip->id);
- BKE_id_blend_write(writer, &clip->id);
+ MovieTracking *tracking = &clip->tracking;
+ MovieTrackingObject *object;
- if (clip->adt) {
- BKE_animdata_blend_write(writer, clip->adt);
- }
+ BLO_write_id_struct(writer, MovieClip, id_address, &clip->id);
+ BKE_id_blend_write(writer, &clip->id);
+
+ if (clip->adt) {
+ BKE_animdata_blend_write(writer, clip->adt);
+ }
- write_movieTracks(writer, &tracking->tracks);
- write_moviePlaneTracks(writer, &tracking->plane_tracks);
- write_movieReconstruction(writer, &tracking->reconstruction);
+ write_movieTracks(writer, &tracking->tracks);
+ write_moviePlaneTracks(writer, &tracking->plane_tracks);
+ write_movieReconstruction(writer, &tracking->reconstruction);
- object = tracking->objects.first;
- while (object) {
- BLO_write_struct(writer, MovieTrackingObject, object);
+ object = tracking->objects.first;
+ while (object) {
+ BLO_write_struct(writer, MovieTrackingObject, object);
- write_movieTracks(writer, &object->tracks);
- write_moviePlaneTracks(writer, &object->plane_tracks);
- write_movieReconstruction(writer, &object->reconstruction);
+ write_movieTracks(writer, &object->tracks);
+ write_moviePlaneTracks(writer, &object->plane_tracks);
+ write_movieReconstruction(writer, &object->reconstruction);
- object = object->next;
- }
+ object = object->next;
}
}
diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c
index 54f0da30a2b..eaa11a6683a 100644
--- a/source/blender/blenkernel/intern/multires.c
+++ b/source/blender/blenkernel/intern/multires.c
@@ -468,15 +468,9 @@ void multires_force_sculpt_rebuild(Object *object)
object->sculpt->pbvh = NULL;
}
- if (ss->pmap != NULL) {
- MEM_freeN(ss->pmap);
- ss->pmap = NULL;
- }
+ MEM_SAFE_FREE(ss->pmap);
- if (ss->pmap_mem != NULL) {
- MEM_freeN(ss->pmap_mem);
- ss->pmap_mem = NULL;
- }
+ MEM_SAFE_FREE(ss->pmap_mem);
}
void multires_force_external_reload(Object *object)
diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc
index 9888e23a7bd..2a0e05a2616 100644
--- a/source/blender/blenkernel/intern/node.cc
+++ b/source/blender/blenkernel/intern/node.cc
@@ -606,19 +606,18 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree)
static void ntree_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
bNodeTree *ntree = (bNodeTree *)id;
- if (ntree->id.us > 0 || BLO_write_is_undo(writer)) {
- /* Clean up, important in undo case to reduce false detection of changed datablocks. */
- ntree->init = 0; /* to set callbacks and force setting types */
- ntree->is_updating = false;
- ntree->typeinfo = nullptr;
- ntree->interface_type = nullptr;
- ntree->progress = nullptr;
- ntree->execdata = nullptr;
- BLO_write_id_struct(writer, bNodeTree, id_address, &ntree->id);
+ /* Clean up, important in undo case to reduce false detection of changed datablocks. */
+ ntree->init = 0; /* to set callbacks and force setting types */
+ ntree->is_updating = false;
+ ntree->typeinfo = nullptr;
+ ntree->interface_type = nullptr;
+ ntree->progress = nullptr;
+ ntree->execdata = nullptr;
- ntreeBlendWrite(writer, ntree);
- }
+ BLO_write_id_struct(writer, bNodeTree, id_address, &ntree->id);
+
+ ntreeBlendWrite(writer, ntree);
}
static void direct_link_node_socket(BlendDataReader *reader, bNodeSocket *sock)
@@ -2364,7 +2363,7 @@ bNodeLink *nodeAddLink(
ntree->update |= NTREE_UPDATE_LINKS;
}
- if (link->tosock->flag & SOCK_MULTI_INPUT) {
+ if (link != nullptr && link->tosock->flag & SOCK_MULTI_INPUT) {
link->multi_input_socket_index = node_count_links(ntree, link->tosock) - 1;
}
@@ -3194,6 +3193,7 @@ void ntreeFreeEmbeddedTree(bNodeTree *ntree)
{
ntreeFreeTree(ntree);
BKE_libblock_free_data(&ntree->id, true);
+ BKE_libblock_free_data_py(&ntree->id);
}
void ntreeFreeLocalTree(bNodeTree *ntree)
@@ -5149,6 +5149,7 @@ static void registerGeometryNodes()
register_node_type_geo_curve_resample();
register_node_type_geo_curve_reverse();
register_node_type_geo_curve_set_handles();
+ register_node_type_geo_curve_spline_type();
register_node_type_geo_curve_subdivide();
register_node_type_geo_curve_to_mesh();
register_node_type_geo_curve_to_points();
@@ -5180,6 +5181,7 @@ static void registerGeometryNodes()
register_node_type_geo_points_to_volume();
register_node_type_geo_raycast();
register_node_type_geo_sample_texture();
+ register_node_type_geo_select_by_handle_type();
register_node_type_geo_select_by_material();
register_node_type_geo_separate_components();
register_node_type_geo_subdivision_surface();
diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c
index 89de37d6e4b..1c08a46adc3 100644
--- a/source/blender/blenkernel/intern/object.c
+++ b/source/blender/blenkernel/intern/object.c
@@ -144,6 +144,7 @@
#include "DRW_engine.h"
#include "BLO_read_write.h"
+#include "BLO_readfile.h"
#include "SEQ_sequencer.h"
@@ -522,74 +523,72 @@ static void object_blend_write(BlendWriter *writer, ID *id, const void *id_addre
Object *ob = (Object *)id;
const bool is_undo = BLO_write_is_undo(writer);
- if (ob->id.us > 0 || is_undo) {
- /* Clean up, important in undo case to reduce false detection of changed data-blocks. */
- BKE_object_runtime_reset(ob);
- if (is_undo) {
- /* For undo we stay in object mode during undo presses, so keep edit-mode disabled on save as
- * well, can help reducing false detection of changed data-blocks. */
- ob->mode &= ~OB_MODE_EDIT;
- }
+ /* Clean up, important in undo case to reduce false detection of changed data-blocks. */
+ BKE_object_runtime_reset(ob);
- /* write LibData */
- BLO_write_id_struct(writer, Object, id_address, &ob->id);
- BKE_id_blend_write(writer, &ob->id);
+ if (is_undo) {
+ /* For undo we stay in object mode during undo presses, so keep edit-mode disabled on save as
+ * well, can help reducing false detection of changed data-blocks. */
+ ob->mode &= ~OB_MODE_EDIT;
+ }
- if (ob->adt) {
- BKE_animdata_blend_write(writer, ob->adt);
- }
+ /* write LibData */
+ BLO_write_id_struct(writer, Object, id_address, &ob->id);
+ BKE_id_blend_write(writer, &ob->id);
+
+ if (ob->adt) {
+ BKE_animdata_blend_write(writer, ob->adt);
+ }
- /* direct data */
- BLO_write_pointer_array(writer, ob->totcol, ob->mat);
- BLO_write_raw(writer, sizeof(char) * ob->totcol, ob->matbits);
+ /* direct data */
+ BLO_write_pointer_array(writer, ob->totcol, ob->mat);
+ BLO_write_raw(writer, sizeof(char) * ob->totcol, ob->matbits);
- bArmature *arm = NULL;
- if (ob->type == OB_ARMATURE) {
- arm = ob->data;
- if (arm && ob->pose && arm->act_bone) {
- BLI_strncpy(
- ob->pose->proxy_act_bone, arm->act_bone->name, sizeof(ob->pose->proxy_act_bone));
- }
+ bArmature *arm = NULL;
+ if (ob->type == OB_ARMATURE) {
+ arm = ob->data;
+ if (arm && ob->pose && arm->act_bone) {
+ BLI_strncpy(ob->pose->proxy_act_bone, arm->act_bone->name, sizeof(ob->pose->proxy_act_bone));
}
+ }
- BKE_pose_blend_write(writer, ob->pose, arm);
- write_fmaps(writer, &ob->fmaps);
- BKE_constraint_blend_write(writer, &ob->constraints);
- animviz_motionpath_blend_write(writer, ob->mpath);
+ BKE_pose_blend_write(writer, ob->pose, arm);
+ write_fmaps(writer, &ob->fmaps);
+ BKE_constraint_blend_write(writer, &ob->constraints);
+ animviz_motionpath_blend_write(writer, ob->mpath);
- BLO_write_struct(writer, PartDeflect, ob->pd);
- if (ob->soft) {
- /* Set deprecated pointers to prevent crashes of older Blenders */
- ob->soft->pointcache = ob->soft->shared->pointcache;
- ob->soft->ptcaches = ob->soft->shared->ptcaches;
- BLO_write_struct(writer, SoftBody, ob->soft);
- BLO_write_struct(writer, SoftBody_Shared, ob->soft->shared);
- BKE_ptcache_blend_write(writer, &(ob->soft->shared->ptcaches));
- BLO_write_struct(writer, EffectorWeights, ob->soft->effector_weights);
- }
+ BLO_write_struct(writer, PartDeflect, ob->pd);
+ if (ob->soft) {
+ /* Set deprecated pointers to prevent crashes of older Blenders */
+ ob->soft->pointcache = ob->soft->shared->pointcache;
+ ob->soft->ptcaches = ob->soft->shared->ptcaches;
+ BLO_write_struct(writer, SoftBody, ob->soft);
+ BLO_write_struct(writer, SoftBody_Shared, ob->soft->shared);
+ BKE_ptcache_blend_write(writer, &(ob->soft->shared->ptcaches));
+ BLO_write_struct(writer, EffectorWeights, ob->soft->effector_weights);
+ }
- if (ob->rigidbody_object) {
- /* TODO: if any extra data is added to handle duplis, will need separate function then */
- BLO_write_struct(writer, RigidBodyOb, ob->rigidbody_object);
- }
- if (ob->rigidbody_constraint) {
- BLO_write_struct(writer, RigidBodyCon, ob->rigidbody_constraint);
- }
+ if (ob->rigidbody_object) {
+ /* TODO: if any extra data is added to handle duplis, will need separate function then */
+ BLO_write_struct(writer, RigidBodyOb, ob->rigidbody_object);
+ }
+ if (ob->rigidbody_constraint) {
+ BLO_write_struct(writer, RigidBodyCon, ob->rigidbody_constraint);
+ }
- if (ob->type == OB_EMPTY && ob->empty_drawtype == OB_EMPTY_IMAGE) {
- BLO_write_struct(writer, ImageUser, ob->iuser);
- }
+ if (ob->type == OB_EMPTY && ob->empty_drawtype == OB_EMPTY_IMAGE) {
+ BLO_write_struct(writer, ImageUser, ob->iuser);
+ }
- BKE_particle_system_blend_write(writer, &ob->particlesystem);
- BKE_modifier_blend_write(writer, &ob->modifiers);
- BKE_gpencil_modifier_blend_write(writer, &ob->greasepencil_modifiers);
- BKE_shaderfx_blend_write(writer, &ob->shader_fx);
+ BKE_particle_system_blend_write(writer, &ob->particlesystem);
+ BKE_modifier_blend_write(writer, &ob->modifiers);
+ BKE_gpencil_modifier_blend_write(writer, &ob->greasepencil_modifiers);
+ BKE_shaderfx_blend_write(writer, &ob->shader_fx);
- BLO_write_struct_list(writer, LinkData, &ob->pc_ids);
+ BLO_write_struct_list(writer, LinkData, &ob->pc_ids);
- BKE_previewimg_blend_write(writer, ob->preview);
- }
+ BKE_previewimg_blend_write(writer, ob->preview);
}
/* XXX deprecated - old animation system */
@@ -833,7 +832,7 @@ static void object_blend_read_lib(BlendLibReader *reader, ID *id)
{
Object *ob = (Object *)id;
- bool warn = false;
+ BlendFileReadReport *reports = BLO_read_lib_reports(reader);
/* XXX deprecated - old animation system <<< */
BLO_read_id_address(reader, ob->id.lib, &ob->ipo);
@@ -851,8 +850,8 @@ static void object_blend_read_lib(BlendLibReader *reader, ID *id)
else {
if (ob->instance_collection != NULL) {
ID *new_id = BLO_read_get_new_id_address(reader, ob->id.lib, &ob->instance_collection->id);
- BLO_reportf_wrap(BLO_read_lib_reports(reader),
- RPT_WARNING,
+ BLO_reportf_wrap(reports,
+ RPT_INFO,
TIP_("Non-Empty object '%s' cannot duplicate collection '%s' "
"anymore in Blender 2.80, removed instancing"),
ob->id.name + 2,
@@ -870,11 +869,17 @@ static void object_blend_read_lib(BlendLibReader *reader, ID *id)
ob->proxy = NULL;
if (ob->id.lib) {
- printf("Proxy lost from object %s lib %s\n", ob->id.name + 2, ob->id.lib->filepath);
+ BLO_reportf_wrap(reports,
+ RPT_INFO,
+ TIP_("Proxy lost from object %s lib %s\n"),
+ ob->id.name + 2,
+ ob->id.lib->filepath);
}
else {
- printf("Proxy lost from object %s lib <NONE>\n", ob->id.name + 2);
+ BLO_reportf_wrap(
+ reports, RPT_INFO, TIP_("Proxy lost from object %s lib <NONE>\n"), ob->id.name + 2);
}
+ reports->count.missing_obproxies++;
}
else {
/* this triggers object_update to always use a copy */
@@ -887,15 +892,7 @@ static void object_blend_read_lib(BlendLibReader *reader, ID *id)
BLO_read_id_address(reader, ob->id.lib, &ob->data);
if (ob->data == NULL && poin != NULL) {
- if (ob->id.lib) {
- printf("Can't find obdata of %s lib %s\n", ob->id.name + 2, ob->id.lib->filepath);
- }
- else {
- printf("Object %s lost data.\n", ob->id.name + 2);
- }
-
ob->type = OB_EMPTY;
- warn = true;
if (ob->pose) {
/* we can't call #BKE_pose_free() here because of library linking
@@ -911,6 +908,18 @@ static void object_blend_read_lib(BlendLibReader *reader, ID *id)
ob->pose = NULL;
ob->mode &= ~OB_MODE_POSE;
}
+
+ if (ob->id.lib) {
+ BLO_reportf_wrap(reports,
+ RPT_INFO,
+ TIP_("Can't find object data of %s lib %s\n"),
+ ob->id.name + 2,
+ ob->id.lib->filepath);
+ }
+ else {
+ BLO_reportf_wrap(reports, RPT_INFO, TIP_("Object %s lost data\n"), ob->id.name + 2);
+ }
+ reports->count.missing_obdata++;
}
for (int a = 0; a < ob->totcol; a++) {
BLO_read_id_address(reader, ob->id.lib, &ob->mat[a]);
@@ -922,7 +931,7 @@ static void object_blend_read_lib(BlendLibReader *reader, ID *id)
const short *totcol_data = BKE_object_material_len_p(ob);
/* Only expand so as not to lose any object materials that might be set. */
if (totcol_data && (*totcol_data > ob->totcol)) {
- /* printf("'%s' %d -> %d\n", ob->id.name, ob->totcol, *totcol_data); */
+ // printf("'%s' %d -> %d\n", ob->id.name, ob->totcol, *totcol_data);
BKE_object_material_resize(BLO_read_lib_get_main(reader), ob, *totcol_data, false);
}
}
@@ -992,10 +1001,6 @@ static void object_blend_read_lib(BlendLibReader *reader, ID *id)
BLO_read_id_address(reader, ob->id.lib, &ob->rigidbody_constraint->ob1);
BLO_read_id_address(reader, ob->id.lib, &ob->rigidbody_constraint->ob2);
}
-
- if (warn) {
- BLO_reportf_wrap(BLO_read_lib_reports(reader), RPT_WARNING, "Warning in console");
- }
}
/* XXX deprecated - old animation system */
@@ -2074,6 +2079,12 @@ static void object_init(Object *ob, const short ob_type)
if (ob->type == OB_GPENCIL) {
ob->dtx |= OB_USE_GPENCIL_LIGHTS;
}
+
+ if (ob->type == OB_LAMP) {
+ /* Lights are invisible to camera rays and are assumed to be a
+ * shadow catcher by default. */
+ ob->visibility_flag |= OB_HIDE_CAMERA | OB_SHADOW_CATCHER;
+ }
}
void *BKE_object_obdata_add_from_type(Main *bmain, int type, const char *name)
@@ -4034,10 +4045,7 @@ void BKE_object_empty_draw_type_set(Object *ob, const int value)
}
}
else {
- if (ob->iuser) {
- MEM_freeN(ob->iuser);
- ob->iuser = NULL;
- }
+ MEM_SAFE_FREE(ob->iuser);
}
}
@@ -5401,9 +5409,12 @@ KDTree_3d *BKE_object_as_kdtree(Object *ob, int *r_tot)
return tree;
}
-bool BKE_object_modifier_use_time(Object *ob, ModifierData *md)
+bool BKE_object_modifier_use_time(Scene *scene,
+ Object *ob,
+ ModifierData *md,
+ const int dag_eval_mode)
{
- if (BKE_modifier_depends_ontime(md)) {
+ if (BKE_modifier_depends_ontime(scene, md, dag_eval_mode)) {
return true;
}
diff --git a/source/blender/blenkernel/intern/object_deform.c b/source/blender/blenkernel/intern/object_deform.c
index c69326a23c6..511f5d4ae66 100644
--- a/source/blender/blenkernel/intern/object_deform.c
+++ b/source/blender/blenkernel/intern/object_deform.c
@@ -307,10 +307,7 @@ static void object_defgroup_remove_common(Object *ob, bDeformGroup *dg, const in
}
else if (ob->type == OB_LATTICE) {
Lattice *lt = object_defgroup_lattice_get((ID *)(ob->data));
- if (lt->dvert) {
- MEM_freeN(lt->dvert);
- lt->dvert = NULL;
- }
+ MEM_SAFE_FREE(lt->dvert);
}
}
else if (BKE_object_defgroup_active_index_get(ob) < 1) {
@@ -465,10 +462,7 @@ void BKE_object_defgroup_remove_all_ex(struct Object *ob, bool only_unlocked)
}
else if (ob->type == OB_LATTICE) {
Lattice *lt = object_defgroup_lattice_get((ID *)(ob->data));
- if (lt->dvert) {
- MEM_freeN(lt->dvert);
- lt->dvert = NULL;
- }
+ MEM_SAFE_FREE(lt->dvert);
}
/* Fix counters/indices */
BKE_object_defgroup_active_index_set(ob, 0);
diff --git a/source/blender/blenkernel/intern/object_dupli.cc b/source/blender/blenkernel/intern/object_dupli.cc
index 77969328365..141a9a25eca 100644
--- a/source/blender/blenkernel/intern/object_dupli.cc
+++ b/source/blender/blenkernel/intern/object_dupli.cc
@@ -1554,15 +1554,15 @@ static const DupliGenerator gen_dupli_particles = {
static const DupliGenerator *get_dupli_generator(const DupliContext *ctx)
{
int transflag = ctx->object->transflag;
- int restrictflag = ctx->object->restrictflag;
+ int visibility_flag = ctx->object->visibility_flag;
if ((transflag & OB_DUPLI) == 0 && ctx->object->runtime.geometry_set_eval == nullptr) {
return nullptr;
}
/* Should the dupli's be generated for this object? - Respect restrict flags. */
- if (DEG_get_mode(ctx->depsgraph) == DAG_EVAL_RENDER ? (restrictflag & OB_RESTRICT_RENDER) :
- (restrictflag & OB_RESTRICT_VIEWPORT)) {
+ if (DEG_get_mode(ctx->depsgraph) == DAG_EVAL_RENDER ? (visibility_flag & OB_HIDE_RENDER) :
+ (visibility_flag & OB_HIDE_VIEWPORT)) {
return nullptr;
}
diff --git a/source/blender/blenkernel/intern/ocean.c b/source/blender/blenkernel/intern/ocean.c
index 3aee5cd639d..e9683d3b52c 100644
--- a/source/blender/blenkernel/intern/ocean.c
+++ b/source/blender/blenkernel/intern/ocean.c
@@ -530,7 +530,7 @@ static void ocean_compute_jacobian_jxx(TaskPool *__restrict pool, void *UNUSED(t
for (j = 0; j <= o->_N / 2; j++) {
fftw_complex mul_param;
- /* init_complex(mul_param, -scale, 0); */
+ // init_complex(mul_param, -scale, 0);
init_complex(mul_param, -1, 0);
mul_complex_f(mul_param, mul_param, chop_amount);
@@ -563,7 +563,7 @@ static void ocean_compute_jacobian_jzz(TaskPool *__restrict pool, void *UNUSED(t
for (j = 0; j <= o->_N / 2; j++) {
fftw_complex mul_param;
- /* init_complex(mul_param, -scale, 0); */
+ // init_complex(mul_param, -scale, 0);
init_complex(mul_param, -1, 0);
mul_complex_f(mul_param, mul_param, chop_amount);
@@ -596,7 +596,7 @@ static void ocean_compute_jacobian_jxz(TaskPool *__restrict pool, void *UNUSED(t
for (j = 0; j <= o->_N / 2; j++) {
fftw_complex mul_param;
- /* init_complex(mul_param, -scale, 0); */
+ // init_complex(mul_param, -scale, 0);
init_complex(mul_param, -1, 0);
mul_complex_f(mul_param, mul_param, chop_amount);
@@ -650,6 +650,14 @@ static void ocean_compute_normal_z(TaskPool *__restrict pool, void *UNUSED(taskd
fftw_execute(o->_N_z_plan);
}
+/**
+ * Return true if the ocean is valid and can be used.
+ */
+bool BKE_ocean_is_valid(const struct Ocean *o)
+{
+ return o->_k != NULL;
+}
+
void BKE_ocean_simulate(struct Ocean *o, float t, float scale, float chop_amount)
{
TaskPool *pool;
@@ -769,7 +777,10 @@ bool BKE_ocean_ensure(struct OceanModifierData *omd, const int resolution)
return true;
}
-void BKE_ocean_init_from_modifier(struct Ocean *ocean,
+/**
+ * Return true if the ocean data is valid and can be used.
+ */
+bool BKE_ocean_init_from_modifier(struct Ocean *ocean,
struct OceanModifierData const *omd,
const int resolution)
{
@@ -783,31 +794,34 @@ void BKE_ocean_init_from_modifier(struct Ocean *ocean,
BKE_ocean_free_data(ocean);
- BKE_ocean_init(ocean,
- resolution * resolution,
- resolution * resolution,
- omd->spatial_size,
- omd->spatial_size,
- omd->wind_velocity,
- omd->smallest_wave,
- 1.0,
- omd->wave_direction,
- omd->damp,
- omd->wave_alignment,
- omd->depth,
- omd->time,
- omd->spectrum,
- omd->fetch_jonswap,
- omd->sharpen_peak_jonswap,
- do_heightfield,
- do_chop,
- do_spray,
- do_normals,
- do_jacobian,
- omd->seed);
-}
-
-void BKE_ocean_init(struct Ocean *o,
+ return BKE_ocean_init(ocean,
+ resolution * resolution,
+ resolution * resolution,
+ omd->spatial_size,
+ omd->spatial_size,
+ omd->wind_velocity,
+ omd->smallest_wave,
+ 1.0,
+ omd->wave_direction,
+ omd->damp,
+ omd->wave_alignment,
+ omd->depth,
+ omd->time,
+ omd->spectrum,
+ omd->fetch_jonswap,
+ omd->sharpen_peak_jonswap,
+ do_heightfield,
+ do_chop,
+ do_spray,
+ do_normals,
+ do_jacobian,
+ omd->seed);
+}
+
+/**
+ * Return true if the ocean data is valid and can be used.
+ */
+bool BKE_ocean_init(struct Ocean *o,
int M,
int N,
float Lx,
@@ -830,7 +844,6 @@ void BKE_ocean_init(struct Ocean *o,
short do_jacobian,
int seed)
{
- RNG *rng;
int i, j, ii;
BLI_rw_mutex_lock(&o->oceanmutex, THREAD_LOCK_WRITE);
@@ -858,18 +871,34 @@ void BKE_ocean_init(struct Ocean *o,
o->_fetch_jonswap = fetch_jonswap;
o->_sharpen_peak_jonswap = sharpen_peak_jonswap * 10.0f;
+ /* NOTE: most modifiers don't account for failure to allocate.
+ * In this case however a large resolution can easily perform large allocations that fail,
+ * support early exiting in this case. */
+ if ((o->_k = (float *)MEM_mallocN(sizeof(float) * (size_t)M * (1 + N / 2), "ocean_k")) &&
+ (o->_h0 = (fftw_complex *)MEM_mallocN(sizeof(fftw_complex) * (size_t)M * N, "ocean_h0")) &&
+ (o->_h0_minus = (fftw_complex *)MEM_mallocN(sizeof(fftw_complex) * (size_t)M * N,
+ "ocean_h0_minus")) &&
+ (o->_kx = (float *)MEM_mallocN(sizeof(float) * o->_M, "ocean_kx")) &&
+ (o->_kz = (float *)MEM_mallocN(sizeof(float) * o->_N, "ocean_kz"))) {
+ /* Success. */
+ }
+ else {
+ MEM_SAFE_FREE(o->_k);
+ MEM_SAFE_FREE(o->_h0);
+ MEM_SAFE_FREE(o->_h0_minus);
+ MEM_SAFE_FREE(o->_kx);
+ MEM_SAFE_FREE(o->_kz);
+
+ BLI_rw_mutex_unlock(&o->oceanmutex);
+ return false;
+ }
+
o->_do_disp_y = do_height_field;
o->_do_normals = do_normals;
o->_do_spray = do_spray;
o->_do_chop = do_chop;
o->_do_jacobian = do_jacobian;
- o->_k = (float *)MEM_mallocN(M * (1 + N / 2) * sizeof(float), "ocean_k");
- o->_h0 = (fftw_complex *)MEM_mallocN(M * N * sizeof(fftw_complex), "ocean_h0");
- o->_h0_minus = (fftw_complex *)MEM_mallocN(M * N * sizeof(fftw_complex), "ocean_h0_minus");
- o->_kx = (float *)MEM_mallocN(o->_M * sizeof(float), "ocean_kx");
- o->_kz = (float *)MEM_mallocN(o->_N * sizeof(float), "ocean_kz");
-
/* make this robust in the face of erroneous usage */
if (o->_Lx == 0.0f) {
o->_Lx = 0.001f;
@@ -902,11 +931,11 @@ void BKE_ocean_init(struct Ocean *o,
/* pre-calculate the k matrix */
for (i = 0; i < o->_M; i++) {
for (j = 0; j <= o->_N / 2; j++) {
- o->_k[i * (1 + o->_N / 2) + j] = sqrt(o->_kx[i] * o->_kx[i] + o->_kz[j] * o->_kz[j]);
+ o->_k[(size_t)i * (1 + o->_N / 2) + j] = sqrt(o->_kx[i] * o->_kx[i] + o->_kz[j] * o->_kz[j]);
}
}
- rng = BLI_rng_new(seed);
+ RNG *rng = BLI_rng_new(seed);
for (i = 0; i < o->_M; i++) {
for (j = 0; j < o->_N; j++) {
@@ -986,7 +1015,7 @@ void BKE_ocean_init(struct Ocean *o,
"ocean_fft_in_nz");
o->_N_x = (double *)MEM_mallocN(o->_M * o->_N * sizeof(double), "ocean_N_x");
- /* o->_N_y = (float *) fftwf_malloc(o->_M * o->_N * sizeof(float)); (MEM01) */
+ // o->_N_y = (float *) fftwf_malloc(o->_M * o->_N * sizeof(float)); /* (MEM01) */
o->_N_z = (double *)MEM_mallocN(o->_M * o->_N * sizeof(double), "ocean_N_z");
o->_N_x_plan = fftw_plan_dft_c2r_2d(o->_M, o->_N, o->_fft_in_nx, o->_N_x, FFTW_ESTIMATE);
@@ -1029,6 +1058,8 @@ void BKE_ocean_init(struct Ocean *o,
set_height_normalize_factor(o);
BLI_rng_free(rng);
+
+ return true;
}
void BKE_ocean_free_data(struct Ocean *oc)
@@ -1052,7 +1083,7 @@ void BKE_ocean_free_data(struct Ocean *oc)
fftw_destroy_plan(oc->_N_x_plan);
fftw_destroy_plan(oc->_N_z_plan);
MEM_freeN(oc->_N_x);
- /* fftwf_free(oc->_N_y); (MEM01) */
+ // fftwf_free(oc->_N_y); /* (MEM01) */
MEM_freeN(oc->_N_z);
}
@@ -1608,7 +1639,7 @@ struct Ocean *BKE_ocean_add(void)
return oc;
}
-void BKE_ocean_init(struct Ocean *UNUSED(o),
+bool BKE_ocean_init(struct Ocean *UNUSED(o),
int UNUSED(M),
int UNUSED(N),
float UNUSED(Lx),
@@ -1631,6 +1662,7 @@ void BKE_ocean_init(struct Ocean *UNUSED(o),
short UNUSED(do_jacobian),
int UNUSED(seed))
{
+ return false;
}
void BKE_ocean_free_data(struct Ocean *UNUSED(oc))
@@ -1700,10 +1732,11 @@ void BKE_ocean_bake(struct Ocean *UNUSED(o),
(void)update_cb;
}
-void BKE_ocean_init_from_modifier(struct Ocean *UNUSED(ocean),
+bool BKE_ocean_init_from_modifier(struct Ocean *UNUSED(ocean),
struct OceanModifierData const *UNUSED(omd),
int UNUSED(resolution))
{
+ return true;
}
#endif /* WITH_OCEANSIM */
diff --git a/source/blender/blenkernel/intern/ocean_spectrum.c b/source/blender/blenkernel/intern/ocean_spectrum.c
index 7ed70234baf..c5504b22b43 100644
--- a/source/blender/blenkernel/intern/ocean_spectrum.c
+++ b/source/blender/blenkernel/intern/ocean_spectrum.c
@@ -77,7 +77,7 @@ static float ocean_spectrum_wind_and_damp(const Ocean *oc,
float newval = val * pow(fabs(k_dot_w), oc->_wind_alignment);
/* Eliminate wavelengths smaller than cutoff. */
- /* val *= exp(-k2 * m_cutoff); */
+ // val *= exp(-k2 * m_cutoff);
/* Reduce reflected waves. */
if (k_dot_w < 0.0f) {
diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c
index a1fa6aae1ce..d6030941c6d 100644
--- a/source/blender/blenkernel/intern/paint.c
+++ b/source/blender/blenkernel/intern/paint.c
@@ -108,14 +108,13 @@ static void palette_free_data(ID *id)
static void palette_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
Palette *palette = (Palette *)id;
- if (palette->id.us > 0 || BLO_write_is_undo(writer)) {
- PaletteColor *color;
- BLO_write_id_struct(writer, Palette, id_address, &palette->id);
- BKE_id_blend_write(writer, &palette->id);
- for (color = palette->colors.first; color; color = color->next) {
- BLO_write_struct(writer, PaletteColor, color);
- }
+ PaletteColor *color;
+ BLO_write_id_struct(writer, Palette, id_address, &palette->id);
+ BKE_id_blend_write(writer, &palette->id);
+
+ for (color = palette->colors.first; color; color = color->next) {
+ BLO_write_struct(writer, PaletteColor, color);
}
}
@@ -187,12 +186,11 @@ static void paint_curve_free_data(ID *id)
static void paint_curve_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
PaintCurve *pc = (PaintCurve *)id;
- if (pc->id.us > 0 || BLO_write_is_undo(writer)) {
- BLO_write_id_struct(writer, PaintCurve, id_address, &pc->id);
- BKE_id_blend_write(writer, &pc->id);
- BLO_write_struct_array(writer, PaintCurvePoint, pc->tot_points, pc->points);
- }
+ BLO_write_id_struct(writer, PaintCurve, id_address, &pc->id);
+ BKE_id_blend_write(writer, &pc->id);
+
+ BLO_write_struct_array(writer, PaintCurvePoint, pc->tot_points, pc->points);
}
static void paint_curve_blend_read_data(BlendDataReader *reader, ID *id)
diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c
index f2f3c5d4ca6..29849c69b6f 100644
--- a/source/blender/blenkernel/intern/particle.c
+++ b/source/blender/blenkernel/intern/particle.c
@@ -255,60 +255,59 @@ static void write_boid_state(BlendWriter *writer, BoidState *state)
static void particle_settings_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
ParticleSettings *part = (ParticleSettings *)id;
- if (part->id.us > 0 || BLO_write_is_undo(writer)) {
- /* write LibData */
- BLO_write_id_struct(writer, ParticleSettings, id_address, &part->id);
- BKE_id_blend_write(writer, &part->id);
- if (part->adt) {
- BKE_animdata_blend_write(writer, part->adt);
- }
- BLO_write_struct(writer, PartDeflect, part->pd);
- BLO_write_struct(writer, PartDeflect, part->pd2);
- BLO_write_struct(writer, EffectorWeights, part->effector_weights);
+ /* write LibData */
+ BLO_write_id_struct(writer, ParticleSettings, id_address, &part->id);
+ BKE_id_blend_write(writer, &part->id);
- if (part->clumpcurve) {
- BKE_curvemapping_blend_write(writer, part->clumpcurve);
- }
- if (part->roughcurve) {
- BKE_curvemapping_blend_write(writer, part->roughcurve);
- }
- if (part->twistcurve) {
- BKE_curvemapping_blend_write(writer, part->twistcurve);
- }
+ if (part->adt) {
+ BKE_animdata_blend_write(writer, part->adt);
+ }
+ BLO_write_struct(writer, PartDeflect, part->pd);
+ BLO_write_struct(writer, PartDeflect, part->pd2);
+ BLO_write_struct(writer, EffectorWeights, part->effector_weights);
- LISTBASE_FOREACH (ParticleDupliWeight *, dw, &part->instance_weights) {
- /* update indices, but only if dw->ob is set (can be NULL after loading e.g.) */
- if (dw->ob != NULL) {
- dw->index = 0;
- if (part->instance_collection) { /* can be NULL if lining fails or set to None */
- FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (part->instance_collection, object) {
- if (object == dw->ob) {
- break;
- }
- dw->index++;
+ if (part->clumpcurve) {
+ BKE_curvemapping_blend_write(writer, part->clumpcurve);
+ }
+ if (part->roughcurve) {
+ BKE_curvemapping_blend_write(writer, part->roughcurve);
+ }
+ if (part->twistcurve) {
+ BKE_curvemapping_blend_write(writer, part->twistcurve);
+ }
+
+ LISTBASE_FOREACH (ParticleDupliWeight *, dw, &part->instance_weights) {
+ /* update indices, but only if dw->ob is set (can be NULL after loading e.g.) */
+ if (dw->ob != NULL) {
+ dw->index = 0;
+ if (part->instance_collection) { /* can be NULL if lining fails or set to None */
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (part->instance_collection, object) {
+ if (object == dw->ob) {
+ break;
}
- FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
+ dw->index++;
}
+ FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
}
- BLO_write_struct(writer, ParticleDupliWeight, dw);
}
+ BLO_write_struct(writer, ParticleDupliWeight, dw);
+ }
- if (part->boids && part->phystype == PART_PHYS_BOIDS) {
- BLO_write_struct(writer, BoidSettings, part->boids);
+ if (part->boids && part->phystype == PART_PHYS_BOIDS) {
+ BLO_write_struct(writer, BoidSettings, part->boids);
- LISTBASE_FOREACH (BoidState *, state, &part->boids->states) {
- write_boid_state(writer, state);
- }
- }
- if (part->fluid && part->phystype == PART_PHYS_FLUID) {
- BLO_write_struct(writer, SPHFluidSettings, part->fluid);
+ LISTBASE_FOREACH (BoidState *, state, &part->boids->states) {
+ write_boid_state(writer, state);
}
+ }
+ if (part->fluid && part->phystype == PART_PHYS_FLUID) {
+ BLO_write_struct(writer, SPHFluidSettings, part->fluid);
+ }
- for (int a = 0; a < MAX_MTEX; a++) {
- if (part->mtex[a]) {
- BLO_write_struct(writer, MTex, part->mtex[a]);
- }
+ for (int a = 0; a < MAX_MTEX; a++) {
+ if (part->mtex[a]) {
+ BLO_write_struct(writer, MTex, part->mtex[a]);
}
}
}
@@ -928,10 +927,7 @@ void free_hair(Object *object, ParticleSystem *psys, int dynamics)
LOOP_PARTICLES
{
- if (pa->hair) {
- MEM_freeN(pa->hair);
- }
- pa->hair = NULL;
+ MEM_SAFE_FREE(pa->hair);
pa->totkey = 0;
}
@@ -1044,25 +1040,13 @@ void psys_free_particles(ParticleSystem *psys)
void psys_free_pdd(ParticleSystem *psys)
{
if (psys->pdd) {
- if (psys->pdd->cdata) {
- MEM_freeN(psys->pdd->cdata);
- }
- psys->pdd->cdata = NULL;
+ MEM_SAFE_FREE(psys->pdd->cdata);
- if (psys->pdd->vdata) {
- MEM_freeN(psys->pdd->vdata);
- }
- psys->pdd->vdata = NULL;
+ MEM_SAFE_FREE(psys->pdd->vdata);
- if (psys->pdd->ndata) {
- MEM_freeN(psys->pdd->ndata);
- }
- psys->pdd->ndata = NULL;
+ MEM_SAFE_FREE(psys->pdd->ndata);
- if (psys->pdd->vedata) {
- MEM_freeN(psys->pdd->vedata);
- }
- psys->pdd->vedata = NULL;
+ MEM_SAFE_FREE(psys->pdd->vedata);
psys->pdd->totpoint = 0;
psys->pdd->totpart = 0;
diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c
index 06d3daaf4d6..60edb78f8ba 100644
--- a/source/blender/blenkernel/intern/particle_system.c
+++ b/source/blender/blenkernel/intern/particle_system.c
@@ -169,10 +169,7 @@ void psys_reset(ParticleSystem *psys, int mode)
}
/* reset children */
- if (psys->child) {
- MEM_freeN(psys->child);
- psys->child = NULL;
- }
+ MEM_SAFE_FREE(psys->child);
psys->totchild = 0;
@@ -182,10 +179,7 @@ void psys_reset(ParticleSystem *psys, int mode)
/* reset point cache */
BKE_ptcache_invalidate(psys->pointcache);
- if (psys->fluid_springs) {
- MEM_freeN(psys->fluid_springs);
- psys->fluid_springs = NULL;
- }
+ MEM_SAFE_FREE(psys->fluid_springs);
psys->tot_fluidsprings = psys->alloc_fluidsprings = 0;
}
@@ -3112,7 +3106,7 @@ static void collision_fail(ParticleData *pa, ParticleCollision *col)
copy_v3_v3(pa->state.vel, col->pce.vel);
mul_v3_fl(pa->state.vel, col->inv_timestep);
- /* printf("max iterations\n"); */
+ // printf("max iterations\n");
}
/* Particle - Mesh collision detection and response
@@ -4516,10 +4510,7 @@ static void system_step(ParticleSimulationData *sim, float cfra, const bool use_
reset_all_particles(sim, 0.0, cfra, oldtotpart);
free_unexisting_particles(sim);
- if (psys->fluid_springs) {
- MEM_freeN(psys->fluid_springs);
- psys->fluid_springs = NULL;
- }
+ MEM_SAFE_FREE(psys->fluid_springs);
psys->tot_fluidsprings = psys->alloc_fluidsprings = 0;
@@ -4770,6 +4761,7 @@ static void particle_settings_free_local(ParticleSettings *particle_settings)
{
BKE_libblock_free_datablock(&particle_settings->id, 0);
BKE_libblock_free_data(&particle_settings->id, false);
+ BLI_assert(!particle_settings->id.py_instance); /* Or call #BKE_libblock_free_data_py. */
MEM_freeN(particle_settings);
}
diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c
index 461ffa7765e..ca1fada8c76 100644
--- a/source/blender/blenkernel/intern/pbvh.c
+++ b/source/blender/blenkernel/intern/pbvh.c
@@ -1026,7 +1026,7 @@ static void pbvh_update_normals_accum_task_cb(void *__restrict userdata,
PBVHNode *node = data->nodes[n];
float(*vnors)[3] = data->vnors;
- if ((node->flag & PBVH_UpdateNormals)) {
+ if (node->flag & PBVH_UpdateNormals) {
unsigned int mpoly_prev = UINT_MAX;
float fn[3];
diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c
index 9ed5b0230e6..57225872c7e 100644
--- a/source/blender/blenkernel/intern/pointcache.c
+++ b/source/blender/blenkernel/intern/pointcache.c
@@ -2753,18 +2753,18 @@ void BKE_ptcache_id_clear(PTCacheID *pid, int mode, unsigned int cfra)
pid->cache->flag |= PTCACHE_FLAG_INFO_DIRTY;
}
-int BKE_ptcache_id_exist(PTCacheID *pid, int cfra)
+bool BKE_ptcache_id_exist(PTCacheID *pid, int cfra)
{
if (!pid->cache) {
- return 0;
+ return false;
}
if (cfra < pid->cache->startframe || cfra > pid->cache->endframe) {
- return 0;
+ return false;
}
if (pid->cache->cached_frames && pid->cache->cached_frames[cfra - pid->cache->startframe] == 0) {
- return 0;
+ return false;
}
if (pid->cache->flag & PTCACHE_DISK_CACHE) {
@@ -2779,10 +2779,10 @@ int BKE_ptcache_id_exist(PTCacheID *pid, int cfra)
for (; pm; pm = pm->next) {
if (pm->frame == cfra) {
- return 1;
+ return true;
}
}
- return 0;
+ return false;
}
void BKE_ptcache_id_time(
PTCacheID *pid, Scene *scene, float cfra, int *startframe, int *endframe, float *timescale)
@@ -3369,7 +3369,7 @@ void BKE_ptcache_bake(PTCacheBaker *baker)
}
/* NOTE: breaking baking should leave calculated frames in cache, not clear it */
- if ((cancel || G.is_break)) {
+ if (cancel || G.is_break) {
break;
}
diff --git a/source/blender/blenkernel/intern/pointcloud.cc b/source/blender/blenkernel/intern/pointcloud.cc
index d9a7a376e2e..837a772607f 100644
--- a/source/blender/blenkernel/intern/pointcloud.cc
+++ b/source/blender/blenkernel/intern/pointcloud.cc
@@ -112,28 +112,27 @@ static void pointcloud_foreach_id(ID *id, LibraryForeachIDData *data)
static void pointcloud_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
PointCloud *pointcloud = (PointCloud *)id;
- if (pointcloud->id.us > 0 || BLO_write_is_undo(writer)) {
- CustomDataLayer *players = nullptr, players_buff[CD_TEMP_CHUNK_SIZE];
- CustomData_blend_write_prepare(
- &pointcloud->pdata, &players, players_buff, ARRAY_SIZE(players_buff));
-
- /* Write LibData */
- BLO_write_id_struct(writer, PointCloud, id_address, &pointcloud->id);
- BKE_id_blend_write(writer, &pointcloud->id);
-
- /* Direct data */
- CustomData_blend_write(
- writer, &pointcloud->pdata, players, pointcloud->totpoint, CD_MASK_ALL, &pointcloud->id);
-
- BLO_write_pointer_array(writer, pointcloud->totcol, pointcloud->mat);
- if (pointcloud->adt) {
- BKE_animdata_blend_write(writer, pointcloud->adt);
- }
- /* Remove temporary data. */
- if (players && players != players_buff) {
- MEM_freeN(players);
- }
+ CustomDataLayer *players = nullptr, players_buff[CD_TEMP_CHUNK_SIZE];
+ CustomData_blend_write_prepare(
+ &pointcloud->pdata, &players, players_buff, ARRAY_SIZE(players_buff));
+
+ /* Write LibData */
+ BLO_write_id_struct(writer, PointCloud, id_address, &pointcloud->id);
+ BKE_id_blend_write(writer, &pointcloud->id);
+
+ /* Direct data */
+ CustomData_blend_write(
+ writer, &pointcloud->pdata, players, pointcloud->totpoint, CD_MASK_ALL, &pointcloud->id);
+
+ BLO_write_pointer_array(writer, pointcloud->totcol, pointcloud->mat);
+ if (pointcloud->adt) {
+ BKE_animdata_blend_write(writer, pointcloud->adt);
+ }
+
+ /* Remove temporary data. */
+ if (players && players != players_buff) {
+ MEM_freeN(players);
}
}
diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c
index 3f75d0963c6..3fe00adc4d5 100644
--- a/source/blender/blenkernel/intern/scene.c
+++ b/source/blender/blenkernel/intern/scene.c
@@ -109,6 +109,8 @@
#include "RE_engine.h"
+#include "RNA_access.h"
+
#include "SEQ_edit.h"
#include "SEQ_iterator.h"
#include "SEQ_modifier.h"
@@ -445,7 +447,8 @@ static void scene_free_data(ID *id)
* for objects directly in the master collection? then other
* collections in the scene need to do it too? */
if (scene->master_collection) {
- BKE_collection_free(scene->master_collection);
+ BKE_collection_free_data(scene->master_collection);
+ BKE_libblock_free_data_py(&scene->master_collection->id);
MEM_freeN(scene->master_collection);
scene->master_collection = NULL;
}
@@ -2178,7 +2181,7 @@ int BKE_scene_base_iter_next(
/* exception: empty scene layer */
while ((*scene)->set) {
(*scene) = (*scene)->set;
- ViewLayer *view_layer_set = BKE_view_layer_default_render((*scene));
+ ViewLayer *view_layer_set = BKE_view_layer_default_render(*scene);
if (view_layer_set->object_bases.first) {
*base = view_layer_set->object_bases.first;
*ob = (*base)->object;
@@ -2199,7 +2202,7 @@ int BKE_scene_base_iter_next(
/* (*scene) is finished, now do the set */
while ((*scene)->set) {
(*scene) = (*scene)->set;
- ViewLayer *view_layer_set = BKE_view_layer_default_render((*scene));
+ ViewLayer *view_layer_set = BKE_view_layer_default_render(*scene);
if (view_layer_set->object_bases.first) {
*base = view_layer_set->object_bases.first;
*ob = (*base)->object;
@@ -2305,7 +2308,7 @@ Object *BKE_scene_camera_switch_find(Scene *scene)
Object *first_camera = NULL;
LISTBASE_FOREACH (TimeMarker *, m, &scene->markers) {
- if (m->camera && (m->camera->restrictflag & OB_RESTRICT_RENDER) == 0) {
+ if (m->camera && (m->camera->visibility_flag & OB_HIDE_RENDER) == 0) {
if ((m->frame <= ctime) && (m->frame > frame)) {
camera = m->camera;
frame = m->frame;
@@ -2898,7 +2901,7 @@ Base *_setlooper_base_step(Scene **sce_iter, ViewLayer *view_layer, Base *base)
next_set:
/* Reached the end, get the next base in the set. */
while ((*sce_iter = (*sce_iter)->set)) {
- ViewLayer *view_layer_set = BKE_view_layer_default_render((*sce_iter));
+ ViewLayer *view_layer_set = BKE_view_layer_default_render(*sce_iter);
base = (Base *)view_layer_set->object_bases.first;
if (base) {
@@ -2937,6 +2940,22 @@ bool BKE_scene_uses_cycles(const Scene *scene)
return STREQ(scene->r.engine, RE_engine_id_CYCLES);
}
+/* This enumeration has to match the one defined in the Cycles addon. */
+typedef enum eCyclesFeatureSet {
+ CYCLES_FEATURES_SUPPORTED = 0,
+ CYCLES_FEATURES_EXPERIMENTAL = 1,
+} eCyclesFeatureSet;
+
+/* We cannot use const as RNA_id_pointer_create is not using a const ID. */
+bool BKE_scene_uses_cycles_experimental_features(Scene *scene)
+{
+ BLI_assert(BKE_scene_uses_cycles(scene));
+ PointerRNA scene_ptr;
+ RNA_id_pointer_create(&scene->id, &scene_ptr);
+ PointerRNA cycles_ptr = RNA_pointer_get(&scene_ptr, "cycles");
+ return RNA_enum_get(&cycles_ptr, "feature_set") == CYCLES_FEATURES_EXPERIMENTAL;
+}
+
void BKE_scene_base_flag_to_objects(ViewLayer *view_layer)
{
Base *base = view_layer->object_bases.first;
@@ -3118,7 +3137,7 @@ bool BKE_scene_multiview_is_render_view_active(const RenderData *rd, const Scene
return false;
}
- if ((srv->viewflag & SCE_VIEW_DISABLE)) {
+ if (srv->viewflag & SCE_VIEW_DISABLE) {
return false;
}
diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c
index c3885b5dcf7..065240bddbc 100644
--- a/source/blender/blenkernel/intern/screen.c
+++ b/source/blender/blenkernel/intern/screen.c
@@ -255,18 +255,16 @@ static void screen_foreach_id(ID *id, LibraryForeachIDData *data)
static void screen_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
bScreen *screen = (bScreen *)id;
- /* Screens are reference counted, only saved if used by a workspace. */
- if (screen->id.us > 0 || BLO_write_is_undo(writer)) {
- /* write LibData */
- /* in 2.50+ files, the file identifier for screens is patched, forward compatibility */
- BLO_write_struct_at_address_with_filecode(writer, ID_SCRN, bScreen, id_address, screen);
- BKE_id_blend_write(writer, &screen->id);
- BKE_previewimg_blend_write(writer, screen->preview);
+ /* write LibData */
+ /* in 2.50+ files, the file identifier for screens is patched, forward compatibility */
+ BLO_write_struct_at_address_with_filecode(writer, ID_SCRN, bScreen, id_address, screen);
+ BKE_id_blend_write(writer, &screen->id);
- /* direct data */
- BKE_screen_area_map_blend_write(writer, AREAMAP_FROM_SCREEN(screen));
- }
+ BKE_previewimg_blend_write(writer, screen->preview);
+
+ /* direct data */
+ BKE_screen_area_map_blend_write(writer, AREAMAP_FROM_SCREEN(screen));
}
/* Cannot use IDTypeInfo callback yet, because of the return value. */
@@ -730,7 +728,7 @@ void BKE_screen_area_map_free(ScrAreaMap *area_map)
}
/** Free (or release) any data used by this screen (does not free the screen itself). */
-void BKE_screen_free(bScreen *screen)
+void BKE_screen_free_data(bScreen *screen)
{
screen_free_data(&screen->id);
}
@@ -766,7 +764,7 @@ void BKE_screen_remove_double_scrverts(bScreen *screen)
while (v1) {
if (v1->newv == NULL) { /* !?! */
if (v1->vec.x == verg->vec.x && v1->vec.y == verg->vec.y) {
- /* printf("doublevert\n"); */
+ // printf("doublevert\n");
v1->newv = verg;
}
}
diff --git a/source/blender/blenkernel/intern/simulation.cc b/source/blender/blenkernel/intern/simulation.cc
index 216563b860d..4c97ccdf8b1 100644
--- a/source/blender/blenkernel/intern/simulation.cc
+++ b/source/blender/blenkernel/intern/simulation.cc
@@ -49,14 +49,10 @@
#include "BKE_simulation.h"
#include "NOD_geometry.h"
-#include "NOD_node_tree_multi_function.hh"
#include "BLI_map.hh"
#include "BLT_translation.h"
-#include "FN_multi_function_network_evaluation.hh"
-#include "FN_multi_function_network_optimization.hh"
-
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
@@ -114,19 +110,18 @@ static void simulation_foreach_id(ID *id, LibraryForeachIDData *data)
static void simulation_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
Simulation *simulation = (Simulation *)id;
- if (simulation->id.us > 0 || BLO_write_is_undo(writer)) {
- BLO_write_id_struct(writer, Simulation, id_address, &simulation->id);
- BKE_id_blend_write(writer, &simulation->id);
-
- if (simulation->adt) {
- BKE_animdata_blend_write(writer, simulation->adt);
- }
-
- /* nodetree is integral part of simulation, no libdata */
- if (simulation->nodetree) {
- BLO_write_struct(writer, bNodeTree, simulation->nodetree);
- ntreeBlendWrite(writer, simulation->nodetree);
- }
+
+ BLO_write_id_struct(writer, Simulation, id_address, &simulation->id);
+ BKE_id_blend_write(writer, &simulation->id);
+
+ if (simulation->adt) {
+ BKE_animdata_blend_write(writer, simulation->adt);
+ }
+
+ /* nodetree is integral part of simulation, no libdata */
+ if (simulation->nodetree) {
+ BLO_write_struct(writer, bNodeTree, simulation->nodetree);
+ ntreeBlendWrite(writer, simulation->nodetree);
}
}
diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c
index e4e2ed94b41..fbc781f5eb9 100644
--- a/source/blender/blenkernel/intern/softbody.c
+++ b/source/blender/blenkernel/intern/softbody.c
@@ -891,11 +891,7 @@ static void free_softbody_baked(SoftBody *sb)
MEM_freeN(key);
}
}
- if (sb->keys) {
- MEM_freeN(sb->keys);
- }
-
- sb->keys = NULL;
+ MEM_SAFE_FREE(sb->keys);
sb->totkey = 0;
}
static void free_scratch(SoftBody *sb)
@@ -2225,7 +2221,7 @@ static void sb_cf_threads_run(Scene *scene,
totthread--;
}
- /* printf("sb_cf_threads_run spawning %d threads\n", totthread); */
+ // printf("sb_cf_threads_run spawning %d threads\n", totthread);
sb_threads = MEM_callocN(sizeof(SB_thread_context) * totthread, "SBThread");
memset(sb_threads, 0, sizeof(SB_thread_context) * totthread);
@@ -2281,7 +2277,7 @@ static void softbody_calc_forces(
float fieldfactor = -1.0f, windfactor = 0.25;
int do_deflector /*, do_selfcollision */, do_springcollision, do_aero;
- /* gravity = sb->grav * sb_grav_force_scale(ob); */ /* UNUSED */
+ // gravity = sb->grav * sb_grav_force_scale(ob); /* UNUSED */
/* check conditions for various options */
do_deflector = query_external_colliders(depsgraph, sb->collision_group);
@@ -2753,7 +2749,7 @@ static void mesh_to_softbody(Object *ob)
build_bps_springlist(ob); /* scan for springs attached to bodypoints ONCE */
/* insert *other second order* springs if desired */
if (sb->secondspring > 0.0000001f) {
- /* exploits the first run of build_bps_springlist(ob); */
+ /* Exploits the first run of `build_bps_springlist(ob)`. */
add_2nd_order_springs(ob, sb->secondspring);
/* yes we need to do it again. */
build_bps_springlist(ob);
@@ -2812,7 +2808,7 @@ static void reference_to_scratch(Object *ob)
}
mul_v3_fl(accu_pos, 1.0f / accu_mass);
copy_v3_v3(sb->scratch->Ref.com, accu_pos);
- /* printf("reference_to_scratch\n"); */
+ // printf("reference_to_scratch\n");
}
/*
@@ -3447,10 +3443,10 @@ static void softbody_step(
float newtime = forcetime * 1.1f; /* hope for 1.1 times better conditions in next step */
if (sb->scratch->flag & SBF_DOFUZZY) {
- ///* stay with this stepsize unless err really small */
+ // /* stay with this stepsize unless err really small */
// if (err > SoftHeunTol/(2.0f*sb->fuzzyness)) {
newtime = forcetime;
- //}
+ // }
}
else {
if (err > SoftHeunTol / 2.0f) { /* stay with this stepsize unless err really small */
diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c
index fcb992e1535..c61fa793367 100644
--- a/source/blender/blenkernel/intern/sound.c
+++ b/source/blender/blenkernel/intern/sound.c
@@ -137,24 +137,23 @@ static void sound_blend_write(BlendWriter *writer, ID *id, const void *id_addres
{
bSound *sound = (bSound *)id;
const bool is_undo = BLO_write_is_undo(writer);
- if (sound->id.us > 0 || is_undo) {
- /* Clean up, important in undo case to reduce false detection of changed datablocks. */
- sound->tags = 0;
- sound->handle = NULL;
- sound->playback_handle = NULL;
- sound->spinlock = NULL;
- /* Do not store packed files in case this is a library override ID. */
- if (ID_IS_OVERRIDE_LIBRARY(sound) && !is_undo) {
- sound->packedfile = NULL;
- }
-
- /* write LibData */
- BLO_write_id_struct(writer, bSound, id_address, &sound->id);
- BKE_id_blend_write(writer, &sound->id);
+ /* Clean up, important in undo case to reduce false detection of changed datablocks. */
+ sound->tags = 0;
+ sound->handle = NULL;
+ sound->playback_handle = NULL;
+ sound->spinlock = NULL;
- BKE_packedfile_blend_write(writer, sound->packedfile);
+ /* Do not store packed files in case this is a library override ID. */
+ if (ID_IS_OVERRIDE_LIBRARY(sound) && !is_undo) {
+ sound->packedfile = NULL;
}
+
+ /* write LibData */
+ BLO_write_id_struct(writer, bSound, id_address, &sound->id);
+ BKE_id_blend_write(writer, &sound->id);
+
+ BKE_packedfile_blend_write(writer, sound->packedfile);
}
static void sound_blend_read_data(BlendDataReader *reader, ID *id)
@@ -703,13 +702,13 @@ void *BKE_sound_scene_add_scene_sound(
Scene *scene, Sequence *sequence, int startframe, int endframe, int frameskip)
{
sound_verify_evaluated_id(&scene->id);
- if (sequence->scene && scene != sequence->scene) {
+ if (sequence->scene && scene != sequence->scene && sequence->sound) {
const double fps = FPS;
return AUD_Sequence_add(scene->sound_scene,
sequence->scene->sound_scene,
startframe / fps,
endframe / fps,
- frameskip / fps);
+ frameskip / fps + sequence->sound->offset_time);
}
return NULL;
}
@@ -737,7 +736,7 @@ void *BKE_sound_add_scene_sound(
sequence->sound->playback_handle,
startframe / fps,
endframe / fps,
- frameskip / fps);
+ frameskip / fps + sequence->sound->offset_time);
AUD_SequenceEntry_setMuted(handle, (sequence->flag & SEQ_MUTE) != 0);
AUD_SequenceEntry_setAnimationData(handle, AUD_AP_VOLUME, CFRA, &sequence->volume, 0);
AUD_SequenceEntry_setAnimationData(handle, AUD_AP_PITCH, CFRA, &sequence->pitch, 0);
@@ -765,22 +764,23 @@ void BKE_sound_mute_scene_sound(void *handle, char mute)
}
void BKE_sound_move_scene_sound(
- Scene *scene, void *handle, int startframe, int endframe, int frameskip)
+ Scene *scene, void *handle, int startframe, int endframe, int frameskip, double audio_offset)
{
sound_verify_evaluated_id(&scene->id);
const double fps = FPS;
- AUD_SequenceEntry_move(handle, startframe / fps, endframe / fps, frameskip / fps);
+ AUD_SequenceEntry_move(handle, startframe / fps, endframe / fps, frameskip / fps + audio_offset);
}
void BKE_sound_move_scene_sound_defaults(Scene *scene, Sequence *sequence)
{
sound_verify_evaluated_id(&scene->id);
- if (sequence->scene_sound) {
+ if (sequence->scene_sound && sequence->sound) {
BKE_sound_move_scene_sound(scene,
sequence->scene_sound,
sequence->startdisp,
sequence->enddisp,
- sequence->startofs + sequence->anim_startofs);
+ sequence->startofs + sequence->anim_startofs,
+ sequence->sound->offset_time);
}
}
@@ -1213,6 +1213,7 @@ static bool sound_info_from_playback_handle(void *playback_handle, SoundInfo *so
AUD_SoundInfo info = AUD_getInfo(playback_handle);
sound_info->specs.channels = (eSoundChannels)info.specs.channels;
sound_info->length = info.length;
+ sound_info->start_offset = info.start_offset;
return true;
}
@@ -1310,7 +1311,8 @@ void BKE_sound_move_scene_sound(Scene *UNUSED(scene),
void *UNUSED(handle),
int UNUSED(startframe),
int UNUSED(endframe),
- int UNUSED(frameskip))
+ int UNUSED(frameskip),
+ double UNUSED(audio_offset))
{
}
void BKE_sound_move_scene_sound_defaults(Scene *UNUSED(scene), Sequence *UNUSED(sequence))
diff --git a/source/blender/blenkernel/intern/speaker.c b/source/blender/blenkernel/intern/speaker.c
index af9b2268879..4b10522c375 100644
--- a/source/blender/blenkernel/intern/speaker.c
+++ b/source/blender/blenkernel/intern/speaker.c
@@ -56,14 +56,13 @@ static void speaker_foreach_id(ID *id, LibraryForeachIDData *data)
static void speaker_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
Speaker *spk = (Speaker *)id;
- if (spk->id.us > 0 || BLO_write_is_undo(writer)) {
- /* write LibData */
- BLO_write_id_struct(writer, Speaker, id_address, &spk->id);
- BKE_id_blend_write(writer, &spk->id);
-
- if (spk->adt) {
- BKE_animdata_blend_write(writer, spk->adt);
- }
+
+ /* write LibData */
+ BLO_write_id_struct(writer, Speaker, id_address, &spk->id);
+ BKE_id_blend_write(writer, &spk->id);
+
+ if (spk->adt) {
+ BKE_animdata_blend_write(writer, spk->adt);
}
}
diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc
index a7caae967f6..dda7abea0fc 100644
--- a/source/blender/blenkernel/intern/spline_base.cc
+++ b/source/blender/blenkernel/intern/spline_base.cc
@@ -189,7 +189,11 @@ static float3 direction_bisect(const float3 &prev, const float3 &middle, const f
const float3 dir_prev = (middle - prev).normalized();
const float3 dir_next = (next - middle).normalized();
- return (dir_prev + dir_next).normalized();
+ const float3 result = (dir_prev + dir_next).normalized();
+ if (UNLIKELY(result.is_zero())) {
+ return float3(0.0f, 0.0f, 1.0f);
+ }
+ return result;
}
static void calculate_tangents(Span<float3> positions,
@@ -197,6 +201,7 @@ static void calculate_tangents(Span<float3> positions,
MutableSpan<float3> tangents)
{
if (positions.size() == 1) {
+ tangents.first() = float3(0.0f, 0.0f, 1.0f);
return;
}
@@ -237,13 +242,8 @@ Span<float3> Spline::evaluated_tangents() const
Span<float3> positions = this->evaluated_positions();
- if (eval_size == 1) {
- evaluated_tangents_cache_.first() = float3(1.0f, 0.0f, 0.0f);
- }
- else {
- calculate_tangents(positions, is_cyclic_, evaluated_tangents_cache_);
- this->correct_end_tangents();
- }
+ calculate_tangents(positions, is_cyclic_, evaluated_tangents_cache_);
+ this->correct_end_tangents();
tangent_cache_dirty_ = false;
return evaluated_tangents_cache_;
diff --git a/source/blender/blenkernel/intern/studiolight.c b/source/blender/blenkernel/intern/studiolight.c
index dc5162f201e..95436372a65 100644
--- a/source/blender/blenkernel/intern/studiolight.c
+++ b/source/blender/blenkernel/intern/studiolight.c
@@ -475,7 +475,7 @@ static void studiolight_load_equirect_image(StudioLight *sl)
NULL, (failed || (specular_ibuf == NULL)) ? magenta : black, 1, 1, 4);
}
- if ((sl->flag & STUDIOLIGHT_TYPE_MATCAP)) {
+ if (sl->flag & STUDIOLIGHT_TYPE_MATCAP) {
sl->matcap_diffuse.ibuf = diffuse_ibuf;
sl->matcap_specular.ibuf = specular_ibuf;
if (specular_ibuf != NULL) {
@@ -1192,7 +1192,7 @@ static void studiolight_add_files_from_datafolder(const int folder_id,
uint totfile = BLI_filelist_dir_contents(folder, &dir);
int i;
for (i = 0; i < totfile; i++) {
- if ((dir[i].type & S_IFREG)) {
+ if (dir[i].type & S_IFREG) {
studiolight_add_file(dir[i].path, flag);
}
}
@@ -1473,7 +1473,7 @@ struct StudioLight *BKE_studiolight_find_default(int flag)
}
LISTBASE_FOREACH (StudioLight *, sl, &studiolights) {
- if ((sl->flag & flag)) {
+ if (sl->flag & flag) {
return sl;
}
}
@@ -1484,7 +1484,7 @@ struct StudioLight *BKE_studiolight_find(const char *name, int flag)
{
LISTBASE_FOREACH (StudioLight *, sl, &studiolights) {
if (STREQLEN(sl->name, name, FILE_MAXFILE)) {
- if ((sl->flag & flag)) {
+ if (sl->flag & flag) {
return sl;
}
@@ -1542,32 +1542,32 @@ void BKE_studiolight_ensure_flag(StudioLight *sl, int flag)
return;
}
- if ((flag & STUDIOLIGHT_EXTERNAL_IMAGE_LOADED)) {
+ if (flag & STUDIOLIGHT_EXTERNAL_IMAGE_LOADED) {
studiolight_load_equirect_image(sl);
}
- if ((flag & STUDIOLIGHT_RADIANCE_BUFFERS_CALCULATED)) {
+ if (flag & STUDIOLIGHT_RADIANCE_BUFFERS_CALCULATED) {
studiolight_calculate_radiance_cubemap_buffers(sl);
}
- if ((flag & STUDIOLIGHT_SPHERICAL_HARMONICS_COEFFICIENTS_CALCULATED)) {
+ if (flag & STUDIOLIGHT_SPHERICAL_HARMONICS_COEFFICIENTS_CALCULATED) {
if (!studiolight_load_spherical_harmonics_coefficients(sl)) {
studiolight_calculate_diffuse_light(sl);
}
}
- if ((flag & STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE)) {
+ if (flag & STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE) {
studiolight_create_equirect_radiance_gputexture(sl);
}
- if ((flag & STUDIOLIGHT_EQUIRECT_IRRADIANCE_GPUTEXTURE)) {
+ if (flag & STUDIOLIGHT_EQUIRECT_IRRADIANCE_GPUTEXTURE) {
studiolight_create_equirect_irradiance_gputexture(sl);
}
- if ((flag & STUDIOLIGHT_EQUIRECT_IRRADIANCE_IMAGE_CALCULATED)) {
+ if (flag & STUDIOLIGHT_EQUIRECT_IRRADIANCE_IMAGE_CALCULATED) {
if (!studiolight_load_irradiance_equirect_image(sl)) {
studiolight_calculate_irradiance_equirect_image(sl);
}
}
- if ((flag & STUDIOLIGHT_MATCAP_DIFFUSE_GPUTEXTURE)) {
+ if (flag & STUDIOLIGHT_MATCAP_DIFFUSE_GPUTEXTURE) {
studiolight_create_matcap_diffuse_gputexture(sl);
}
- if ((flag & STUDIOLIGHT_MATCAP_SPECULAR_GPUTEXTURE)) {
+ if (flag & STUDIOLIGHT_MATCAP_SPECULAR_GPUTEXTURE) {
studiolight_create_matcap_specular_gputexture(sl);
}
}
diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c
index 80ff8ce9162..275cf0d4c38 100644
--- a/source/blender/blenkernel/intern/text.c
+++ b/source/blender/blenkernel/intern/text.c
@@ -171,9 +171,6 @@ static void text_free_data(ID *id)
static void text_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
- if (id->us < 1 && !BLO_write_is_undo(writer)) {
- return;
- }
Text *text = (Text *)id;
/* NOTE: we are clearing local temp data here, *not* the flag in the actual 'real' ID. */
@@ -2331,7 +2328,7 @@ int txt_setcurr_tab_spaces(Text *text, int space)
}
while (text->curl->line[i] == indent) {
- // we only count those tabs/spaces that are before any text or before the curs;
+ /* We only count those tabs/spaces that are before any text or before the curs; */
if (i == text->curc) {
return i;
}
diff --git a/source/blender/blenkernel/intern/text_suggestions.c b/source/blender/blenkernel/intern/text_suggestions.c
index d717b88e8ca..e93e969cb33 100644
--- a/source/blender/blenkernel/intern/text_suggestions.c
+++ b/source/blender/blenkernel/intern/text_suggestions.c
@@ -57,10 +57,7 @@ static void txttl_free_suggest(void)
static void txttl_free_docs(void)
{
- if (documentation) {
- MEM_freeN(documentation);
- documentation = NULL;
- }
+ MEM_SAFE_FREE(documentation);
}
/**************************/
@@ -240,10 +237,7 @@ void texttool_docs_show(const char *docs)
len = strlen(docs);
- if (documentation) {
- MEM_freeN(documentation);
- documentation = NULL;
- }
+ MEM_SAFE_FREE(documentation);
/* Ensure documentation ends with a '\n' */
if (docs[len - 1] != '\n') {
diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c
index 29c41b88135..228e6fffdf7 100644
--- a/source/blender/blenkernel/intern/texture.c
+++ b/source/blender/blenkernel/intern/texture.c
@@ -150,28 +150,27 @@ static void texture_foreach_id(ID *id, LibraryForeachIDData *data)
static void texture_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
Tex *tex = (Tex *)id;
- if (tex->id.us > 0 || BLO_write_is_undo(writer)) {
- /* write LibData */
- BLO_write_id_struct(writer, Tex, id_address, &tex->id);
- BKE_id_blend_write(writer, &tex->id);
- if (tex->adt) {
- BKE_animdata_blend_write(writer, tex->adt);
- }
+ /* write LibData */
+ BLO_write_id_struct(writer, Tex, id_address, &tex->id);
+ BKE_id_blend_write(writer, &tex->id);
- /* direct data */
- if (tex->coba) {
- BLO_write_struct(writer, ColorBand, tex->coba);
- }
+ if (tex->adt) {
+ BKE_animdata_blend_write(writer, tex->adt);
+ }
- /* nodetree is integral part of texture, no libdata */
- if (tex->nodetree) {
- BLO_write_struct(writer, bNodeTree, tex->nodetree);
- ntreeBlendWrite(writer, tex->nodetree);
- }
+ /* direct data */
+ if (tex->coba) {
+ BLO_write_struct(writer, ColorBand, tex->coba);
+ }
- BKE_previewimg_blend_write(writer, tex->preview);
+ /* nodetree is integral part of texture, no libdata */
+ if (tex->nodetree) {
+ BLO_write_struct(writer, bNodeTree, tex->nodetree);
+ ntreeBlendWrite(writer, tex->nodetree);
}
+
+ BKE_previewimg_blend_write(writer, tex->preview);
}
static void texture_blend_read_data(BlendDataReader *reader, ID *id)
@@ -490,9 +489,8 @@ void set_current_linestyle_texture(FreestyleLineStyle *linestyle, Tex *newtex)
linestyle->mtex[act]->tex = newtex;
id_us_plus(&newtex->id);
}
- else if (linestyle->mtex[act]) {
- MEM_freeN(linestyle->mtex[act]);
- linestyle->mtex[act] = NULL;
+ else {
+ MEM_SAFE_FREE(linestyle->mtex[act]);
}
}
@@ -595,9 +593,8 @@ void set_current_particle_texture(ParticleSettings *part, Tex *newtex)
part->mtex[act]->tex = newtex;
id_us_plus(&newtex->id);
}
- else if (part->mtex[act]) {
- MEM_freeN(part->mtex[act]);
- part->mtex[act] = NULL;
+ else {
+ MEM_SAFE_FREE(part->mtex[act]);
}
}
@@ -660,14 +657,8 @@ void BKE_texture_pointdensity_free_data(PointDensity *pd)
BLI_bvhtree_free(pd->point_tree);
pd->point_tree = NULL;
}
- if (pd->point_data) {
- MEM_freeN(pd->point_data);
- pd->point_data = NULL;
- }
- if (pd->coba) {
- MEM_freeN(pd->coba);
- pd->coba = NULL;
- }
+ MEM_SAFE_FREE(pd->point_data);
+ MEM_SAFE_FREE(pd->coba);
BKE_curvemapping_free(pd->falloff_curve); /* can be NULL */
}
diff --git a/source/blender/blenkernel/intern/tracking_auto.c b/source/blender/blenkernel/intern/tracking_auto.c
index f107fc4d6bc..92ff0911cf3 100644
--- a/source/blender/blenkernel/intern/tracking_auto.c
+++ b/source/blender/blenkernel/intern/tracking_auto.c
@@ -322,7 +322,7 @@ static bool tracking_check_marker_margin(const libmv_Marker *libmv_marker,
static bool autotrack_is_marker_usable(const MovieTrackingMarker *marker)
{
- if ((marker->flag & MARKER_DISABLED)) {
+ if (marker->flag & MARKER_DISABLED) {
return false;
}
return true;
@@ -797,7 +797,7 @@ void BKE_autotrack_context_finish(AutoTrackContext *context)
clip, context->start_scene_frame);
LISTBASE_FOREACH (MovieTrackingPlaneTrack *, plane_track, plane_tracks_base) {
- if ((plane_track->flag & PLANE_TRACK_AUTOKEY)) {
+ if (plane_track->flag & PLANE_TRACK_AUTOKEY) {
continue;
}
for (int track_index = 0; track_index < context->num_all_tracks; track_index++) {
diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc
index b28d17df814..69452d6896f 100644
--- a/source/blender/blenkernel/intern/volume.cc
+++ b/source/blender/blenkernel/intern/volume.cc
@@ -578,27 +578,26 @@ static void volume_blend_write(BlendWriter *writer, ID *id, const void *id_addre
{
Volume *volume = (Volume *)id;
const bool is_undo = BLO_write_is_undo(writer);
- if (volume->id.us > 0 || is_undo) {
- /* Clean up, important in undo case to reduce false detection of changed datablocks. */
- volume->runtime.grids = nullptr;
- /* Do not store packed files in case this is a library override ID. */
- if (ID_IS_OVERRIDE_LIBRARY(volume) && !is_undo) {
- volume->packedfile = nullptr;
- }
+ /* Clean up, important in undo case to reduce false detection of changed datablocks. */
+ volume->runtime.grids = nullptr;
- /* write LibData */
- BLO_write_id_struct(writer, Volume, id_address, &volume->id);
- BKE_id_blend_write(writer, &volume->id);
+ /* Do not store packed files in case this is a library override ID. */
+ if (ID_IS_OVERRIDE_LIBRARY(volume) && !is_undo) {
+ volume->packedfile = nullptr;
+ }
- /* direct data */
- BLO_write_pointer_array(writer, volume->totcol, volume->mat);
- if (volume->adt) {
- BKE_animdata_blend_write(writer, volume->adt);
- }
+ /* write LibData */
+ BLO_write_id_struct(writer, Volume, id_address, &volume->id);
+ BKE_id_blend_write(writer, &volume->id);
- BKE_packedfile_blend_write(writer, volume->packedfile);
+ /* direct data */
+ BLO_write_pointer_array(writer, volume->totcol, volume->mat);
+ if (volume->adt) {
+ BKE_animdata_blend_write(writer, volume->adt);
}
+
+ BKE_packedfile_blend_write(writer, volume->packedfile);
}
static void volume_blend_read_data(BlendDataReader *reader, ID *id)
diff --git a/source/blender/blenkernel/intern/volume_to_mesh.cc b/source/blender/blenkernel/intern/volume_to_mesh.cc
index 7ab67516242..e9d6eea4614 100644
--- a/source/blender/blenkernel/intern/volume_to_mesh.cc
+++ b/source/blender/blenkernel/intern/volume_to_mesh.cc
@@ -159,7 +159,7 @@ static Mesh *new_mesh_from_openvdb_data(Span<openvdb::Vec3s> verts,
}
BKE_mesh_calc_edges(mesh, false, false);
- BKE_mesh_calc_normals(mesh);
+ BKE_mesh_normals_tag_dirty(mesh);
return mesh;
}
diff --git a/source/blender/blenkernel/intern/workspace.c b/source/blender/blenkernel/intern/workspace.c
index 059dc68b1dc..329633c6759 100644
--- a/source/blender/blenkernel/intern/workspace.c
+++ b/source/blender/blenkernel/intern/workspace.c
@@ -58,7 +58,7 @@ static void workspace_init_data(ID *id)
{
WorkSpace *workspace = (WorkSpace *)id;
- BKE_asset_library_reference_init_default(&workspace->asset_library);
+ BKE_asset_library_reference_init_default(&workspace->asset_library_ref);
}
static void workspace_free_data(ID *id)
diff --git a/source/blender/blenkernel/intern/world.c b/source/blender/blenkernel/intern/world.c
index e889d8af1d5..4abe1ff0f20 100644
--- a/source/blender/blenkernel/intern/world.c
+++ b/source/blender/blenkernel/intern/world.c
@@ -138,26 +138,25 @@ static void world_foreach_id(ID *id, LibraryForeachIDData *data)
static void world_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
World *wrld = (World *)id;
- if (wrld->id.us > 0 || BLO_write_is_undo(writer)) {
- /* Clean up, important in undo case to reduce false detection of changed datablocks. */
- BLI_listbase_clear(&wrld->gpumaterial);
- /* write LibData */
- BLO_write_id_struct(writer, World, id_address, &wrld->id);
- BKE_id_blend_write(writer, &wrld->id);
+ /* Clean up, important in undo case to reduce false detection of changed datablocks. */
+ BLI_listbase_clear(&wrld->gpumaterial);
- if (wrld->adt) {
- BKE_animdata_blend_write(writer, wrld->adt);
- }
+ /* write LibData */
+ BLO_write_id_struct(writer, World, id_address, &wrld->id);
+ BKE_id_blend_write(writer, &wrld->id);
- /* nodetree is integral part of world, no libdata */
- if (wrld->nodetree) {
- BLO_write_struct(writer, bNodeTree, wrld->nodetree);
- ntreeBlendWrite(writer, wrld->nodetree);
- }
+ if (wrld->adt) {
+ BKE_animdata_blend_write(writer, wrld->adt);
+ }
- BKE_previewimg_blend_write(writer, wrld->preview);
+ /* nodetree is integral part of world, no libdata */
+ if (wrld->nodetree) {
+ BLO_write_struct(writer, bNodeTree, wrld->nodetree);
+ ntreeBlendWrite(writer, wrld->nodetree);
}
+
+ BKE_previewimg_blend_write(writer, wrld->preview);
}
static void world_blend_read_data(BlendDataReader *reader, ID *id)
diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c
index 9b3103a638b..323da7473b5 100644
--- a/source/blender/blenkernel/intern/writeffmpeg.c
+++ b/source/blender/blenkernel/intern/writeffmpeg.c
@@ -149,7 +149,6 @@ static int write_audio_frame(FFMpegContext *context)
AUD_Device_read(
context->audio_mixdown_device, context->audio_input_buffer, context->audio_input_samples);
- context->audio_time += (double)context->audio_input_samples / (double)c->sample_rate;
frame = av_frame_alloc();
frame->pts = context->audio_time / av_q2d(c->time_base);
@@ -184,7 +183,7 @@ static int write_audio_frame(FFMpegContext *context)
context->audio_input_samples * c->channels * context->audio_sample_size,
1);
- int success = 0;
+ int success = 1;
int ret = avcodec_send_frame(c, frame);
if (ret < 0) {
@@ -369,7 +368,7 @@ static int write_video_frame(FFMpegContext *context, int cfra, AVFrame *frame, R
return success;
}
-/* read and encode a frame of audio from the buffer */
+/* read and encode a frame of video from the buffer */
static AVFrame *generate_video_frame(FFMpegContext *context, const uint8_t *pixels)
{
AVCodecParameters *codec = context->video_stream->codecpar;
@@ -741,7 +740,7 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
}
}
- if ((of->oformat->flags & AVFMT_GLOBALHEADER)) {
+ if (of->oformat->flags & AVFMT_GLOBALHEADER) {
PRINT("Using global header\n");
c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
@@ -1226,9 +1225,8 @@ fail:
* parameter.
* </p>
*/
-static void flush_ffmpeg(FFMpegContext *context)
+static void flush_ffmpeg(AVCodecContext *c, AVStream *stream, AVFormatContext *outfile)
{
- AVCodecContext *c = context->video_codec;
AVPacket *packet = av_packet_alloc();
avcodec_send_frame(c, NULL);
@@ -1247,13 +1245,13 @@ static void flush_ffmpeg(FFMpegContext *context)
break;
}
- packet->stream_index = context->video_stream->index;
- av_packet_rescale_ts(packet, c->time_base, context->video_stream->time_base);
+ packet->stream_index = stream->index;
+ av_packet_rescale_ts(packet, c->time_base, stream->time_base);
# ifdef FFMPEG_USE_DURATION_WORKAROUND
- my_guess_pkt_duration(context->outfile, context->video_stream, packet);
+ my_guess_pkt_duration(context->outfile, stream, packet);
# endif
- int write_ret = av_interleaved_write_frame(context->outfile, packet);
+ int write_ret = av_interleaved_write_frame(outfile, packet);
if (write_ret != 0) {
fprintf(stderr, "Error writing delayed frame: %s\n", av_err2str(write_ret));
break;
@@ -1396,12 +1394,13 @@ static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit);
# ifdef WITH_AUDASPACE
static void write_audio_frames(FFMpegContext *context, double to_pts)
{
- int finished = 0;
+ AVCodecContext *c = context->audio_codec;
- while (context->audio_stream && !finished) {
- if ((context->audio_time >= to_pts) || (write_audio_frame(context))) {
- finished = 1;
+ while (context->audio_stream) {
+ if ((context->audio_time >= to_pts) || !write_audio_frame(context)) {
+ break;
}
+ context->audio_time += (double)context->audio_input_samples / (double)c->sample_rate;
}
}
# endif
@@ -1422,9 +1421,6 @@ int BKE_ffmpeg_append(void *context_v,
PRINT("Writing frame %i, render width=%d, render height=%d\n", frame, rectx, recty);
- /* why is this done before writing the video frame and again at end_ffmpeg? */
- // write_audio_frames(frame / (((double)rd->frs_sec) / rd->frs_sec_base));
-
if (context->video_stream) {
avframe = generate_video_frame(context, (unsigned char *)pixels);
success = (avframe && write_video_frame(context, frame - start_frame, avframe, reports));
@@ -1439,8 +1435,9 @@ int BKE_ffmpeg_append(void *context_v,
}
# ifdef WITH_AUDASPACE
- write_audio_frames(context,
- (frame - start_frame) / (((double)rd->frs_sec) / (double)rd->frs_sec_base));
+ /* Add +1 frame because we want to encode audio up until the next video frame. */
+ write_audio_frames(
+ context, (frame - start_frame + 1) / (((double)rd->frs_sec) / (double)rd->frs_sec_base));
# endif
return success;
}
@@ -1461,8 +1458,13 @@ static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit)
# endif
if (context->video_stream) {
- PRINT("Flushing delayed frames...\n");
- flush_ffmpeg(context);
+ PRINT("Flushing delayed video frames...\n");
+ flush_ffmpeg(context->video_codec, context->video_stream, context->outfile);
+ }
+
+ if (context->audio_stream) {
+ PRINT("Flushing delayed audio frames...\n");
+ flush_ffmpeg(context->audio_codec, context->audio_stream, context->outfile);
}
if (context->outfile) {
diff --git a/source/blender/blenlib/BLI_array.h b/source/blender/blenlib/BLI_array.h
index 6bf29a6168f..084f573e8c7 100644
--- a/source/blender/blenlib/BLI_array.h
+++ b/source/blender/blenlib/BLI_array.h
@@ -156,7 +156,7 @@ void _bli_array_grow_func(void **arr_p,
* \{ */
/**
- * not part of the 'API' but handy funcs,
+ * Not part of the 'API' but handy functions,
* same purpose as #BLI_array_staticdeclare()
* but use when the max size is known ahead of time */
#define BLI_array_fixedstack_declare(arr, maxstatic, realsize, allocstr) \
diff --git a/source/blender/blenlib/BLI_enumerable_thread_specific.hh b/source/blender/blenlib/BLI_enumerable_thread_specific.hh
index 3051d980d45..b5981028893 100644
--- a/source/blender/blenlib/BLI_enumerable_thread_specific.hh
+++ b/source/blender/blenlib/BLI_enumerable_thread_specific.hh
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
+ * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
diff --git a/source/blender/blenlib/BLI_fileops.h b/source/blender/blenlib/BLI_fileops.h
index 7cfecc798a7..906a56ce909 100644
--- a/source/blender/blenlib/BLI_fileops.h
+++ b/source/blender/blenlib/BLI_fileops.h
@@ -154,18 +154,17 @@ bool BLI_file_is_writable(const char *file) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL
bool BLI_file_touch(const char *file) ATTR_NONNULL();
bool BLI_file_alias_target(const char *filepath, char *r_targetpath) ATTR_WARN_UNUSED_RESULT;
-#if 0 /* UNUSED */
-int BLI_file_gzip(const char *from, const char *to) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
-#endif
-char *BLI_file_ungzip_to_mem(const char *from_file, int *r_size) ATTR_WARN_UNUSED_RESULT
- ATTR_NONNULL();
-size_t BLI_gzip_mem_to_file_at_pos(void *buf,
- size_t len,
- FILE *file,
- size_t gz_stream_offset,
- int compression_level) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
-size_t BLI_ungzip_file_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t gz_stream_offset)
+bool BLI_file_magic_is_gzip(const char header[4]);
+
+size_t BLI_file_zstd_from_mem_at_pos(void *buf,
+ size_t len,
+ FILE *file,
+ size_t file_offset,
+ int compression_level) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
+size_t BLI_file_unzstd_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t file_offset)
ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
+bool BLI_file_magic_is_zstd(const char header[4]);
+
size_t BLI_file_descriptor_size(int file) ATTR_WARN_UNUSED_RESULT;
size_t BLI_file_size(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
diff --git a/source/blender/blenlib/BLI_filereader.h b/source/blender/blenlib/BLI_filereader.h
new file mode 100644
index 00000000000..8d1fa3d1596
--- /dev/null
+++ b/source/blender/blenlib/BLI_filereader.h
@@ -0,0 +1,81 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bli
+ * \brief Wrapper for reading from various sources (e.g. raw files, compressed files, memory...).
+ */
+
+#pragma once
+
+#ifdef WIN32
+# include "BLI_winstuff.h"
+#else
+# include <sys/types.h>
+#endif
+
+#include "BLI_compiler_attrs.h"
+#include "BLI_utildefines.h"
+
+#if defined(_MSC_VER) || defined(__APPLE__) || defined(__HAIKU__) || defined(__NetBSD__)
+typedef int64_t off64_t;
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct FileReader;
+
+typedef ssize_t (*FileReaderReadFn)(struct FileReader *reader, void *buffer, size_t size);
+typedef off64_t (*FileReaderSeekFn)(struct FileReader *reader, off64_t offset, int whence);
+typedef void (*FileReaderCloseFn)(struct FileReader *reader);
+
+/* General structure for all FileReaders, implementations add custom fields at the end. */
+typedef struct FileReader {
+ FileReaderReadFn read;
+ FileReaderSeekFn seek;
+ FileReaderCloseFn close;
+
+ off64_t offset;
+} FileReader;
+
+/* Functions for opening the various types of FileReader.
+ * They either succeed and return a valid FileReader, or fail and return NULL.
+ *
+ * If a FileReader is created, it has to be cleaned up and freed by calling
+ * its close() function unless another FileReader has taken ownership - for example,
+ * Zstd and Gzip take over the base FileReader and will clean it up when their clean() is called.
+ */
+
+/* Create FileReader from raw file descriptor. */
+FileReader *BLI_filereader_new_file(int filedes) ATTR_WARN_UNUSED_RESULT;
+/* Create FileReader from raw file descriptor using memory-mapped IO. */
+FileReader *BLI_filereader_new_mmap(int filedes) ATTR_WARN_UNUSED_RESULT;
+/* Create FileReader from a region of memory. */
+FileReader *BLI_filereader_new_memory(const void *data, size_t len) ATTR_WARN_UNUSED_RESULT
+ ATTR_NONNULL();
+/* Create FileReader from applying Zstd decompression on an underlying file. */
+FileReader *BLI_filereader_new_zstd(FileReader *base) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
+/* Create FileReader from applying Gzip decompression on an underlying file. */
+FileReader *BLI_filereader_new_gzip(FileReader *base) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/blenlib/BLI_index_mask.hh b/source/blender/blenlib/BLI_index_mask.hh
index f04c0e9c80a..7a3169520ca 100644
--- a/source/blender/blenlib/BLI_index_mask.hh
+++ b/source/blender/blenlib/BLI_index_mask.hh
@@ -58,11 +58,7 @@ class IndexMask {
*/
IndexMask(Span<int64_t> indices) : indices_(indices)
{
-#ifdef DEBUG
- for (int64_t i = 1; i < indices.size(); i++) {
- BLI_assert(indices[i - 1] < indices[i]);
- }
-#endif
+ BLI_assert(IndexMask::indices_are_valid_index_mask(indices));
}
/**
@@ -94,6 +90,22 @@ class IndexMask {
{
}
+ /** Checks that the indices are non-negative and in ascending order. */
+ static bool indices_are_valid_index_mask(Span<int64_t> indices)
+ {
+ if (!indices.is_empty()) {
+ if (indices.first() < 0) {
+ return false;
+ }
+ }
+ for (int64_t i = 1; i < indices.size(); i++) {
+ if (indices[i - 1] >= indices[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
operator Span<int64_t>() const
{
return indices_;
@@ -204,6 +216,11 @@ class IndexMask {
{
return indices_.size();
}
+
+ bool is_empty() const
+ {
+ return indices_.is_empty();
+ }
};
} // namespace blender
diff --git a/source/blender/blenlib/BLI_math_base.h b/source/blender/blenlib/BLI_math_base.h
index 88dc20a64f2..e877503e835 100644
--- a/source/blender/blenlib/BLI_math_base.h
+++ b/source/blender/blenlib/BLI_math_base.h
@@ -120,6 +120,9 @@ MINLINE double interpd(double a, double b, double t);
MINLINE float ratiof(float min, float max, float pos);
MINLINE double ratiod(double min, double max, double pos);
+MINLINE float scalenorm(float a, float b, float x);
+MINLINE double scalenormd(double a, double b, double x);
+
/* NOTE: Compilers will upcast all types smaller than int to int when performing arithmetic
* operation. */
MINLINE int square_s(short a);
diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h
index 9ac14a6edfe..bcda25ca533 100644
--- a/source/blender/blenlib/BLI_math_geom.h
+++ b/source/blender/blenlib/BLI_math_geom.h
@@ -682,14 +682,14 @@ void planes_from_projmat(const float mat[4][4],
float near[4],
float far[4]);
-void projmat_dimensions(const float projmat[4][4],
+void projmat_dimensions(const float winmat[4][4],
float *r_left,
float *r_right,
float *r_bottom,
float *r_top,
float *r_near,
float *r_far);
-void projmat_dimensions_db(const float projmat[4][4],
+void projmat_dimensions_db(const float winmat[4][4],
double *r_left,
double *r_right,
double *r_bottom,
diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h
index 2f4cf1721af..860ba14a3ed 100644
--- a/source/blender/blenlib/BLI_math_vector.h
+++ b/source/blender/blenlib/BLI_math_vector.h
@@ -185,6 +185,7 @@ MINLINE void negate_v3_db(double r[3]);
MINLINE void invert_v2(float r[2]);
MINLINE void invert_v3(float r[3]);
+MINLINE void invert_v3_safe(float r[3]); /* Invert the vector, but leaves zero values as zero. */
MINLINE void abs_v2(float r[2]);
MINLINE void abs_v2_v2(float r[2], const float a[2]);
diff --git a/source/blender/functions/FN_multi_function_network_optimization.hh b/source/blender/blenlib/BLI_range.h
index 96664fa368e..e55f443769d 100644
--- a/source/blender/functions/FN_multi_function_network_optimization.hh
+++ b/source/blender/blenlib/BLI_range.h
@@ -16,14 +16,24 @@
#pragma once
-#include "FN_multi_function_network.hh"
+/** \file
+ * \ingroup bli
+ */
-#include "BLI_resource_scope.hh"
+#ifdef __cplusplus
+extern "C" {
+#endif
-namespace blender::fn::mf_network_optimization {
+typedef struct Range2f {
+ float min;
+ float max;
+} Range2f;
-void dead_node_removal(MFNetwork &network);
-void constant_folding(MFNetwork &network, ResourceScope &scope);
-void common_subnetwork_elimination(MFNetwork &network);
+BLI_INLINE bool range2f_in_range(const Range2f *range, const float value)
+{
+ return IN_RANGE(value, range->min, range->max);
+}
-} // namespace blender::fn::mf_network_optimization
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/blenlib/BLI_winstuff.h b/source/blender/blenlib/BLI_winstuff.h
index 0953e3f1946..bf09b56c779 100644
--- a/source/blender/blenlib/BLI_winstuff.h
+++ b/source/blender/blenlib/BLI_winstuff.h
@@ -45,7 +45,7 @@
#undef small
-// These definitions are also in BLI_math for simplicity
+/* These definitions are also in BLI_math for simplicity. */
#ifdef __cplusplus
extern "C" {
@@ -72,7 +72,7 @@ extern "C" {
#if defined(_MSC_VER)
# define R_OK 4
# define W_OK 2
-// not accepted by access() on windows
+/* Not accepted by `access()` on windows. */
//# define X_OK 1
# define F_OK 0
#endif
diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt
index ea5572f1c8a..f98d15ad08b 100644
--- a/source/blender/blenlib/CMakeLists.txt
+++ b/source/blender/blenlib/CMakeLists.txt
@@ -31,6 +31,7 @@ set(INC
set(INC_SYS
${ZLIB_INCLUDE_DIRS}
+ ${ZSTD_INCLUDE_DIRS}
${FREETYPE_INCLUDE_DIRS}
${GMP_INCLUDE_DIRS}
)
@@ -75,6 +76,10 @@ set(SRC
intern/endian_switch.c
intern/expr_pylike_eval.c
intern/fileops.c
+ intern/filereader_file.c
+ intern/filereader_gzip.c
+ intern/filereader_memory.c
+ intern/filereader_zstd.c
intern/fnmatch.c
intern/freetypefont.c
intern/gsqueue.c
@@ -194,6 +199,7 @@ set(SRC
BLI_enumerable_thread_specific.hh
BLI_expr_pylike_eval.h
BLI_fileops.h
+ BLI_filereader.h
BLI_fileops_types.h
BLI_float2.hh
BLI_float3.hh
@@ -265,6 +271,7 @@ set(SRC
BLI_quadric.h
BLI_rand.h
BLI_rand.hh
+ BLI_range.h
BLI_rect.h
BLI_resource_scope.hh
BLI_scanfill.h
@@ -322,6 +329,7 @@ set(LIB
${FREETYPE_LIBRARY}
${ZLIB_LIBRARIES}
+ ${ZSTD_LIBRARIES}
)
if(WITH_MEM_VALGRIND)
diff --git a/source/blender/blenlib/intern/boxpack_2d.c b/source/blender/blenlib/intern/boxpack_2d.c
index ea6c2d8d498..4a07f1134d0 100644
--- a/source/blender/blenlib/intern/boxpack_2d.c
+++ b/source/blender/blenlib/intern/boxpack_2d.c
@@ -409,8 +409,8 @@ void BLI_box_pack_2d(BoxPack *boxarray, const uint len, float *r_tot_x, float *r
for (i = 0; i < verts_pack_len && isect; i++) {
vert = &vs_ctx.vertarray[vertex_pack_indices[i]];
- /* printf("\ttesting vert %i %i %i %f %f\n", i,
- * vert->free, verts_pack_len, vert->x, vert->y); */
+ // printf("\ttesting vert %i %i %i %f %f\n", i,
+ // vert->free, verts_pack_len, vert->x, vert->y);
/* This vert has a free quadrant
* Test if we can place the box here
diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c
index ac034d2b5cd..532a29b8147 100644
--- a/source/blender/blenlib/intern/fileops.c
+++ b/source/blender/blenlib/intern/fileops.c
@@ -31,6 +31,7 @@
#include <errno.h>
#include "zlib.h"
+#include "zstd.h"
#ifdef WIN32
# include "BLI_fileops_types.h"
@@ -61,200 +62,124 @@
#include "BLI_sys_types.h" /* for intptr_t support */
#include "BLI_utildefines.h"
-#if 0 /* UNUSED */
-/* gzip the file in from and write it to "to".
- * return -1 if zlib fails, -2 if the originating file does not exist
- * NOTE: will remove the "from" file
- */
-int BLI_file_gzip(const char *from, const char *to)
+size_t BLI_file_zstd_from_mem_at_pos(
+ void *buf, size_t len, FILE *file, size_t file_offset, int compression_level)
{
- char buffer[10240];
- int file;
- int readsize = 0;
- int rval = 0, err;
- gzFile gzfile;
+ fseek(file, file_offset, SEEK_SET);
- /* level 1 is very close to 3 (the default) in terms of file size,
- * but about twice as fast, best use for speedy saving - campbell */
- gzfile = BLI_gzopen(to, "wb1");
- if (gzfile == NULL) {
- return -1;
- }
- file = BLI_open(from, O_BINARY | O_RDONLY, 0);
- if (file == -1) {
- return -2;
- }
+ ZSTD_CCtx *ctx = ZSTD_createCCtx();
+ ZSTD_CCtx_setParameter(ctx, ZSTD_c_compressionLevel, compression_level);
+
+ ZSTD_inBuffer input = {buf, len, 0};
- while (1) {
- readsize = read(file, buffer, sizeof(buffer));
+ size_t out_len = ZSTD_CStreamOutSize();
+ void *out_buf = MEM_mallocN(out_len, __func__);
+ size_t total_written = 0;
- if (readsize < 0) {
- rval = -2; /* error happened in reading */
- fprintf(stderr, "Error reading file %s: %s.\n", from, strerror(errno));
+ /* Compress block and write it out until the input has been consumed. */
+ while (input.pos < input.size) {
+ ZSTD_outBuffer output = {out_buf, out_len, 0};
+ size_t ret = ZSTD_compressStream2(ctx, &output, &input, ZSTD_e_continue);
+ if (ZSTD_isError(ret)) {
break;
}
- else if (readsize == 0) {
- break; /* done reading */
+ if (fwrite(out_buf, 1, output.pos, file) != output.pos) {
+ break;
}
+ total_written += output.pos;
+ }
- if (gzwrite(gzfile, buffer, readsize) <= 0) {
- rval = -1; /* error happened in writing */
- fprintf(stderr, "Error writing gz file %s: %s.\n", to, gzerror(gzfile, &err));
+ /* Finalize the Zstd frame. */
+ size_t ret = 1;
+ while (ret != 0) {
+ ZSTD_outBuffer output = {out_buf, out_len, 0};
+ ret = ZSTD_compressStream2(ctx, &output, &input, ZSTD_e_end);
+ if (ZSTD_isError(ret)) {
+ break;
+ }
+ if (fwrite(out_buf, 1, output.pos, file) != output.pos) {
break;
}
+ total_written += output.pos;
}
- gzclose(gzfile);
- close(file);
+ MEM_freeN(out_buf);
+ ZSTD_freeCCtx(ctx);
- return rval;
+ return ZSTD_isError(ret) ? 0 : total_written;
}
-#endif
-/* gzip the file in from_file and write it to memory to_mem, at most size bytes.
- * return the unzipped size
- */
-char *BLI_file_ungzip_to_mem(const char *from_file, int *r_size)
+size_t BLI_file_unzstd_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t file_offset)
{
- gzFile gzfile;
- int readsize, size, alloc_size = 0;
- char *mem = NULL;
- const int chunk_size = 512 * 1024;
+ fseek(file, file_offset, SEEK_SET);
- size = 0;
+ ZSTD_DCtx *ctx = ZSTD_createDCtx();
- gzfile = BLI_gzopen(from_file, "rb");
- for (;;) {
- if (mem == NULL) {
- mem = MEM_callocN(chunk_size, "BLI_ungzip_to_mem");
- alloc_size = chunk_size;
- }
- else {
- mem = MEM_reallocN(mem, size + chunk_size);
- alloc_size += chunk_size;
- }
+ size_t in_len = ZSTD_DStreamInSize();
+ void *in_buf = MEM_mallocN(in_len, __func__);
+ ZSTD_inBuffer input = {in_buf, in_len, 0};
- readsize = gzread(gzfile, mem + size, chunk_size);
- if (readsize > 0) {
- size += readsize;
- }
- else {
+ ZSTD_outBuffer output = {buf, len, 0};
+
+ size_t ret = 0;
+ /* Read and decompress chunks of input data until we have enough output. */
+ while (output.pos < output.size && !ZSTD_isError(ret)) {
+ input.size = fread(in_buf, 1, in_len, file);
+ if (input.size == 0) {
break;
}
- }
- gzclose(gzfile);
+ /* Consume input data until we run out or have enough output. */
+ input.pos = 0;
+ while (input.pos < input.size && output.pos < output.size) {
+ ret = ZSTD_decompressStream(ctx, &output, &input);
- if (size == 0) {
- MEM_freeN(mem);
- mem = NULL;
- }
- else if (alloc_size != size) {
- mem = MEM_reallocN(mem, size);
+ if (ZSTD_isError(ret)) {
+ break;
+ }
+ }
}
- *r_size = size;
+ MEM_freeN(in_buf);
+ ZSTD_freeDCtx(ctx);
- return mem;
+ return ZSTD_isError(ret) ? 0 : output.pos;
}
-#define CHUNK (256 * 1024)
-
-/* gzip byte array from memory and write it to file at certain position.
- * return size of gzip stream.
- */
-size_t BLI_gzip_mem_to_file_at_pos(
- void *buf, size_t len, FILE *file, size_t gz_stream_offset, int compression_level)
+bool BLI_file_magic_is_gzip(const char header[4])
{
- int ret, flush;
- unsigned have;
- z_stream strm;
- unsigned char out[CHUNK];
-
- BLI_fseek(file, gz_stream_offset, 0);
-
- strm.zalloc = Z_NULL;
- strm.zfree = Z_NULL;
- strm.opaque = Z_NULL;
- ret = deflateInit(&strm, compression_level);
- if (ret != Z_OK) {
- return 0;
- }
-
- strm.avail_in = len;
- strm.next_in = (Bytef *)buf;
- flush = Z_FINISH;
-
- do {
- strm.avail_out = CHUNK;
- strm.next_out = out;
- ret = deflate(&strm, flush);
- if (ret == Z_STREAM_ERROR) {
- return 0;
- }
- have = CHUNK - strm.avail_out;
- if (fwrite(out, 1, have, file) != have || ferror(file)) {
- deflateEnd(&strm);
- return 0;
- }
- } while (strm.avail_out == 0);
-
- if (strm.avail_in != 0 || ret != Z_STREAM_END) {
- return 0;
- }
-
- deflateEnd(&strm);
- return (size_t)strm.total_out;
+ /* GZIP itself starts with the magic bytes 0x1f 0x8b.
+ * The third byte indicates the compression method, which is 0x08 for DEFLATE. */
+ return header[0] == 0x1f && header[1] == 0x8b && header[2] == 0x08;
}
-/* read and decompress gzip stream from file at certain position to buffer.
- * return size of decompressed data.
- */
-size_t BLI_ungzip_file_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t gz_stream_offset)
+bool BLI_file_magic_is_zstd(const char header[4])
{
- int ret;
- z_stream strm;
- size_t chunk = 256 * 1024;
- unsigned char in[CHUNK];
-
- BLI_fseek(file, gz_stream_offset, 0);
-
- strm.zalloc = Z_NULL;
- strm.zfree = Z_NULL;
- strm.opaque = Z_NULL;
- strm.avail_in = 0;
- strm.next_in = Z_NULL;
- ret = inflateInit(&strm);
- if (ret != Z_OK) {
- return 0;
+ /* ZSTD files consist of concatenated frames, each either a Zstd frame or a skippable frame.
+ * Both types of frames start with a magic number: 0xFD2FB528 for Zstd frames and 0x184D2A5*
+ * for skippable frames, with the * being anything from 0 to F.
+ *
+ * To check whether a file is Zstd-compressed, we just check whether the first frame matches
+ * either. Seeking through the file until a Zstd frame is found would make things more
+ * complicated and the probability of a false positive is rather low anyways.
+ *
+ * Note that LZ4 uses a compatible format, so even though its compressed frames have a
+ * different magic number, a valid LZ4 file might also start with a skippable frame matching
+ * the second check here.
+ *
+ * For more details, see https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md
+ */
+
+ uint32_t magic = *((uint32_t *)header);
+ if (magic == 0xFD2FB528) {
+ return true;
}
-
- do {
- strm.avail_in = fread(in, 1, chunk, file);
- strm.next_in = in;
- if (ferror(file)) {
- inflateEnd(&strm);
- return 0;
- }
-
- do {
- strm.avail_out = len;
- strm.next_out = (Bytef *)buf + strm.total_out;
-
- ret = inflate(&strm, Z_NO_FLUSH);
- if (ret == Z_STREAM_ERROR) {
- return 0;
- }
- } while (strm.avail_out == 0);
-
- } while (ret != Z_STREAM_END);
-
- inflateEnd(&strm);
- return (size_t)strm.total_out;
+ if ((magic >> 4) == 0x184D2A5) {
+ return true;
+ }
+ return false;
}
-#undef CHUNK
-
/**
* Returns true if the file with the specified name can be written.
* This implementation uses access(2), which makes the check according
diff --git a/source/blender/blenlib/intern/filereader_file.c b/source/blender/blenlib/intern/filereader_file.c
new file mode 100644
index 00000000000..3a833871e27
--- /dev/null
+++ b/source/blender/blenlib/intern/filereader_file.c
@@ -0,0 +1,80 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2004-2021 Blender Foundation
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bli
+ */
+
+#ifndef WIN32
+# include <unistd.h> /* for read close */
+#else
+# include "BLI_winstuff.h"
+# include "winsock2.h"
+# include <io.h> /* for open close read */
+#endif
+
+#include "BLI_blenlib.h"
+#include "BLI_filereader.h"
+
+#include "MEM_guardedalloc.h"
+
+typedef struct {
+ FileReader reader;
+
+ int filedes;
+} RawFileReader;
+
+static ssize_t file_read(FileReader *reader, void *buffer, size_t size)
+{
+ RawFileReader *rawfile = (RawFileReader *)reader;
+ ssize_t readsize = read(rawfile->filedes, buffer, size);
+
+ if (readsize >= 0) {
+ rawfile->reader.offset += readsize;
+ }
+
+ return readsize;
+}
+
+static off64_t file_seek(FileReader *reader, off64_t offset, int whence)
+{
+ RawFileReader *rawfile = (RawFileReader *)reader;
+ rawfile->reader.offset = BLI_lseek(rawfile->filedes, offset, whence);
+ return rawfile->reader.offset;
+}
+
+static void file_close(FileReader *reader)
+{
+ RawFileReader *rawfile = (RawFileReader *)reader;
+ close(rawfile->filedes);
+ MEM_freeN(rawfile);
+}
+
+FileReader *BLI_filereader_new_file(int filedes)
+{
+ RawFileReader *rawfile = MEM_callocN(sizeof(RawFileReader), __func__);
+
+ rawfile->filedes = filedes;
+
+ rawfile->reader.read = file_read;
+ rawfile->reader.seek = file_seek;
+ rawfile->reader.close = file_close;
+
+ return (FileReader *)rawfile;
+}
diff --git a/source/blender/blenlib/intern/filereader_gzip.c b/source/blender/blenlib/intern/filereader_gzip.c
new file mode 100644
index 00000000000..72eb153a8b9
--- /dev/null
+++ b/source/blender/blenlib/intern/filereader_gzip.c
@@ -0,0 +1,108 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2004-2021 Blender Foundation
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bli
+ */
+
+#include <zlib.h>
+
+#include "BLI_blenlib.h"
+#include "BLI_filereader.h"
+
+#include "MEM_guardedalloc.h"
+
+typedef struct {
+ FileReader reader;
+
+ FileReader *base;
+
+ z_stream strm;
+
+ void *in_buf;
+ size_t in_size;
+} GzipReader;
+
+static ssize_t gzip_read(FileReader *reader, void *buffer, size_t size)
+{
+ GzipReader *gzip = (GzipReader *)reader;
+
+ gzip->strm.avail_out = size;
+ gzip->strm.next_out = buffer;
+
+ while (gzip->strm.avail_out > 0) {
+ if (gzip->strm.avail_in == 0) {
+ /* Ran out of buffered input data, read some more. */
+ size_t readsize = gzip->base->read(gzip->base, gzip->in_buf, gzip->in_size);
+
+ if (readsize > 0) {
+ /* We got some data, so mark the buffer as refilled. */
+ gzip->strm.avail_in = readsize;
+ gzip->strm.next_in = gzip->in_buf;
+ }
+ else {
+ /* The underlying file is EOF, so return as much as we can. */
+ break;
+ }
+ }
+
+ int ret = inflate(&gzip->strm, Z_NO_FLUSH);
+
+ if (ret != Z_OK && ret != Z_BUF_ERROR) {
+ break;
+ }
+ }
+
+ ssize_t read_len = size - gzip->strm.avail_out;
+ gzip->reader.offset += read_len;
+ return read_len;
+}
+
+static void gzip_close(FileReader *reader)
+{
+ GzipReader *gzip = (GzipReader *)reader;
+
+ if (inflateEnd(&gzip->strm) != Z_OK) {
+ printf("close gzip stream error\n");
+ }
+ MEM_freeN((void *)gzip->in_buf);
+
+ gzip->base->close(gzip->base);
+ MEM_freeN(gzip);
+}
+
+FileReader *BLI_filereader_new_gzip(FileReader *base)
+{
+ GzipReader *gzip = MEM_callocN(sizeof(GzipReader), __func__);
+ gzip->base = base;
+
+ if (inflateInit2(&gzip->strm, 16 + MAX_WBITS) != Z_OK) {
+ MEM_freeN(gzip);
+ return NULL;
+ }
+
+ gzip->in_size = 256 * 2014;
+ gzip->in_buf = MEM_mallocN(gzip->in_size, "gzip in buf");
+
+ gzip->reader.read = gzip_read;
+ gzip->reader.seek = NULL;
+ gzip->reader.close = gzip_close;
+
+ return (FileReader *)gzip;
+}
diff --git a/source/blender/blenlib/intern/filereader_memory.c b/source/blender/blenlib/intern/filereader_memory.c
new file mode 100644
index 00000000000..150fed7d1cc
--- /dev/null
+++ b/source/blender/blenlib/intern/filereader_memory.c
@@ -0,0 +1,145 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2004-2021 Blender Foundation
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bli
+ */
+
+#include <string.h>
+
+#include "BLI_blenlib.h"
+#include "BLI_filereader.h"
+#include "BLI_mmap.h"
+
+#include "MEM_guardedalloc.h"
+
+/* This file implements both memory-backed and memory-mapped-file-backed reading. */
+typedef struct {
+ FileReader reader;
+
+ const char *data;
+ BLI_mmap_file *mmap;
+ size_t length;
+} MemoryReader;
+
+static ssize_t memory_read_raw(FileReader *reader, void *buffer, size_t size)
+{
+ MemoryReader *mem = (MemoryReader *)reader;
+
+ /* Don't read more bytes than there are available in the buffer. */
+ size_t readsize = MIN2(size, (size_t)(mem->length - mem->reader.offset));
+
+ memcpy(buffer, mem->data + mem->reader.offset, readsize);
+ mem->reader.offset += readsize;
+
+ return readsize;
+}
+
+static off64_t memory_seek(FileReader *reader, off64_t offset, int whence)
+{
+ MemoryReader *mem = (MemoryReader *)reader;
+
+ off64_t new_pos;
+ if (whence == SEEK_CUR) {
+ new_pos = mem->reader.offset + offset;
+ }
+ else if (whence == SEEK_SET) {
+ new_pos = offset;
+ }
+ else if (whence == SEEK_END) {
+ new_pos = mem->length + offset;
+ }
+ else {
+ return -1;
+ }
+
+ if (new_pos < 0 || new_pos > mem->length) {
+ return -1;
+ }
+
+ mem->reader.offset = new_pos;
+ return mem->reader.offset;
+}
+
+static void memory_close_raw(FileReader *reader)
+{
+ MEM_freeN(reader);
+}
+
+FileReader *BLI_filereader_new_memory(const void *data, size_t len)
+{
+ MemoryReader *mem = MEM_callocN(sizeof(MemoryReader), __func__);
+
+ mem->data = (const char *)data;
+ mem->length = len;
+
+ mem->reader.read = memory_read_raw;
+ mem->reader.seek = memory_seek;
+ mem->reader.close = memory_close_raw;
+
+ return (FileReader *)mem;
+}
+
+/* Memory-mapped file reading.
+ * By using `mmap()`, we can map a file so that it can be treated like normal memory,
+ * meaning that we can just read from it with `memcpy()` etc.
+ * This avoids system call overhead and can significantly speed up file loading.
+ */
+
+static ssize_t memory_read_mmap(FileReader *reader, void *buffer, size_t size)
+{
+ MemoryReader *mem = (MemoryReader *)reader;
+
+ /* Don't read more bytes than there are available in the buffer. */
+ size_t readsize = MIN2(size, (size_t)(mem->length - mem->reader.offset));
+
+ if (!BLI_mmap_read(mem->mmap, buffer, mem->reader.offset, readsize)) {
+ return 0;
+ }
+
+ mem->reader.offset += readsize;
+
+ return readsize;
+}
+
+static void memory_close_mmap(FileReader *reader)
+{
+ MemoryReader *mem = (MemoryReader *)reader;
+ BLI_mmap_free(mem->mmap);
+ MEM_freeN(mem);
+}
+
+FileReader *BLI_filereader_new_mmap(int filedes)
+{
+ BLI_mmap_file *mmap = BLI_mmap_open(filedes);
+ if (mmap == NULL) {
+ return NULL;
+ }
+
+ MemoryReader *mem = MEM_callocN(sizeof(MemoryReader), __func__);
+
+ mem->mmap = mmap;
+ mem->length = BLI_lseek(filedes, 0, SEEK_END);
+
+ mem->reader.read = memory_read_mmap;
+ mem->reader.seek = memory_seek;
+ mem->reader.close = memory_close_mmap;
+
+ return (FileReader *)mem;
+}
diff --git a/source/blender/blenlib/intern/filereader_zstd.c b/source/blender/blenlib/intern/filereader_zstd.c
new file mode 100644
index 00000000000..785a40cd1a1
--- /dev/null
+++ b/source/blender/blenlib/intern/filereader_zstd.c
@@ -0,0 +1,335 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2021 Blender Foundation
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bli
+ */
+
+#include <string.h>
+#include <zstd.h>
+
+#include "BLI_blenlib.h"
+#include "BLI_endian_switch.h"
+#include "BLI_filereader.h"
+#include "BLI_math_base.h"
+
+#include "MEM_guardedalloc.h"
+
+typedef struct {
+ FileReader reader;
+
+ FileReader *base;
+
+ ZSTD_DCtx *ctx;
+ ZSTD_inBuffer in_buf;
+ size_t in_buf_max_size;
+
+ struct {
+ int num_frames;
+ size_t *compressed_ofs;
+ size_t *uncompressed_ofs;
+
+ char *cached_content;
+ int cached_frame;
+ } seek;
+} ZstdReader;
+
+static bool zstd_read_u32(FileReader *base, uint32_t *val)
+{
+ if (base->read(base, val, sizeof(uint32_t)) != sizeof(uint32_t)) {
+ return false;
+ }
+#ifdef __BIG_ENDIAN__
+ BLI_endian_switch_uint32(val);
+#endif
+ return true;
+}
+
+static bool zstd_read_seek_table(ZstdReader *zstd)
+{
+ FileReader *base = zstd->base;
+
+ /* The seek table frame is at the end of the file, so seek there
+ * and verify that there is enough data. */
+ if (base->seek(base, -4, SEEK_END) < 13) {
+ return false;
+ }
+ uint32_t magic;
+ if (!zstd_read_u32(base, &magic) || magic != 0x8F92EAB1) {
+ return false;
+ }
+
+ uint8_t flags;
+ if (base->seek(base, -5, SEEK_END) < 0 || base->read(base, &flags, 1) != 1) {
+ return false;
+ }
+ /* Bit 7 indicates checksums. Bits 5 and 6 must be zero. */
+ bool has_checksums = (flags & 0x80);
+ if (flags & 0x60) {
+ return false;
+ }
+
+ uint32_t num_frames;
+ if (base->seek(base, -9, SEEK_END) < 0 || !zstd_read_u32(base, &num_frames)) {
+ return false;
+ }
+
+ /* Each frame has either 2 or 3 uint32_t, and after that we have
+ * num_frames, flags and magic for another 9 bytes. */
+ uint32_t expected_frame_length = num_frames * (has_checksums ? 12 : 8) + 9;
+ /* The frame starts with another magic number and its length, but these
+ * two fields are not included when counting length. */
+ off64_t frame_start_ofs = 8 + expected_frame_length;
+ /* Sanity check: Before the start of the seek table frame,
+ * there must be num_frames frames, each of which at least 8 bytes long. */
+ off64_t seek_frame_start = base->seek(base, -frame_start_ofs, SEEK_END);
+ if (seek_frame_start < num_frames * 8) {
+ return false;
+ }
+
+ if (!zstd_read_u32(base, &magic) || magic != 0x184D2A5E) {
+ return false;
+ }
+
+ uint32_t frame_length;
+ if (!zstd_read_u32(base, &frame_length) || frame_length != expected_frame_length) {
+ return false;
+ }
+
+ zstd->seek.num_frames = num_frames;
+ zstd->seek.compressed_ofs = MEM_malloc_arrayN(num_frames + 1, sizeof(size_t), __func__);
+ zstd->seek.uncompressed_ofs = MEM_malloc_arrayN(num_frames + 1, sizeof(size_t), __func__);
+
+ size_t compressed_ofs = 0;
+ size_t uncompressed_ofs = 0;
+ for (int i = 0; i < num_frames; i++) {
+ uint32_t compressed_size, uncompressed_size;
+ if (!zstd_read_u32(base, &compressed_size) || !zstd_read_u32(base, &uncompressed_size)) {
+ break;
+ }
+ if (has_checksums && base->seek(base, 4, SEEK_CUR) < 0) {
+ break;
+ }
+ zstd->seek.compressed_ofs[i] = compressed_ofs;
+ zstd->seek.uncompressed_ofs[i] = uncompressed_ofs;
+ compressed_ofs += compressed_size;
+ uncompressed_ofs += uncompressed_size;
+ }
+ zstd->seek.compressed_ofs[num_frames] = compressed_ofs;
+ zstd->seek.uncompressed_ofs[num_frames] = uncompressed_ofs;
+
+ /* Seek to the end of the previous frame for the following BHead frame detection. */
+ if (seek_frame_start != compressed_ofs || base->seek(base, seek_frame_start, SEEK_SET) < 0) {
+ MEM_freeN(zstd->seek.compressed_ofs);
+ MEM_freeN(zstd->seek.uncompressed_ofs);
+ memset(&zstd->seek, 0, sizeof(zstd->seek));
+ return false;
+ }
+
+ zstd->seek.cached_frame = -1;
+
+ return true;
+}
+
+/* Find out which frame contains the given position in the uncompressed stream.
+ * Basically just bisection. */
+static int zstd_frame_from_pos(ZstdReader *zstd, size_t pos)
+{
+ int low = 0, high = zstd->seek.num_frames;
+
+ if (pos >= zstd->seek.uncompressed_ofs[zstd->seek.num_frames]) {
+ return -1;
+ }
+
+ while (low + 1 < high) {
+ int mid = low + ((high - low) >> 1);
+ if (zstd->seek.uncompressed_ofs[mid] <= pos) {
+ low = mid;
+ }
+ else {
+ high = mid;
+ }
+ }
+
+ return low;
+}
+
+/* Ensure that the currently loaded frame is the correct one. */
+static const char *zstd_ensure_cache(ZstdReader *zstd, int frame)
+{
+ if (zstd->seek.cached_frame == frame) {
+ /* Cached frame matches, so just return it. */
+ return zstd->seek.cached_content;
+ }
+
+ /* Cached frame doesn't match, so discard it and cache the wanted one onstead. */
+ MEM_SAFE_FREE(zstd->seek.cached_content);
+
+ size_t compressed_size = zstd->seek.compressed_ofs[frame + 1] - zstd->seek.compressed_ofs[frame];
+ size_t uncompressed_size = zstd->seek.uncompressed_ofs[frame + 1] -
+ zstd->seek.uncompressed_ofs[frame];
+
+ char *uncompressed_data = MEM_mallocN(uncompressed_size, __func__);
+ char *compressed_data = MEM_mallocN(compressed_size, __func__);
+ if (zstd->base->seek(zstd->base, zstd->seek.compressed_ofs[frame], SEEK_SET) < 0 ||
+ zstd->base->read(zstd->base, compressed_data, compressed_size) < compressed_size) {
+ MEM_freeN(compressed_data);
+ MEM_freeN(uncompressed_data);
+ return NULL;
+ }
+
+ size_t res = ZSTD_decompressDCtx(
+ zstd->ctx, uncompressed_data, uncompressed_size, compressed_data, compressed_size);
+ MEM_freeN(compressed_data);
+ if (ZSTD_isError(res) || res < uncompressed_size) {
+ MEM_freeN(uncompressed_data);
+ return NULL;
+ }
+
+ zstd->seek.cached_frame = frame;
+ zstd->seek.cached_content = uncompressed_data;
+ return uncompressed_data;
+}
+
+static ssize_t zstd_read_seekable(FileReader *reader, void *buffer, size_t size)
+{
+ ZstdReader *zstd = (ZstdReader *)reader;
+
+ size_t end_offset = zstd->reader.offset + size, read_len = 0;
+ while (zstd->reader.offset < end_offset) {
+ int frame = zstd_frame_from_pos(zstd, zstd->reader.offset);
+ if (frame < 0) {
+ /* EOF is reached, so return as much as we can. */
+ break;
+ }
+
+ const char *framedata = zstd_ensure_cache(zstd, frame);
+ if (framedata == NULL) {
+ /* Error while reading the frame, so return as much as we can. */
+ break;
+ }
+
+ size_t frame_end_offset = min_zz(zstd->seek.uncompressed_ofs[frame + 1], end_offset);
+ size_t frame_read_len = frame_end_offset - zstd->reader.offset;
+
+ size_t offset_in_frame = zstd->reader.offset - zstd->seek.uncompressed_ofs[frame];
+ memcpy((char *)buffer + read_len, framedata + offset_in_frame, frame_read_len);
+ read_len += frame_read_len;
+ zstd->reader.offset = frame_end_offset;
+ }
+
+ return read_len;
+}
+
+static off64_t zstd_seek(FileReader *reader, off64_t offset, int whence)
+{
+ ZstdReader *zstd = (ZstdReader *)reader;
+ off64_t new_pos;
+ if (whence == SEEK_SET) {
+ new_pos = offset;
+ }
+ else if (whence == SEEK_END) {
+ new_pos = zstd->seek.uncompressed_ofs[zstd->seek.num_frames] + offset;
+ }
+ else {
+ new_pos = zstd->reader.offset + offset;
+ }
+
+ if (new_pos < 0 || new_pos > zstd->seek.uncompressed_ofs[zstd->seek.num_frames]) {
+ return -1;
+ }
+ zstd->reader.offset = new_pos;
+ return zstd->reader.offset;
+}
+
+static ssize_t zstd_read(FileReader *reader, void *buffer, size_t size)
+{
+ ZstdReader *zstd = (ZstdReader *)reader;
+ ZSTD_outBuffer output = {buffer, size, 0};
+
+ while (output.pos < output.size) {
+ if (zstd->in_buf.pos == zstd->in_buf.size) {
+ /* Ran out of buffered input data, read some more. */
+ zstd->in_buf.pos = 0;
+ ssize_t readsize = zstd->base->read(
+ zstd->base, (char *)zstd->in_buf.src, zstd->in_buf_max_size);
+
+ if (readsize > 0) {
+ /* We got some data, so mark the buffer as refilled. */
+ zstd->in_buf.size = readsize;
+ }
+ else {
+ /* The underlying file is EOF, so return as much as we can. */
+ break;
+ }
+ }
+
+ if (ZSTD_isError(ZSTD_decompressStream(zstd->ctx, &output, &zstd->in_buf))) {
+ break;
+ }
+ }
+
+ zstd->reader.offset += output.pos;
+ return output.pos;
+}
+
+static void zstd_close(FileReader *reader)
+{
+ ZstdReader *zstd = (ZstdReader *)reader;
+
+ ZSTD_freeDCtx(zstd->ctx);
+ if (zstd->reader.seek) {
+ MEM_freeN(zstd->seek.uncompressed_ofs);
+ MEM_freeN(zstd->seek.compressed_ofs);
+ MEM_freeN(zstd->seek.cached_content);
+ }
+ else {
+ MEM_freeN((void *)zstd->in_buf.src);
+ }
+
+ zstd->base->close(zstd->base);
+ MEM_freeN(zstd);
+}
+
+FileReader *BLI_filereader_new_zstd(FileReader *base)
+{
+ ZstdReader *zstd = MEM_callocN(sizeof(ZstdReader), __func__);
+
+ zstd->ctx = ZSTD_createDCtx();
+ zstd->base = base;
+
+ if (zstd_read_seek_table(zstd)) {
+ zstd->reader.read = zstd_read_seekable;
+ zstd->reader.seek = zstd_seek;
+ }
+ else {
+ zstd->reader.read = zstd_read;
+ zstd->reader.seek = NULL;
+
+ zstd->in_buf_max_size = ZSTD_DStreamInSize();
+ zstd->in_buf.src = MEM_mallocN(zstd->in_buf_max_size, "zstd in buf");
+ zstd->in_buf.size = zstd->in_buf_max_size;
+ /* This signals that the buffer has run out,
+ * which will make the read function refill it on the first call. */
+ zstd->in_buf.pos = zstd->in_buf_max_size;
+ }
+ zstd->reader.close = zstd_close;
+
+ return (FileReader *)zstd;
+}
diff --git a/source/blender/blenlib/intern/freetypefont.c b/source/blender/blenlib/intern/freetypefont.c
index 98da85e3a8c..a8b50b66f5f 100644
--- a/source/blender/blenlib/intern/freetypefont.c
+++ b/source/blender/blenlib/intern/freetypefont.c
@@ -40,6 +40,7 @@
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_string.h"
+#include "BLI_string_utf8.h"
#include "BLI_utildefines.h"
#include "BLI_vfontdata.h"
@@ -286,7 +287,6 @@ static VFontData *objfnt_to_ftvfontdata(PackedFile *pf)
const FT_ULong charcode_reserve = 256;
FT_ULong charcode = 0, lcode;
FT_UInt glyph_index;
- const char *fontname;
VFontData *vfd;
/* load the freetype font */
@@ -299,36 +299,29 @@ static VFontData *objfnt_to_ftvfontdata(PackedFile *pf)
/* allocate blender font */
vfd = MEM_callocN(sizeof(*vfd), "FTVFontData");
- /* get the name */
- fontname = FT_Get_Postscript_Name(face);
- BLI_strncpy(vfd->name, (fontname == NULL) ? "" : fontname, sizeof(vfd->name));
+ /* Get the name. */
+ if (face->family_name) {
+ BLI_snprintf(vfd->name, sizeof(vfd->name), "%s %s", face->family_name, face->style_name);
+ BLI_utf8_invalid_strip(vfd->name, strlen(vfd->name));
+ }
+
+ /* Select a character map. */
+ err = FT_Select_Charmap(face, FT_ENCODING_UNICODE);
+ if (err) {
+ err = FT_Select_Charmap(face, FT_ENCODING_APPLE_ROMAN);
+ }
+ if (err && face->num_charmaps > 0) {
+ err = FT_Select_Charmap(face, face->charmaps[0]->encoding);
+ }
+ if (err) {
+ FT_Done_Face(face);
+ MEM_freeN(vfd);
+ return NULL;
+ }
/* Extract the first 256 character from TTF */
lcode = charcode = FT_Get_First_Char(face, &glyph_index);
- /* No `charmap` found from the TTF so we need to figure it out. */
- if (glyph_index == 0) {
- FT_CharMap found = NULL;
- FT_CharMap charmap;
- int n;
-
- for (n = 0; n < face->num_charmaps; n++) {
- charmap = face->charmaps[n];
- if (charmap->encoding == FT_ENCODING_APPLE_ROMAN) {
- found = charmap;
- break;
- }
- }
-
- err = FT_Set_Charmap(face, found);
-
- if (err) {
- return NULL;
- }
-
- lcode = charcode = FT_Get_First_Char(face, &glyph_index);
- }
-
/* Blender default BFont is not "complete". */
const bool complete_font = (face->ascender != 0) && (face->descender != 0) &&
(face->ascender != face->descender);
diff --git a/source/blender/blenlib/intern/math_base_inline.c b/source/blender/blenlib/intern/math_base_inline.c
index 6e3846e59c6..a80c495ecf3 100644
--- a/source/blender/blenlib/intern/math_base_inline.c
+++ b/source/blender/blenlib/intern/math_base_inline.c
@@ -199,6 +199,13 @@ MINLINE float scalenorm(float a, float b, float x)
return (x * (b - a)) + a;
}
+/* Map a normalized value, i.e. from interval [0, 1] to interval [a, b]. */
+MINLINE double scalenormd(double a, double b, double x)
+{
+ BLI_assert(x <= 1 && x >= 0);
+ return (x * (b - a)) + a;
+}
+
/* Used for zoom values. */
MINLINE float power_of_2(float val)
{
diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c
index 803291e4a3b..8afb6b5a2be 100644
--- a/source/blender/blenlib/intern/math_geom.c
+++ b/source/blender/blenlib/intern/math_geom.c
@@ -1787,7 +1787,7 @@ bool isect_ray_tri_v3(const float ray_origin[3],
}
*r_lambda = f * dot_v3v3(e2, q);
- if ((*r_lambda < 0.0f)) {
+ if (*r_lambda < 0.0f) {
return false;
}
@@ -1864,7 +1864,7 @@ bool isect_ray_tri_epsilon_v3(const float ray_origin[3],
}
*r_lambda = f * dot_v3v3(e2, q);
- if ((*r_lambda < 0.0f)) {
+ if (*r_lambda < 0.0f) {
return false;
}
@@ -2024,7 +2024,7 @@ bool isect_ray_tri_threshold_v3(const float ray_origin[3],
cross_v3_v3v3(q, s, e1);
*r_lambda = f * dot_v3v3(e2, q);
- if ((*r_lambda < 0.0f)) {
+ if (*r_lambda < 0.0f) {
return false;
}
@@ -3325,7 +3325,7 @@ bool isect_ray_aabb_v3_simple(const float orig[3],
t[5] = (double)(bb_max[2] - orig[2]) * invdirz;
hit_dist[0] = (float)fmax(fmax(fmin(t[0], t[1]), fmin(t[2], t[3])), fmin(t[4], t[5]));
hit_dist[1] = (float)fmin(fmin(fmax(t[0], t[1]), fmax(t[2], t[3])), fmax(t[4], t[5]));
- if ((hit_dist[1] < 0.0f || hit_dist[0] > hit_dist[1])) {
+ if ((hit_dist[1] < 0.0f) || (hit_dist[0] > hit_dist[1])) {
return false;
}
@@ -4962,7 +4962,7 @@ void planes_from_projmat(const float mat[4][4],
}
}
-void projmat_dimensions(const float projmat[4][4],
+void projmat_dimensions(const float winmat[4][4],
float *r_left,
float *r_right,
float *r_bottom,
@@ -4970,27 +4970,27 @@ void projmat_dimensions(const float projmat[4][4],
float *r_near,
float *r_far)
{
- bool is_persp = projmat[3][3] == 0.0f;
-
+ const bool is_persp = winmat[3][3] == 0.0f;
if (is_persp) {
- *r_left = (projmat[2][0] - 1.0f) / projmat[0][0];
- *r_right = (projmat[2][0] + 1.0f) / projmat[0][0];
- *r_bottom = (projmat[2][1] - 1.0f) / projmat[1][1];
- *r_top = (projmat[2][1] + 1.0f) / projmat[1][1];
- *r_near = projmat[3][2] / (projmat[2][2] - 1.0f);
- *r_far = projmat[3][2] / (projmat[2][2] + 1.0f);
+ const float near = winmat[3][2] / (winmat[2][2] - 1.0f);
+ *r_left = near * ((winmat[2][0] - 1.0f) / winmat[0][0]);
+ *r_right = near * ((winmat[2][0] + 1.0f) / winmat[0][0]);
+ *r_bottom = near * ((winmat[2][1] - 1.0f) / winmat[1][1]);
+ *r_top = near * ((winmat[2][1] + 1.0f) / winmat[1][1]);
+ *r_near = near;
+ *r_far = winmat[3][2] / (winmat[2][2] + 1.0f);
}
else {
- *r_left = (-projmat[3][0] - 1.0f) / projmat[0][0];
- *r_right = (-projmat[3][0] + 1.0f) / projmat[0][0];
- *r_bottom = (-projmat[3][1] - 1.0f) / projmat[1][1];
- *r_top = (-projmat[3][1] + 1.0f) / projmat[1][1];
- *r_near = (projmat[3][2] + 1.0f) / projmat[2][2];
- *r_far = (projmat[3][2] - 1.0f) / projmat[2][2];
+ *r_left = (-winmat[3][0] - 1.0f) / winmat[0][0];
+ *r_right = (-winmat[3][0] + 1.0f) / winmat[0][0];
+ *r_bottom = (-winmat[3][1] - 1.0f) / winmat[1][1];
+ *r_top = (-winmat[3][1] + 1.0f) / winmat[1][1];
+ *r_near = (winmat[3][2] + 1.0f) / winmat[2][2];
+ *r_far = (winmat[3][2] - 1.0f) / winmat[2][2];
}
}
-void projmat_dimensions_db(const float projmat_fl[4][4],
+void projmat_dimensions_db(const float winmat_fl[4][4],
double *r_left,
double *r_right,
double *r_bottom,
@@ -4998,26 +4998,26 @@ void projmat_dimensions_db(const float projmat_fl[4][4],
double *r_near,
double *r_far)
{
- double projmat[4][4];
- copy_m4d_m4(projmat, projmat_fl);
-
- bool is_persp = projmat[3][3] == 0.0f;
+ double winmat[4][4];
+ copy_m4d_m4(winmat, winmat_fl);
+ const bool is_persp = winmat[3][3] == 0.0f;
if (is_persp) {
- *r_left = (projmat[2][0] - 1.0) / projmat[0][0];
- *r_right = (projmat[2][0] + 1.0) / projmat[0][0];
- *r_bottom = (projmat[2][1] - 1.0) / projmat[1][1];
- *r_top = (projmat[2][1] + 1.0) / projmat[1][1];
- *r_near = projmat[3][2] / (projmat[2][2] - 1.0);
- *r_far = projmat[3][2] / (projmat[2][2] + 1.0);
+ const double near = winmat[3][2] / (winmat[2][2] - 1.0);
+ *r_left = near * ((winmat[2][0] - 1.0) / winmat[0][0]);
+ *r_right = near * ((winmat[2][0] + 1.0) / winmat[0][0]);
+ *r_bottom = near * ((winmat[2][1] - 1.0) / winmat[1][1]);
+ *r_top = near * ((winmat[2][1] + 1.0) / winmat[1][1]);
+ *r_near = near;
+ *r_far = winmat[3][2] / (winmat[2][2] + 1.0);
}
else {
- *r_left = (-projmat[3][0] - 1.0) / projmat[0][0];
- *r_right = (-projmat[3][0] + 1.0) / projmat[0][0];
- *r_bottom = (-projmat[3][1] - 1.0) / projmat[1][1];
- *r_top = (-projmat[3][1] + 1.0) / projmat[1][1];
- *r_near = (projmat[3][2] + 1.0) / projmat[2][2];
- *r_far = (projmat[3][2] - 1.0) / projmat[2][2];
+ *r_left = (-winmat[3][0] - 1.0) / winmat[0][0];
+ *r_right = (-winmat[3][0] + 1.0) / winmat[0][0];
+ *r_bottom = (-winmat[3][1] - 1.0) / winmat[1][1];
+ *r_top = (-winmat[3][1] + 1.0) / winmat[1][1];
+ *r_near = (winmat[3][2] + 1.0) / winmat[2][2];
+ *r_far = (winmat[3][2] - 1.0) / winmat[2][2];
}
}
diff --git a/source/blender/blenlib/intern/math_vector_inline.c b/source/blender/blenlib/intern/math_vector_inline.c
index 55f7a152b83..ddfdaffb706 100644
--- a/source/blender/blenlib/intern/math_vector_inline.c
+++ b/source/blender/blenlib/intern/math_vector_inline.c
@@ -847,6 +847,19 @@ MINLINE void invert_v3(float r[3])
r[2] = 1.0f / r[2];
}
+MINLINE void invert_v3_safe(float r[3])
+{
+ if (r[0] != 0.0f) {
+ r[0] = 1.0f / r[0];
+ }
+ if (r[1] != 0.0f) {
+ r[1] = 1.0f / r[1];
+ }
+ if (r[2] != 0.0f) {
+ r[2] = 1.0f / r[2];
+ }
+}
+
MINLINE void abs_v2(float r[2])
{
r[0] = fabsf(r[0]);
@@ -1132,6 +1145,9 @@ MINLINE float len_v3v3(const float a[3], const float b[3])
return len_v3(d);
}
+/**
+ * \note any vectors containing `nan` will be zeroed out.
+ */
MINLINE float normalize_v2_v2_length(float r[2], const float a[2], const float unit_length)
{
float d = dot_v2v2(a, a);
@@ -1141,6 +1157,7 @@ MINLINE float normalize_v2_v2_length(float r[2], const float a[2], const float u
mul_v2_v2fl(r, a, unit_length / d);
}
else {
+ /* Either the vector is small or one of it's values contained `nan`. */
zero_v2(r);
d = 0.0f;
}
@@ -1162,17 +1179,20 @@ MINLINE float normalize_v2_length(float n[2], const float unit_length)
return normalize_v2_v2_length(n, n, unit_length);
}
+/**
+ * \note any vectors containing `nan` will be zeroed out.
+ */
MINLINE float normalize_v3_v3_length(float r[3], const float a[3], const float unit_length)
{
float d = dot_v3v3(a, a);
- /* a larger value causes normalize errors in a
- * scaled down models with camera extreme close */
+ /* A larger value causes normalize errors in a scaled down models with camera extreme close. */
if (d > 1.0e-35f) {
d = sqrtf(d);
mul_v3_v3fl(r, a, unit_length / d);
}
else {
+ /* Either the vector is small or one of it's values contained `nan`. */
zero_v3(r);
d = 0.0f;
}
diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c
index 4d0dc43ed1e..4d0678035ba 100644
--- a/source/blender/blenlib/intern/path_util.c
+++ b/source/blender/blenlib/intern/path_util.c
@@ -1102,12 +1102,9 @@ bool BLI_path_abs(char *path, const char *basepath)
}
#ifdef WIN32
- /* skip first two chars, which in case of
- * absolute path will be drive:/blabla and
- * in case of relpath //blabla/. So relpath
- * // will be retained, rest will be nice and
- * shiny win32 backward slashes :) -jesterKing
- */
+ /* NOTE(@jesterking): Skip first two chars, which in case of absolute path will
+ * be `drive:/blabla` and in case of `relpath` `//blabla/`.
+ * So `relpath` `//` will be retained, rest will be nice and shiny WIN32 backward slashes. */
BLI_str_replace_char(path + 2, '/', '\\');
#endif
@@ -1897,7 +1894,7 @@ bool BLI_path_name_at_index(const char *__restrict path,
if (index_step == index) {
*r_offset = prev;
*r_len = i - prev;
- /* printf("!!! %d %d\n", start, end); */
+ // printf("!!! %d %d\n", start, end);
return true;
}
index_step += 1;
diff --git a/source/blender/blenlib/intern/polyfill_2d_beautify.c b/source/blender/blenlib/intern/polyfill_2d_beautify.c
index 7781e3a0f6f..ed07b002e32 100644
--- a/source/blender/blenlib/intern/polyfill_2d_beautify.c
+++ b/source/blender/blenlib/intern/polyfill_2d_beautify.c
@@ -25,7 +25,7 @@
* on a simple polygon representation where we _know_:
*
* - The polygon is primitive with no holes with a continuous boundary.
- * - Tris have consistent winding.
+ * - Triangles have consistent winding.
* - 2d (saves some hassles projecting face pairs on an axis for every edge-rotation)
* also saves us having to store all previous edge-states (see #EdRotState in bmesh_beautify.c)
*
diff --git a/source/blender/blenlib/intern/scanfill.c b/source/blender/blenlib/intern/scanfill.c
index b0d00007580..f0cf19bf508 100644
--- a/source/blender/blenlib/intern/scanfill.c
+++ b/source/blender/blenlib/intern/scanfill.c
@@ -430,7 +430,7 @@ static void testvertexnearedge(ScanFillContext *sf_ctx)
/* new edge */
ed1 = BLI_scanfill_edge_add(sf_ctx, eed->v1, eve);
- /* printf("fill: vertex near edge %x\n", eve); */
+ // printf("fill: vertex near edge %x\n", eve);
ed1->poly_nr = eed->poly_nr;
eed->v1 = eve;
eve->edge_tot = 3;
@@ -608,7 +608,7 @@ static unsigned int scanfill(ScanFillContext *sf_ctx, PolyFill *pf, const int fl
sc = scdata;
for (a = 0; a < verts; a++) {
- /* printf("VERTEX %d index %d\n", a, sc->vert->tmp.u); */
+ // printf("VERTEX %d index %d\n", a, sc->vert->tmp.u);
/* Set connect-flags. */
for (ed1 = sc->edge_first; ed1; ed1 = eed_next) {
eed_next = ed1->next;
@@ -634,13 +634,13 @@ static unsigned int scanfill(ScanFillContext *sf_ctx, PolyFill *pf, const int fl
* (and doesn't work during grab). */
/* if (callLocalInterruptCallBack()) break; */
if (totface >= maxface) {
- /* printf("Fill error: endless loop. Escaped at vert %d, tot: %d.\n", a, verts); */
+ // printf("Fill error: endless loop. Escaped at vert %d, tot: %d.\n", a, verts);
a = verts;
break;
}
if (ed2 == NULL) {
sc->edge_first = sc->edge_last = NULL;
- /* printf("just 1 edge to vert\n"); */
+ // printf("just 1 edge to vert\n");
BLI_addtail(&sf_ctx->filledgebase, ed1);
ed1->v2->f = SF_VERT_NEW;
ed1->v1->edge_tot--;
@@ -662,7 +662,7 @@ static unsigned int scanfill(ScanFillContext *sf_ctx, PolyFill *pf, const int fl
break;
}
- /* printf("test verts %d %d %d\n", v1->tmp.u, v2->tmp.u, v3->tmp.u); */
+ // printf("test verts %d %d %d\n", v1->tmp.u, v2->tmp.u, v3->tmp.u);
miny = min_ff(v1->xy[1], v3->xy[1]);
sc1 = sc + 1;
@@ -705,7 +705,7 @@ static unsigned int scanfill(ScanFillContext *sf_ctx, PolyFill *pf, const int fl
if (best_sc) {
/* make new edge, and start over */
- /* printf("add new edge %d %d and start again\n", v2->tmp.u, best_sc->vert->tmp.u); */
+ // printf("add new edge %d %d and start again\n", v2->tmp.u, best_sc->vert->tmp.u);
ed3 = BLI_scanfill_edge_add(sf_ctx, v2, best_sc->vert);
BLI_remlink(&sf_ctx->filledgebase, ed3);
@@ -717,7 +717,7 @@ static unsigned int scanfill(ScanFillContext *sf_ctx, PolyFill *pf, const int fl
}
else {
/* new triangle */
- /* printf("add face %d %d %d\n", v1->tmp.u, v2->tmp.u, v3->tmp.u); */
+ // printf("add face %d %d %d\n", v1->tmp.u, v2->tmp.u, v3->tmp.u);
addfillface(sf_ctx, v1, v2, v3);
totface++;
BLI_remlink((ListBase *)&(sc->edge_first), ed1);
@@ -741,11 +741,11 @@ static unsigned int scanfill(ScanFillContext *sf_ctx, PolyFill *pf, const int fl
ed3->v1->edge_tot++;
ed3->v2->edge_tot++;
- /* printf("add new edge %x %x\n", v1, v3); */
+ // printf("add new edge %x %x\n", v1, v3);
sc1 = addedgetoscanlist(scdata, ed3, verts);
if (sc1) { /* ed3 already exists: remove if a boundary */
- /* printf("Edge exists\n"); */
+ // printf("Edge exists\n");
ed3->v1->edge_tot--;
ed3->v2->edge_tot--;
@@ -954,7 +954,7 @@ unsigned int BLI_scanfill_calc_ex(ScanFillContext *sf_ctx, const int flag, const
poly++;
}
}
- /* printf("amount of poly's: %d\n", poly); */
+ // printf("amount of poly's: %d\n", poly);
}
else if (poly) {
/* we pre-calculated poly_nr */
@@ -1020,7 +1020,7 @@ unsigned int BLI_scanfill_calc_ex(ScanFillContext *sf_ctx, const int flag, const
}
}
if (BLI_listbase_is_empty(&sf_ctx->filledgebase)) {
- /* printf("All edges removed\n"); */
+ // printf("All edges removed\n");
return 0;
}
}
diff --git a/source/blender/blenlib/intern/storage.c b/source/blender/blenlib/intern/storage.c
index 19b925535e2..47bb2f0e8dd 100644
--- a/source/blender/blenlib/intern/storage.c
+++ b/source/blender/blenlib/intern/storage.c
@@ -114,7 +114,7 @@ double BLI_dir_free_space(const char *dir)
tmp[0] = '\\';
tmp[1] = 0; /* Just a fail-safe. */
- if (ELEM(dir[0] == '/', '\\')) {
+ if (ELEM(dir[0], '/', '\\')) {
tmp[0] = '\\';
tmp[1] = 0;
}
diff --git a/source/blender/blenlib/tests/BLI_array_store_test.cc b/source/blender/blenlib/tests/BLI_array_store_test.cc
index 8bbd109fb81..89aeccdc105 100644
--- a/source/blender/blenlib/tests/BLI_array_store_test.cc
+++ b/source/blender/blenlib/tests/BLI_array_store_test.cc
@@ -187,17 +187,6 @@ static void testbuffer_list_state_from_data__stride_expand(ListBase *lb,
((void)0)
/* test in both directions */
-#define TESTBUFFER_STRINGS_EX(bs, ...) \
- { \
- ListBase lb; \
- TESTBUFFER_STRINGS_CREATE(&lb, __VA_ARGS__); \
-\
- testbuffer_run_tests(bs, &lb); \
-\
- testbuffer_list_free(&lb); \
- } \
- ((void)0)
-
#define TESTBUFFER_STRINGS(stride, chunk_count, ...) \
{ \
ListBase lb; \
diff --git a/source/blender/blenlib/tests/BLI_math_vector_test.cc b/source/blender/blenlib/tests/BLI_math_vector_test.cc
index 7e75a521d4c..955f02b8065 100644
--- a/source/blender/blenlib/tests/BLI_math_vector_test.cc
+++ b/source/blender/blenlib/tests/BLI_math_vector_test.cc
@@ -45,3 +45,21 @@ TEST(math_vector, ClampVecWithVecs)
EXPECT_FLOAT_EQ(1.0f, c[0]);
EXPECT_FLOAT_EQ(3.0f, c[1]);
}
+
+TEST(math_vector, test_invert_v3_safe)
+{
+ float v3_with_zeroes[3] = {0.0f, 2.0f, 3.0f};
+ invert_v3_safe(v3_with_zeroes);
+ EXPECT_FLOAT_EQ(0.0f, v3_with_zeroes[0]);
+ EXPECT_FLOAT_EQ(0.5f, v3_with_zeroes[1]);
+ EXPECT_FLOAT_EQ(0.33333333333f, v3_with_zeroes[2]);
+
+ float v3_without_zeroes[3] = {1.0f, 2.0f, 3.0f};
+ float inverted_unsafe[3] = {1.0f, 2.0f, 3.0f};
+ invert_v3_safe(v3_without_zeroes);
+ invert_v3(inverted_unsafe);
+
+ EXPECT_FLOAT_EQ(inverted_unsafe[0], v3_without_zeroes[0]);
+ EXPECT_FLOAT_EQ(inverted_unsafe[1], v3_without_zeroes[1]);
+ EXPECT_FLOAT_EQ(inverted_unsafe[2], v3_without_zeroes[2]);
+}
diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h
index 04e13fbd1d6..dbdb181281a 100644
--- a/source/blender/blenloader/BLO_readfile.h
+++ b/source/blender/blenloader/BLO_readfile.h
@@ -108,6 +108,9 @@ typedef struct BlendFileReadReport {
* during this file read. */
int missing_libraries;
int missing_linked_id;
+ /* Some sub-categories of the above `missing_linked_id` counter. */
+ int missing_obdata;
+ int missing_obproxies;
/* Number of root override IDs that were resynced. */
int resynced_lib_overrides;
} count;
@@ -115,6 +118,7 @@ typedef struct BlendFileReadReport {
/* Number of libraries which had overrides that needed to be resynced, and a single linked list
* of those. */
int resynced_lib_overrides_libraries_count;
+ bool do_resynced_lib_overrides_libraries_list;
struct LinkNode *resynced_lib_overrides_libraries;
} BlendFileReadReport;
diff --git a/source/blender/blenloader/BLO_undofile.h b/source/blender/blenloader/BLO_undofile.h
index fc41a6e832f..4e240e2462b 100644
--- a/source/blender/blenloader/BLO_undofile.h
+++ b/source/blender/blenloader/BLO_undofile.h
@@ -24,6 +24,8 @@
* \ingroup blenloader
*/
+#include "BLI_filereader.h"
+
struct GHash;
struct Scene;
@@ -65,6 +67,16 @@ typedef struct MemFileUndoData {
size_t undo_size;
} MemFileUndoData;
+/* FileReader-compatible wrapper for reading MemFiles */
+typedef struct {
+ FileReader reader;
+
+ MemFile *memfile;
+ int undo_direction;
+
+ bool memchunk_identical;
+} UndoReader;
+
/* actually only used writefile.c */
void BLO_memfile_write_init(MemFileWriteData *mem_data,
@@ -84,3 +96,5 @@ extern struct Main *BLO_memfile_main_get(struct MemFile *memfile,
struct Main *bmain,
struct Scene **r_scene);
extern bool BLO_memfile_write_file(struct MemFile *memfile, const char *filename);
+
+FileReader *BLO_memfile_new_filereader(MemFile *memfile, int undo_direction);
diff --git a/source/blender/blenloader/CMakeLists.txt b/source/blender/blenloader/CMakeLists.txt
index f5baf0dcb83..89631588ed0 100644
--- a/source/blender/blenloader/CMakeLists.txt
+++ b/source/blender/blenloader/CMakeLists.txt
@@ -42,7 +42,7 @@ set(INC
)
set(INC_SYS
- ${ZLIB_INCLUDE_DIRS}
+ ${ZSTD_INCLUDE_DIRS}
)
set(SRC
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index e48c305fc4b..49c3497f996 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -21,8 +21,6 @@
* \ingroup blenloader
*/
-#include "zlib.h"
-
#include <ctype.h> /* for isdigit. */
#include <fcntl.h> /* for open flags (O_BINARY, O_RDONLY). */
#include <limits.h>
@@ -71,7 +69,6 @@
#include "BLI_math.h"
#include "BLI_memarena.h"
#include "BLI_mempool.h"
-#include "BLI_mmap.h"
#include "BLI_threads.h"
#include "PIL_time.h"
@@ -788,7 +785,7 @@ static BHeadN *get_bhead(FileData *fd)
*/
if (fd->flags & FD_FLAGS_FILE_POINTSIZE_IS_4) {
bhead4.code = DATA;
- readsize = fd->read(fd, &bhead4, sizeof(bhead4), NULL);
+ readsize = fd->file->read(fd->file, &bhead4, sizeof(bhead4));
if (readsize == sizeof(bhead4) || bhead4.code == ENDB) {
if (fd->flags & FD_FLAGS_SWITCH_ENDIAN) {
@@ -811,7 +808,7 @@ static BHeadN *get_bhead(FileData *fd)
}
else {
bhead8.code = DATA;
- readsize = fd->read(fd, &bhead8, sizeof(bhead8), NULL);
+ readsize = fd->file->read(fd->file, &bhead8, sizeof(bhead8));
if (readsize == sizeof(bhead8) || bhead8.code == ENDB) {
if (fd->flags & FD_FLAGS_SWITCH_ENDIAN) {
@@ -845,22 +842,22 @@ static BHeadN *get_bhead(FileData *fd)
/* pass */
}
#ifdef USE_BHEAD_READ_ON_DEMAND
- else if (fd->seek != NULL && BHEAD_USE_READ_ON_DEMAND(&bhead)) {
+ else if (fd->file->seek != NULL && BHEAD_USE_READ_ON_DEMAND(&bhead)) {
/* Delay reading bhead content. */
new_bhead = MEM_mallocN(sizeof(BHeadN), "new_bhead");
if (new_bhead) {
new_bhead->next = new_bhead->prev = NULL;
- new_bhead->file_offset = fd->file_offset;
+ new_bhead->file_offset = fd->file->offset;
new_bhead->has_data = false;
new_bhead->is_memchunk_identical = false;
new_bhead->bhead = bhead;
- off64_t seek_new = fd->seek(fd, bhead.len, SEEK_CUR);
+ off64_t seek_new = fd->file->seek(fd->file, bhead.len, SEEK_CUR);
if (seek_new == -1) {
fd->is_eof = true;
MEM_freeN(new_bhead);
new_bhead = NULL;
}
- BLI_assert(fd->file_offset == seek_new);
+ BLI_assert(fd->file->offset == seek_new);
}
else {
fd->is_eof = true;
@@ -878,14 +875,17 @@ static BHeadN *get_bhead(FileData *fd)
new_bhead->is_memchunk_identical = false;
new_bhead->bhead = bhead;
- readsize = fd->read(
- fd, new_bhead + 1, (size_t)bhead.len, &new_bhead->is_memchunk_identical);
+ readsize = fd->file->read(fd->file, new_bhead + 1, (size_t)bhead.len);
- if (readsize != (ssize_t)bhead.len) {
+ if (readsize != bhead.len) {
fd->is_eof = true;
MEM_freeN(new_bhead);
new_bhead = NULL;
}
+
+ if (fd->flags & FD_FLAGS_IS_MEMFILE) {
+ new_bhead->is_memchunk_identical = ((UndoReader *)fd->file)->memchunk_identical;
+ }
}
else {
fd->is_eof = true;
@@ -964,17 +964,19 @@ static bool blo_bhead_read_data(FileData *fd, BHead *thisblock, void *buf)
bool success = true;
BHeadN *new_bhead = BHEADN_FROM_BHEAD(thisblock);
BLI_assert(new_bhead->has_data == false && new_bhead->file_offset != 0);
- off64_t offset_backup = fd->file_offset;
- if (UNLIKELY(fd->seek(fd, new_bhead->file_offset, SEEK_SET) == -1)) {
+ off64_t offset_backup = fd->file->offset;
+ if (UNLIKELY(fd->file->seek(fd->file, new_bhead->file_offset, SEEK_SET) == -1)) {
success = false;
}
else {
- if (fd->read(fd, buf, (size_t)new_bhead->bhead.len, &new_bhead->is_memchunk_identical) !=
- (ssize_t)new_bhead->bhead.len) {
+ if (fd->file->read(fd->file, buf, (size_t)new_bhead->bhead.len) != new_bhead->bhead.len) {
success = false;
}
+ if (fd->flags & FD_FLAGS_IS_MEMFILE) {
+ new_bhead->is_memchunk_identical = ((UndoReader *)fd->file)->memchunk_identical;
+ }
}
- if (fd->seek(fd, offset_backup, SEEK_SET) == -1) {
+ if (fd->file->seek(fd->file, offset_backup, SEEK_SET) == -1) {
success = false;
}
return success;
@@ -1017,7 +1019,7 @@ static void decode_blender_header(FileData *fd)
ssize_t readsize;
/* read in the header data */
- readsize = fd->read(fd, header, sizeof(header), NULL);
+ readsize = fd->file->read(fd->file, header, sizeof(header));
if (readsize == sizeof(header) && STREQLEN(header, "BLENDER", 7) && ELEM(header[7], '_', '-') &&
ELEM(header[8], 'v', 'V') &&
@@ -1147,210 +1149,12 @@ static int *read_file_thumbnail(FileData *fd)
/** \} */
-/* -------------------------------------------------------------------- */
-/** \name File Data API
- * \{ */
-
-/* Regular file reading. */
-
-static ssize_t fd_read_data_from_file(FileData *filedata,
- void *buffer,
- size_t size,
- bool *UNUSED(r_is_memchunck_identical))
-{
- ssize_t readsize = read(filedata->filedes, buffer, size);
-
- if (readsize < 0) {
- readsize = EOF;
- }
- else {
- filedata->file_offset += readsize;
- }
-
- return readsize;
-}
-
-static off64_t fd_seek_data_from_file(FileData *filedata, off64_t offset, int whence)
-{
- filedata->file_offset = BLI_lseek(filedata->filedes, offset, whence);
- return filedata->file_offset;
-}
-
-/* GZip file reading. */
-
-static ssize_t fd_read_gzip_from_file(FileData *filedata,
- void *buffer,
- size_t size,
- bool *UNUSED(r_is_memchunck_identical))
-{
- BLI_assert(size <= INT_MAX);
-
- ssize_t readsize = gzread(filedata->gzfiledes, buffer, (uint)size);
-
- if (readsize < 0) {
- readsize = EOF;
- }
- else {
- filedata->file_offset += readsize;
- }
-
- return readsize;
-}
-
-/* Memory reading. */
-
-static ssize_t fd_read_from_memory(FileData *filedata,
- void *buffer,
- size_t size,
- bool *UNUSED(r_is_memchunck_identical))
-{
- /* don't read more bytes than there are available in the buffer */
- ssize_t readsize = (ssize_t)MIN2(size, filedata->buffersize - (size_t)filedata->file_offset);
-
- memcpy(buffer, filedata->buffer + filedata->file_offset, (size_t)readsize);
- filedata->file_offset += readsize;
-
- return readsize;
-}
-
-/* Memory-mapped file reading.
- * By using mmap(), we can map a file so that it can be treated like normal memory,
- * meaning that we can just read from it with memcpy() etc.
- * This avoids system call overhead and can significantly speed up file loading.
- */
-
-static ssize_t fd_read_from_mmap(FileData *filedata,
- void *buffer,
- size_t size,
- bool *UNUSED(r_is_memchunck_identical))
-{
- /* don't read more bytes than there are available in the buffer */
- size_t readsize = MIN2(size, (size_t)(filedata->buffersize - filedata->file_offset));
-
- if (!BLI_mmap_read(filedata->mmap_file, buffer, filedata->file_offset, readsize)) {
- return 0;
- }
-
- filedata->file_offset += readsize;
-
- return readsize;
-}
-
-static off64_t fd_seek_from_mmap(FileData *filedata, off64_t offset, int whence)
-{
- off64_t new_pos;
- if (whence == SEEK_CUR) {
- new_pos = filedata->file_offset + offset;
- }
- else if (whence == SEEK_SET) {
- new_pos = offset;
- }
- else if (whence == SEEK_END) {
- new_pos = filedata->buffersize + offset;
- }
- else {
- return -1;
- }
-
- if (new_pos < 0 || new_pos > filedata->buffersize) {
- return -1;
- }
-
- filedata->file_offset = new_pos;
- return filedata->file_offset;
-}
-
-/* MemFile reading. */
-
-static ssize_t fd_read_from_memfile(FileData *filedata,
- void *buffer,
- size_t size,
- bool *r_is_memchunck_identical)
-{
- static size_t seek = SIZE_MAX; /* the current position */
- static size_t offset = 0; /* size of previous chunks */
- static MemFileChunk *chunk = NULL;
- size_t chunkoffset, readsize, totread;
-
- if (r_is_memchunck_identical != NULL) {
- *r_is_memchunck_identical = true;
- }
-
- if (size == 0) {
- return 0;
- }
-
- if (seek != (size_t)filedata->file_offset) {
- chunk = filedata->memfile->chunks.first;
- seek = 0;
-
- while (chunk) {
- if (seek + chunk->size > (size_t)filedata->file_offset) {
- break;
- }
- seek += chunk->size;
- chunk = chunk->next;
- }
- offset = seek;
- seek = (size_t)filedata->file_offset;
- }
-
- if (chunk) {
- totread = 0;
-
- do {
- /* first check if it's on the end if current chunk */
- if (seek - offset == chunk->size) {
- offset += chunk->size;
- chunk = chunk->next;
- }
-
- /* debug, should never happen */
- if (chunk == NULL) {
- CLOG_ERROR(&LOG, "Illegal read, got a NULL chunk");
- return 0;
- }
-
- chunkoffset = seek - offset;
- readsize = size - totread;
-
- /* data can be spread over multiple chunks, so clamp size
- * to within this chunk, and then it will read further in
- * the next chunk */
- if (chunkoffset + readsize > chunk->size) {
- readsize = chunk->size - chunkoffset;
- }
-
- memcpy(POINTER_OFFSET(buffer, totread), chunk->buf + chunkoffset, readsize);
- totread += readsize;
- filedata->file_offset += readsize;
- seek += readsize;
- if (r_is_memchunck_identical != NULL) {
- /* `is_identical` of current chunk represents whether it changed compared to previous undo
- * step. this is fine in redo case, but not in undo case, where we need an extra flag
- * defined when saving the next (future) step after the one we want to restore, as we are
- * supposed to 'come from' that future undo step, and not the one before current one. */
- *r_is_memchunck_identical &= filedata->undo_direction == STEP_REDO ?
- chunk->is_identical :
- chunk->is_identical_future;
- }
- } while (totread < size);
-
- return (ssize_t)totread;
- }
-
- return 0;
-}
-
static FileData *filedata_new(BlendFileReadReport *reports)
{
BLI_assert(reports != NULL);
FileData *fd = MEM_callocN(sizeof(FileData), "FileData");
- fd->filedes = -1;
- fd->gzfiledes = NULL;
-
fd->memsdna = DNA_sdna_current_get();
fd->datamap = oldnewmap_new();
@@ -1387,78 +1191,66 @@ static FileData *blo_decode_and_check(FileData *fd, ReportList *reports)
static FileData *blo_filedata_from_file_descriptor(const char *filepath,
BlendFileReadReport *reports,
- int file)
+ int filedes)
{
- FileDataReadFn *read_fn = NULL;
- FileDataSeekFn *seek_fn = NULL; /* Optional. */
- size_t buffersize = 0;
- BLI_mmap_file *mmap_file = NULL;
-
- gzFile gzfile = (gzFile)Z_NULL;
-
char header[7];
+ FileReader *rawfile = BLI_filereader_new_file(filedes);
+ FileReader *file = NULL;
- /* Regular file. */
errno = 0;
- if (read(file, header, sizeof(header)) != sizeof(header)) {
+ /* If opening the file failed or we can't read the header, give up. */
+ if (rawfile == NULL || rawfile->read(rawfile, header, sizeof(header)) != sizeof(header)) {
BKE_reportf(reports->reports,
RPT_WARNING,
"Unable to read '%s': %s",
filepath,
errno ? strerror(errno) : TIP_("insufficient content"));
+ if (rawfile) {
+ rawfile->close(rawfile);
+ }
+ else {
+ close(filedes);
+ }
return NULL;
}
- /* Regular file. */
- if (memcmp(header, "BLENDER", sizeof(header)) == 0) {
- read_fn = fd_read_data_from_file;
- seek_fn = fd_seek_data_from_file;
+ /* Rewind the file after reading the header. */
+ rawfile->seek(rawfile, 0, SEEK_SET);
- mmap_file = BLI_mmap_open(file);
- if (mmap_file != NULL) {
- read_fn = fd_read_from_mmap;
- seek_fn = fd_seek_from_mmap;
- buffersize = BLI_lseek(file, 0, SEEK_END);
+ /* Check if we have a regular file. */
+ if (memcmp(header, "BLENDER", sizeof(header)) == 0) {
+ /* Try opening the file with memory-mapped IO. */
+ file = BLI_filereader_new_mmap(filedes);
+ if (file == NULL) {
+ /* mmap failed, so just keep using rawfile. */
+ file = rawfile;
+ rawfile = NULL;
}
}
-
- BLI_lseek(file, 0, SEEK_SET);
-
- /* Gzip file. */
- errno = 0;
- if ((read_fn == NULL) &&
- /* Check header magic. */
- (header[0] == 0x1f && header[1] == 0x8b)) {
- gzfile = BLI_gzopen(filepath, "rb");
- if (gzfile == (gzFile)Z_NULL) {
- BKE_reportf(reports->reports,
- RPT_WARNING,
- "Unable to open '%s': %s",
- filepath,
- errno ? strerror(errno) : TIP_("unknown error reading file"));
- return NULL;
+ else if (BLI_file_magic_is_gzip(header)) {
+ file = BLI_filereader_new_gzip(rawfile);
+ if (file != NULL) {
+ rawfile = NULL; /* The Gzip FileReader takes ownership of `rawfile`. */
+ }
+ }
+ else if (BLI_file_magic_is_zstd(header)) {
+ file = BLI_filereader_new_zstd(rawfile);
+ if (file != NULL) {
+ rawfile = NULL; /* The Zstd FileReader takes ownership of `rawfile`. */
}
-
- /* 'seek_fn' is too slow for gzip, don't set it. */
- read_fn = fd_read_gzip_from_file;
- /* Caller must close. */
- file = -1;
}
- if (read_fn == NULL) {
+ /* Clean up `rawfile` if it wasn't taken over. */
+ if (rawfile != NULL) {
+ rawfile->close(rawfile);
+ }
+ if (file == NULL) {
BKE_reportf(reports->reports, RPT_WARNING, "Unrecognized file format '%s'", filepath);
return NULL;
}
FileData *fd = filedata_new(reports);
-
- fd->filedes = file;
- fd->gzfiledes = gzfile;
-
- fd->read = read_fn;
- fd->seek = seek_fn;
- fd->mmap_file = mmap_file;
- fd->buffersize = buffersize;
+ fd->file = file;
return fd;
}
@@ -1475,11 +1267,7 @@ static FileData *blo_filedata_from_file_open(const char *filepath, BlendFileRead
errno ? strerror(errno) : TIP_("unknown error reading file"));
return NULL;
}
- FileData *fd = blo_filedata_from_file_descriptor(filepath, reports, file);
- if ((fd == NULL) || (fd->filedes == -1)) {
- close(file);
- }
- return fd;
+ return blo_filedata_from_file_descriptor(filepath, reports, file);
}
/* cannot be called with relative paths anymore! */
@@ -1513,50 +1301,6 @@ static FileData *blo_filedata_from_file_minimal(const char *filepath)
return NULL;
}
-static ssize_t fd_read_gzip_from_memory(FileData *filedata,
- void *buffer,
- size_t size,
- bool *UNUSED(r_is_memchunck_identical))
-{
- int err;
-
- filedata->strm.next_out = (Bytef *)buffer;
- filedata->strm.avail_out = (uint)size;
-
- /* Inflate another chunk. */
- err = inflate(&filedata->strm, Z_SYNC_FLUSH);
-
- if (err == Z_STREAM_END) {
- return 0;
- }
- if (err != Z_OK) {
- CLOG_ERROR(&LOG, "ZLib error (code %d)", err);
- return 0;
- }
-
- filedata->file_offset += size;
-
- return (ssize_t)size;
-}
-
-static int fd_read_gzip_from_memory_init(FileData *fd)
-{
-
- fd->strm.next_in = (Bytef *)fd->buffer;
- fd->strm.avail_in = fd->buffersize;
- fd->strm.total_out = 0;
- fd->strm.zalloc = Z_NULL;
- fd->strm.zfree = Z_NULL;
-
- if (inflateInit2(&fd->strm, (16 + MAX_WBITS)) != Z_OK) {
- return 0;
- }
-
- fd->read = fd_read_gzip_from_memory;
-
- return 1;
-}
-
FileData *blo_filedata_from_memory(const void *mem, int memsize, BlendFileReadReport *reports)
{
if (!mem || memsize < SIZEOFBLENDERHEADER) {
@@ -1565,24 +1309,24 @@ FileData *blo_filedata_from_memory(const void *mem, int memsize, BlendFileReadRe
return NULL;
}
- FileData *fd = filedata_new(reports);
- const char *cp = mem;
-
- fd->buffer = mem;
- fd->buffersize = memsize;
+ FileReader *mem_file = BLI_filereader_new_memory(mem, memsize);
+ FileReader *file = mem_file;
- /* test if gzip */
- if (cp[0] == 0x1f && cp[1] == 0x8b) {
- if (0 == fd_read_gzip_from_memory_init(fd)) {
- blo_filedata_free(fd);
- return NULL;
- }
+ if (BLI_file_magic_is_gzip(mem)) {
+ file = BLI_filereader_new_gzip(mem_file);
}
- else {
- fd->read = fd_read_from_memory;
+ else if (BLI_file_magic_is_zstd(mem)) {
+ file = BLI_filereader_new_zstd(mem_file);
}
- fd->flags |= FD_FLAGS_NOT_MY_BUFFER;
+ if (file == NULL) {
+ /* Compression initialization failed. */
+ mem_file->close(mem_file);
+ return NULL;
+ }
+
+ FileData *fd = filedata_new(reports);
+ fd->file = file;
return blo_decode_and_check(fd, reports->reports);
}
@@ -1597,11 +1341,9 @@ FileData *blo_filedata_from_memfile(MemFile *memfile,
}
FileData *fd = filedata_new(reports);
- fd->memfile = memfile;
+ fd->file = BLO_memfile_new_filereader(memfile, params->undo_direction);
fd->undo_direction = params->undo_direction;
-
- fd->read = fd_read_from_memfile;
- fd->flags |= FD_FLAGS_NOT_MY_BUFFER;
+ fd->flags |= FD_FLAGS_IS_MEMFILE;
return blo_decode_and_check(fd, reports->reports);
}
@@ -1609,30 +1351,7 @@ FileData *blo_filedata_from_memfile(MemFile *memfile,
void blo_filedata_free(FileData *fd)
{
if (fd) {
- if (fd->filedes != -1) {
- close(fd->filedes);
- }
-
- if (fd->gzfiledes != NULL) {
- gzclose(fd->gzfiledes);
- }
-
- if (fd->strm.next_in) {
- int err = inflateEnd(&fd->strm);
- if (err != Z_OK) {
- CLOG_ERROR(&LOG, "Close gzip stream error (code %d)", err);
- }
- }
-
- if (fd->buffer && !(fd->flags & FD_FLAGS_NOT_MY_BUFFER)) {
- MEM_freeN((void *)fd->buffer);
- fd->buffer = NULL;
- }
-
- if (fd->mmap_file) {
- BLI_mmap_free(fd->mmap_file);
- fd->mmap_file = NULL;
- }
+ fd->file->close(fd->file);
/* Free all BHeadN data blocks */
#ifndef NDEBUG
@@ -1640,7 +1359,7 @@ void blo_filedata_free(FileData *fd)
#else
/* Sanity check we're not keeping memory we don't need. */
LISTBASE_FOREACH_MUTABLE (BHeadN *, new_bhead, &fd->bhead_list) {
- if (fd->seek != NULL && BHEAD_USE_READ_ON_DEMAND(&new_bhead->bhead)) {
+ if (fd->file->seek != NULL && BHEAD_USE_READ_ON_DEMAND(&new_bhead->bhead)) {
BLI_assert(new_bhead->has_data == 0);
}
MEM_freeN(new_bhead);
@@ -2096,7 +1815,7 @@ static void blo_cache_storage_entry_clear_in_old(ID *UNUSED(id),
void blo_cache_storage_init(FileData *fd, Main *bmain)
{
- if (fd->memfile != NULL) {
+ if (fd->flags & FD_FLAGS_IS_MEMFILE) {
BLI_assert(fd->cache_storage == NULL);
fd->cache_storage = MEM_mallocN(sizeof(*fd->cache_storage), __func__);
fd->cache_storage->memarena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
@@ -2261,7 +1980,7 @@ static void *read_struct(FileData *fd, BHead *bh, const char *blockname)
* undo since DNA must match. */
static const void *peek_struct_undo(FileData *fd, BHead *bhead)
{
- BLI_assert(fd->memfile != NULL);
+ BLI_assert(fd->flags & FD_FLAGS_IS_MEMFILE);
UNUSED_VARS_NDEBUG(fd);
return (bhead->len) ? (const void *)(bhead + 1) : NULL;
}
@@ -3679,7 +3398,7 @@ static BHead *read_libblock(FileData *fd,
* When datablocks are changed but still exist, we restore them at the old
* address and inherit recalc flags for the dependency graph. */
ID *id_old = NULL;
- if (fd->memfile != NULL) {
+ if (fd->flags & FD_FLAGS_IS_MEMFILE) {
if (read_libblock_undo_restore(fd, main, bhead, tag, &id_old)) {
if (r_id) {
*r_id = id_old;
@@ -3980,13 +3699,14 @@ static void lib_link_all(FileData *fd, Main *bmain)
continue;
}
- if (fd->memfile != NULL && GS(id->name) == ID_WM) {
+ if ((fd->flags & FD_FLAGS_IS_MEMFILE) && GS(id->name) == ID_WM) {
/* No load UI for undo memfiles.
* Only WM currently, SCR needs it still (see below), and so does WS? */
continue;
}
- if (fd->memfile != NULL && do_partial_undo && (id->tag & LIB_TAG_UNDO_OLD_ID_REUSED) != 0) {
+ if ((fd->flags & FD_FLAGS_IS_MEMFILE) && do_partial_undo &&
+ (id->tag & LIB_TAG_UNDO_OLD_ID_REUSED) != 0) {
/* This ID has been re-used from 'old' bmain. Since it was therefore unchanged across
* current undo step, and old IDs re-use their old memory address, we do not need to liblink
* it at all. */
@@ -4165,7 +3885,7 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath)
BlendFileData *bfd;
ListBase mainlist = {NULL, NULL};
- if (fd->memfile != NULL) {
+ if (fd->flags & FD_FLAGS_IS_MEMFILE) {
CLOG_INFO(&LOG_UNDO, 2, "UNDO: read step");
}
@@ -4256,7 +3976,7 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath)
}
/* do before read_libraries, but skip undo case */
- if (fd->memfile == NULL) {
+ if ((fd->flags & FD_FLAGS_IS_MEMFILE) == 0) {
if ((fd->skip_flags & BLO_READ_SKIP_DATA) == 0) {
do_versions(fd, NULL, bfd->main);
}
@@ -4278,7 +3998,7 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath)
fd->reports->duration.libraries = PIL_check_seconds_timer() - fd->reports->duration.libraries;
/* Skip in undo case. */
- if (fd->memfile == NULL) {
+ if ((fd->flags & FD_FLAGS_IS_MEMFILE) == 0) {
/* Note that we can't recompute user-counts at this point in undo case, we play too much with
* IDs from different memory realms, and Main database is not in a fully valid state yet.
*/
@@ -4311,7 +4031,7 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath)
/* Now that all our data-blocks are loaded,
* we can re-generate overrides from their references. */
- if (fd->memfile == NULL) {
+ if ((fd->flags & FD_FLAGS_IS_MEMFILE) == 0) {
/* Do not apply in undo case! */
fd->reports->duration.lib_overrides = PIL_check_seconds_timer();
@@ -4391,7 +4111,7 @@ static void sort_bhead_old_map(FileData *fd)
static BHead *find_previous_lib(FileData *fd, BHead *bhead)
{
/* Skip library data-blocks in undo, see comment in read_libblock. */
- if (fd->memfile) {
+ if (fd->flags & FD_FLAGS_IS_MEMFILE) {
return NULL;
}
@@ -5850,7 +5570,7 @@ void BLO_read_pointer_array(BlendDataReader *reader, void **ptr_p)
bool BLO_read_data_is_undo(BlendDataReader *reader)
{
- return reader->fd->memfile != NULL;
+ return (reader->fd->flags & FD_FLAGS_IS_MEMFILE);
}
void BLO_read_data_globmap_add(BlendDataReader *reader, void *oldaddr, void *newaddr)
@@ -5870,7 +5590,7 @@ BlendFileReadReport *BLO_read_data_reports(BlendDataReader *reader)
bool BLO_read_lib_is_undo(BlendLibReader *reader)
{
- return reader->fd->memfile != NULL;
+ return (reader->fd->flags & FD_FLAGS_IS_MEMFILE);
}
Main *BLO_read_lib_get_main(BlendLibReader *reader)
diff --git a/source/blender/blenloader/intern/readfile.h b/source/blender/blenloader/intern/readfile.h
index b04043f9641..beeed8e45ae 100644
--- a/source/blender/blenloader/intern/readfile.h
+++ b/source/blender/blenloader/intern/readfile.h
@@ -28,10 +28,10 @@
# include "BLI_winstuff.h"
#endif
+#include "BLI_filereader.h"
#include "DNA_sdna_types.h"
#include "DNA_space_types.h"
#include "DNA_windowmanager_types.h" /* for ReportType */
-#include "zlib.h"
struct BLI_mmap_file;
struct BLOCacheStorage;
@@ -50,7 +50,7 @@ enum eFileDataFlag {
FD_FLAGS_FILE_POINTSIZE_IS_4 = 1 << 1,
FD_FLAGS_POINTSIZE_DIFFERS = 1 << 2,
FD_FLAGS_FILE_OK = 1 << 3,
- FD_FLAGS_NOT_MY_BUFFER = 1 << 4,
+ FD_FLAGS_IS_MEMFILE = 1 << 4,
/* XXX Unused in practice (checked once but never set). */
FD_FLAGS_NOT_MY_LIBMAP = 1 << 5,
};
@@ -60,44 +60,18 @@ enum eFileDataFlag {
# pragma GCC poison off_t
#endif
-#if defined(_MSC_VER) || defined(__APPLE__) || defined(__HAIKU__) || defined(__NetBSD__)
-typedef int64_t off64_t;
-#endif
-
-typedef ssize_t(FileDataReadFn)(struct FileData *filedata,
- void *buffer,
- size_t size,
- bool *r_is_memchunk_identical);
-typedef off64_t(FileDataSeekFn)(struct FileData *filedata, off64_t offset, int whence);
-
typedef struct FileData {
/** Linked list of BHeadN's. */
ListBase bhead_list;
enum eFileDataFlag flags;
bool is_eof;
- size_t buffersize;
- off64_t file_offset;
- FileDataReadFn *read;
- FileDataSeekFn *seek;
+ FileReader *file;
- /** Regular file reading. */
- int filedes;
-
- /** Variables needed for reading from memory / stream / memory-mapped files. */
- const char *buffer;
- struct BLI_mmap_file *mmap_file;
- /** Variables needed for reading from memfile (undo). */
- struct MemFile *memfile;
/** Whether we are undoing (< 0) or redoing (> 0), used to choose which 'unchanged' flag to use
* to detect unchanged data from memfile. */
int undo_direction; /* eUndoStepDir */
- /** Variables needed for reading from file. */
- gzFile gzfiledes;
- /** Gzip stream for memory decompression. */
- z_stream strm;
-
/** Now only in use for library appending. */
char relabase[FILE_MAX];
diff --git a/source/blender/blenloader/intern/undofile.c b/source/blender/blenloader/intern/undofile.c
index 2eeeac2e8d7..62072cf7df5 100644
--- a/source/blender/blenloader/intern/undofile.c
+++ b/source/blender/blenloader/intern/undofile.c
@@ -48,6 +48,7 @@
#include "BKE_lib_id.h"
#include "BKE_main.h"
+#include "BKE_undo_system.h"
/* keep last */
#include "BLI_strict_flags.h"
@@ -273,3 +274,97 @@ bool BLO_memfile_write_file(struct MemFile *memfile, const char *filename)
}
return true;
}
+
+static ssize_t undo_read(FileReader *reader, void *buffer, size_t size)
+{
+ UndoReader *undo = (UndoReader *)reader;
+
+ static size_t seek = SIZE_MAX; /* The current position. */
+ static size_t offset = 0; /* Size of previous chunks. */
+ static MemFileChunk *chunk = NULL;
+ size_t chunkoffset, readsize, totread;
+
+ undo->memchunk_identical = true;
+
+ if (size == 0) {
+ return 0;
+ }
+
+ if (seek != (size_t)undo->reader.offset) {
+ chunk = undo->memfile->chunks.first;
+ seek = 0;
+
+ while (chunk) {
+ if (seek + chunk->size > (size_t)undo->reader.offset) {
+ break;
+ }
+ seek += chunk->size;
+ chunk = chunk->next;
+ }
+ offset = seek;
+ seek = (size_t)undo->reader.offset;
+ }
+
+ if (chunk) {
+ totread = 0;
+
+ do {
+ /* First check if it's on the end if current chunk. */
+ if (seek - offset == chunk->size) {
+ offset += chunk->size;
+ chunk = chunk->next;
+ }
+
+ /* Debug, should never happen. */
+ if (chunk == NULL) {
+ printf("illegal read, chunk zero\n");
+ return 0;
+ }
+
+ chunkoffset = seek - offset;
+ readsize = size - totread;
+
+ /* Data can be spread over multiple chunks, so clamp size
+ * to within this chunk, and then it will read further in
+ * the next chunk. */
+ if (chunkoffset + readsize > chunk->size) {
+ readsize = chunk->size - chunkoffset;
+ }
+
+ memcpy(POINTER_OFFSET(buffer, totread), chunk->buf + chunkoffset, readsize);
+ totread += readsize;
+ undo->reader.offset += (off64_t)readsize;
+ seek += readsize;
+
+ /* `is_identical` of current chunk represents whether it changed compared to previous undo
+ * step. this is fine in redo case, but not in undo case, where we need an extra flag
+ * defined when saving the next (future) step after the one we want to restore, as we are
+ * supposed to 'come from' that future undo step, and not the one before current one. */
+ undo->memchunk_identical &= undo->undo_direction == STEP_REDO ? chunk->is_identical :
+ chunk->is_identical_future;
+ } while (totread < size);
+
+ return (ssize_t)totread;
+ }
+
+ return 0;
+}
+
+static void undo_close(FileReader *reader)
+{
+ MEM_freeN(reader);
+}
+
+FileReader *BLO_memfile_new_filereader(MemFile *memfile, int undo_direction)
+{
+ UndoReader *undo = MEM_callocN(sizeof(UndoReader), __func__);
+
+ undo->memfile = memfile;
+ undo->undo_direction = undo_direction;
+
+ undo->reader.read = undo_read;
+ undo->reader.seek = NULL;
+ undo->reader.close = undo_close;
+
+ return (FileReader *)undo;
+}
diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c
index e56c1995363..436645c2241 100644
--- a/source/blender/blenloader/intern/versioning_250.c
+++ b/source/blender/blenloader/intern/versioning_250.c
@@ -23,8 +23,7 @@
#else
# include "BLI_winstuff.h"
# include "winsock2.h"
-# include <io.h> /* for open close read */
-# include <zlib.h> /* odd include order-issue */
+# include <io.h> /* for open close read */
#endif
/* allow readfile to use deprecated functionality */
diff --git a/source/blender/blenloader/intern/versioning_260.c b/source/blender/blenloader/intern/versioning_260.c
index 858f5d85a90..7c644fa3b55 100644
--- a/source/blender/blenloader/intern/versioning_260.c
+++ b/source/blender/blenloader/intern/versioning_260.c
@@ -2390,7 +2390,7 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain)
for (md = ob->modifiers.first; md; md = md->next) {
if (md->type == eModifierType_Triangulate) {
TriangulateModifierData *tmd = (TriangulateModifierData *)md;
- if ((tmd->flag & MOD_TRIANGULATE_BEAUTY)) {
+ if (tmd->flag & MOD_TRIANGULATE_BEAUTY) {
tmd->quad_method = MOD_TRIANGULATE_QUAD_BEAUTY;
tmd->ngon_method = MOD_TRIANGULATE_NGON_BEAUTY;
}
diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c
index af05c4b902f..9d65488e8d4 100644
--- a/source/blender/blenloader/intern/versioning_280.c
+++ b/source/blender/blenloader/intern/versioning_280.c
@@ -111,40 +111,13 @@
#include "BLO_readfile.h"
#include "readfile.h"
+#include "versioning_common.h"
+
#include "MEM_guardedalloc.h"
/* Make preferences read-only, use versioning_userdef.c. */
#define U (*((const UserDef *)&U))
-/**
- * Rename if the ID doesn't exist.
- */
-static ID *rename_id_for_versioning(Main *bmain,
- const short id_type,
- const char *name_src,
- const char *name_dst)
-{
- /* We can ignore libraries */
- ListBase *lb = which_libbase(bmain, id_type);
- ID *id = NULL;
- LISTBASE_FOREACH (ID *, idtest, lb) {
- if (idtest->lib == NULL) {
- if (STREQ(idtest->name + 2, name_src)) {
- id = idtest;
- }
- if (STREQ(idtest->name + 2, name_dst)) {
- return NULL;
- }
- }
- }
- if (id != NULL) {
- BLI_strncpy(id->name + 2, name_dst, sizeof(id->name) - 2);
- /* We know it's unique, this just sorts. */
- BLI_libblock_ensure_unique_name(bmain, id->name);
- }
- return id;
-}
-
static bScreen *screen_parent_find(const bScreen *screen)
{
/* Can avoid lookup if screen state isn't maximized/full
@@ -348,7 +321,7 @@ static void do_version_layer_collection_post(ViewLayer *view_layer,
lc->flag |= LAYER_COLLECTION_EXCLUDE;
}
if (enabled && !selectable) {
- lc->collection->flag |= COLLECTION_RESTRICT_SELECT;
+ lc->collection->flag |= COLLECTION_HIDE_SELECT;
}
}
@@ -477,7 +450,7 @@ static void do_version_layers_to_collections(Main *bmain, Scene *scene)
collections[layer] = collection;
if (!(scene->lay & (1 << layer))) {
- collection->flag |= COLLECTION_RESTRICT_VIEWPORT | COLLECTION_RESTRICT_RENDER;
+ collection->flag |= COLLECTION_HIDE_VIEWPORT | COLLECTION_HIDE_RENDER;
}
}
@@ -1225,7 +1198,7 @@ void do_versions_after_linking_280(Main *bmain, ReportList *UNUSED(reports))
/* Add fake user for all existing groups. */
id_fake_user_set(&collection->id);
- if (collection->flag & (COLLECTION_RESTRICT_VIEWPORT | COLLECTION_RESTRICT_RENDER)) {
+ if (collection->flag & (COLLECTION_HIDE_VIEWPORT | COLLECTION_HIDE_RENDER)) {
continue;
}
@@ -1256,8 +1229,7 @@ void do_versions_after_linking_280(Main *bmain, ReportList *UNUSED(reports))
char name[MAX_ID_NAME];
BLI_snprintf(name, sizeof(name), DATA_("Hidden %d"), coll_idx + 1);
*collection_hidden = BKE_collection_add(bmain, collection, name);
- (*collection_hidden)->flag |= COLLECTION_RESTRICT_VIEWPORT |
- COLLECTION_RESTRICT_RENDER;
+ (*collection_hidden)->flag |= COLLECTION_HIDE_VIEWPORT | COLLECTION_HIDE_RENDER;
}
BKE_collection_object_add(bmain, *collection_hidden, ob);
@@ -1679,32 +1651,32 @@ void do_versions_after_linking_280(Main *bmain, ReportList *UNUSED(reports))
Brush *brush;
Material *ma;
/* Pen Soft brush. */
- brush = (Brush *)rename_id_for_versioning(bmain, ID_BR, "Draw Soft", "Pencil Soft");
+ brush = (Brush *)do_versions_rename_id(bmain, ID_BR, "Draw Soft", "Pencil Soft");
if (brush) {
brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PEN;
}
- rename_id_for_versioning(bmain, ID_BR, "Draw Pencil", "Pencil");
- rename_id_for_versioning(bmain, ID_BR, "Draw Pen", "Pen");
- rename_id_for_versioning(bmain, ID_BR, "Draw Ink", "Ink Pen");
- rename_id_for_versioning(bmain, ID_BR, "Draw Noise", "Ink Pen Rough");
- rename_id_for_versioning(bmain, ID_BR, "Draw Marker", "Marker Bold");
- rename_id_for_versioning(bmain, ID_BR, "Draw Block", "Marker Chisel");
+ do_versions_rename_id(bmain, ID_BR, "Draw Pencil", "Pencil");
+ do_versions_rename_id(bmain, ID_BR, "Draw Pen", "Pen");
+ do_versions_rename_id(bmain, ID_BR, "Draw Ink", "Ink Pen");
+ do_versions_rename_id(bmain, ID_BR, "Draw Noise", "Ink Pen Rough");
+ do_versions_rename_id(bmain, ID_BR, "Draw Marker", "Marker Bold");
+ do_versions_rename_id(bmain, ID_BR, "Draw Block", "Marker Chisel");
ma = BLI_findstring(&bmain->materials, "Black", offsetof(ID, name) + 2);
if (ma && ma->gp_style) {
- rename_id_for_versioning(bmain, ID_MA, "Black", "Solid Stroke");
+ do_versions_rename_id(bmain, ID_MA, "Black", "Solid Stroke");
}
ma = BLI_findstring(&bmain->materials, "Red", offsetof(ID, name) + 2);
if (ma && ma->gp_style) {
- rename_id_for_versioning(bmain, ID_MA, "Red", "Squares Stroke");
+ do_versions_rename_id(bmain, ID_MA, "Red", "Squares Stroke");
}
ma = BLI_findstring(&bmain->materials, "Grey", offsetof(ID, name) + 2);
if (ma && ma->gp_style) {
- rename_id_for_versioning(bmain, ID_MA, "Grey", "Solid Fill");
+ do_versions_rename_id(bmain, ID_MA, "Grey", "Solid Fill");
}
ma = BLI_findstring(&bmain->materials, "Black Dots", offsetof(ID, name) + 2);
if (ma && ma->gp_style) {
- rename_id_for_versioning(bmain, ID_MA, "Black Dots", "Dots Stroke");
+ do_versions_rename_id(bmain, ID_MA, "Black Dots", "Dots Stroke");
}
brush = BLI_findstring(&bmain->brushes, "Pencil", offsetof(ID, name) + 2);
@@ -4110,9 +4082,8 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
if (!MAIN_VERSION_ATLEAST(bmain, 280, 75)) {
for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) {
if (scene->master_collection != NULL) {
- scene->master_collection->flag &= ~(COLLECTION_RESTRICT_VIEWPORT |
- COLLECTION_RESTRICT_SELECT |
- COLLECTION_RESTRICT_RENDER);
+ scene->master_collection->flag &= ~(COLLECTION_HIDE_VIEWPORT | COLLECTION_HIDE_SELECT |
+ COLLECTION_HIDE_RENDER);
}
UnitSettings *unit = &scene->unit;
diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c
index 09d43676b8f..7f7a2d97cbb 100644
--- a/source/blender/blenloader/intern/versioning_290.c
+++ b/source/blender/blenloader/intern/versioning_290.c
@@ -170,7 +170,7 @@ static void seq_convert_transform_crop(const Scene *scene,
int image_size_x = scene->r.xsch;
int image_size_y = scene->r.ysch;
- /* Hardcoded legacy bit-flags which has been removed. */
+ /* Hard-coded legacy bit-flags which has been removed. */
const uint32_t use_transform_flag = (1 << 16);
const uint32_t use_crop_flag = (1 << 17);
diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c
index 9aec18ea279..eb01bfbfb9c 100644
--- a/source/blender/blenloader/intern/versioning_300.c
+++ b/source/blender/blenloader/intern/versioning_300.c
@@ -22,6 +22,7 @@
#include "BLI_listbase.h"
#include "BLI_math_vector.h"
+#include "BLI_path_util.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
@@ -29,6 +30,7 @@
#include "DNA_armature_types.h"
#include "DNA_brush_types.h"
#include "DNA_collection_types.h"
+#include "DNA_constraint_types.h"
#include "DNA_curve_types.h"
#include "DNA_genfile.h"
#include "DNA_listBase.h"
@@ -383,6 +385,19 @@ static void do_version_bones_bbone_len_scale(ListBase *lb)
}
}
+static void do_version_constraints_spline_ik_joint_bindings(ListBase *lb)
+{
+ /* Binding array data could be freed without properly resetting its size data. */
+ LISTBASE_FOREACH (bConstraint *, con, lb) {
+ if (con->type == CONSTRAINT_TYPE_SPLINEIK) {
+ bSplineIKConstraint *data = (bSplineIKConstraint *)con->data;
+ if (data->points == NULL) {
+ data->numpoints = 0;
+ }
+ }
+ }
+}
+
/* NOLINTNEXTLINE: readability-function-size */
void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
{
@@ -643,12 +658,12 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
if (!DNA_struct_elem_find(
fd->filesdna, "WorkSpace", "AssetLibraryReference", "asset_library")) {
LISTBASE_FOREACH (WorkSpace *, workspace, &bmain->workspaces) {
- BKE_asset_library_reference_init_default(&workspace->asset_library);
+ BKE_asset_library_reference_init_default(&workspace->asset_library_ref);
}
}
if (!DNA_struct_elem_find(
- fd->filesdna, "FileAssetSelectParams", "AssetLibraryReference", "asset_library")) {
+ fd->filesdna, "FileAssetSelectParams", "AssetLibraryReference", "asset_library_ref")) {
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, space, &area->spacedata) {
@@ -657,7 +672,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
if (sfile->browse_mode != FILE_BROWSE_MODE_ASSETS) {
continue;
}
- BKE_asset_library_reference_init_default(&sfile->asset_params->asset_library);
+ BKE_asset_library_reference_init_default(&sfile->asset_params->asset_library_ref);
}
}
}
@@ -691,6 +706,76 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
+ /* Font names were copied directly into ID names, see: T90417. */
+ if (!MAIN_VERSION_ATLEAST(bmain, 300, 16)) {
+ ListBase *lb = which_libbase(bmain, ID_VF);
+ BKE_main_id_repair_duplicate_names_listbase(lb);
+ }
+
+ if (!MAIN_VERSION_ATLEAST(bmain, 300, 17)) {
+ if (!DNA_struct_elem_find(
+ fd->filesdna, "View3DOverlay", "float", "normals_constant_screen_size")) {
+ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
+ if (sl->spacetype == SPACE_VIEW3D) {
+ View3D *v3d = (View3D *)sl;
+ v3d->overlay.normals_constant_screen_size = 7.0f;
+ }
+ }
+ }
+ }
+ }
+
+ /* Fix SplineIK constraint's inconsistency between binding points array and its stored size. */
+ LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+ /* NOTE: Objects should never have SplineIK constraint, so no need to apply this fix on
+ * their constraints. */
+ if (ob->pose) {
+ LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
+ do_version_constraints_spline_ik_joint_bindings(&pchan->constraints);
+ }
+ }
+ }
+ }
+
+ if (!MAIN_VERSION_ATLEAST(bmain, 300, 18)) {
+ if (!DNA_struct_elem_find(
+ fd->filesdna, "WorkSpace", "AssetLibraryReference", "asset_library_ref")) {
+ LISTBASE_FOREACH (WorkSpace *, workspace, &bmain->workspaces) {
+ BKE_asset_library_reference_init_default(&workspace->asset_library_ref);
+ }
+ }
+
+ if (!DNA_struct_elem_find(
+ fd->filesdna, "FileAssetSelectParams", "AssetLibraryReference", "asset_library_ref")) {
+ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ LISTBASE_FOREACH (SpaceLink *, space, &area->spacedata) {
+ if (space->spacetype != SPACE_FILE) {
+ continue;
+ }
+
+ SpaceFile *sfile = (SpaceFile *)space;
+ if (sfile->browse_mode != FILE_BROWSE_MODE_ASSETS) {
+ continue;
+ }
+ BKE_asset_library_reference_init_default(&sfile->asset_params->asset_library_ref);
+ }
+ }
+ }
+ }
+
+ /* Previously, only text ending with `.py` would run, apply this logic
+ * to existing files so text that happens to have the "Register" enabled
+ * doesn't suddenly start running code on startup that was previously ignored. */
+ LISTBASE_FOREACH (Text *, text, &bmain->texts) {
+ if ((text->flags & TXT_ISSCRIPT) && !BLI_path_extension_check(text->id.name + 2, ".py")) {
+ text->flags &= ~TXT_ISSCRIPT;
+ }
+ }
+ }
+
/**
* Versioning code until next subversion bump goes here.
*
@@ -702,5 +787,23 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
*/
{
/* Keep this block, even when empty. */
+
+ /* Add node storage for subdivision surface node. */
+ FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
+ if (ntree->type == NTREE_GEOMETRY) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ if (node->type == GEO_NODE_SUBDIVISION_SURFACE) {
+ if (node->storage == NULL) {
+ NodeGeometrySubdivisionSurface *data = MEM_callocN(
+ sizeof(NodeGeometrySubdivisionSurface), __func__);
+ data->uv_smooth = SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES;
+ data->boundary_smooth = SUBSURF_BOUNDARY_SMOOTH_ALL;
+ node->storage = data;
+ }
+ }
+ }
+ }
+ }
+ FOREACH_NODETREE_END;
}
}
diff --git a/source/blender/blenloader/intern/versioning_common.cc b/source/blender/blenloader/intern/versioning_common.cc
index f5083b8e259..208c02b60d1 100644
--- a/source/blender/blenloader/intern/versioning_common.cc
+++ b/source/blender/blenloader/intern/versioning_common.cc
@@ -20,9 +20,15 @@
/* allow readfile to use deprecated functionality */
#define DNA_DEPRECATED_ALLOW
+#include <cstring>
+
#include "DNA_screen_types.h"
#include "BLI_listbase.h"
+#include "BLI_string.h"
+
+#include "BKE_lib_id.h"
+#include "BKE_main.h"
#include "MEM_guardedalloc.h"
@@ -48,3 +54,34 @@ ARegion *do_versions_add_region_if_not_found(ListBase *regionbase,
BLI_insertlinkafter(regionbase, link_after_region, new_region);
return new_region;
}
+
+/**
+ * Rename if the ID doesn't exist.
+ *
+ * \return the ID (if found).
+ */
+ID *do_versions_rename_id(Main *bmain,
+ const short id_type,
+ const char *name_src,
+ const char *name_dst)
+{
+ /* We can ignore libraries */
+ ListBase *lb = which_libbase(bmain, id_type);
+ ID *id = nullptr;
+ LISTBASE_FOREACH (ID *, idtest, lb) {
+ if (idtest->lib == nullptr) {
+ if (STREQ(idtest->name + 2, name_src)) {
+ id = idtest;
+ }
+ if (STREQ(idtest->name + 2, name_dst)) {
+ return nullptr;
+ }
+ }
+ }
+ if (id != nullptr) {
+ BLI_strncpy(id->name + 2, name_dst, sizeof(id->name) - 2);
+ /* We know it's unique, this just sorts. */
+ BLI_libblock_ensure_unique_name(bmain, id->name);
+ }
+ return id;
+}
diff --git a/source/blender/blenloader/intern/versioning_common.h b/source/blender/blenloader/intern/versioning_common.h
index a1769d4639e..47e0b74a3e4 100644
--- a/source/blender/blenloader/intern/versioning_common.h
+++ b/source/blender/blenloader/intern/versioning_common.h
@@ -22,6 +22,7 @@
struct ARegion;
struct ListBase;
+struct Main;
#ifdef __cplusplus
extern "C" {
@@ -32,6 +33,11 @@ struct ARegion *do_versions_add_region_if_not_found(struct ListBase *regionbase,
const char *name,
int link_after_region_type);
+ID *do_versions_rename_id(Main *bmain,
+ const short id_type,
+ const char *name_src,
+ const char *name_dst);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenloader/intern/versioning_cycles.c b/source/blender/blenloader/intern/versioning_cycles.c
index 94ee89c5120..90e6b43f02e 100644
--- a/source/blender/blenloader/intern/versioning_cycles.c
+++ b/source/blender/blenloader/intern/versioning_cycles.c
@@ -78,6 +78,12 @@ static IDProperty *cycles_properties_from_ID(ID *id)
return (idprop) ? IDP_GetPropertyTypeFromGroup(idprop, "cycles", IDP_GROUP) : NULL;
}
+static IDProperty *cycles_visibility_properties_from_ID(ID *id)
+{
+ IDProperty *idprop = IDP_GetProperties(id, false);
+ return (idprop) ? IDP_GetPropertyTypeFromGroup(idprop, "cycles_visibility", IDP_GROUP) : NULL;
+}
+
static IDProperty *cycles_properties_from_view_layer(ViewLayer *view_layer)
{
IDProperty *idprop = view_layer->id_properties;
@@ -1600,4 +1606,35 @@ void do_versions_after_linking_cycles(Main *bmain)
}
}
}
+
+ /* Move visibility from Cycles to Blender. */
+ if (!MAIN_VERSION_ATLEAST(bmain, 300, 17)) {
+ LISTBASE_FOREACH (Object *, object, &bmain->objects) {
+ IDProperty *cvisibility = cycles_visibility_properties_from_ID(&object->id);
+ int flag = 0;
+
+ if (cvisibility) {
+ flag |= cycles_property_boolean(cvisibility, "camera", true) ? 0 : OB_HIDE_CAMERA;
+ flag |= cycles_property_boolean(cvisibility, "diffuse", true) ? 0 : OB_HIDE_DIFFUSE;
+ flag |= cycles_property_boolean(cvisibility, "glossy", true) ? 0 : OB_HIDE_GLOSSY;
+ flag |= cycles_property_boolean(cvisibility, "transmission", true) ? 0 :
+ OB_HIDE_TRANSMISSION;
+ flag |= cycles_property_boolean(cvisibility, "scatter", true) ? 0 : OB_HIDE_VOLUME_SCATTER;
+ flag |= cycles_property_boolean(cvisibility, "shadow", true) ? 0 : OB_HIDE_SHADOW;
+ }
+
+ IDProperty *cobject = cycles_properties_from_ID(&object->id);
+ if (cobject) {
+ flag |= cycles_property_boolean(cobject, "is_holdout", false) ? OB_HOLDOUT : 0;
+ flag |= cycles_property_boolean(cobject, "is_shadow_catcher", false) ? OB_SHADOW_CATCHER :
+ 0;
+ }
+
+ if (object->type == OB_LAMP) {
+ flag |= OB_HIDE_CAMERA | OB_SHADOW_CATCHER;
+ }
+
+ object->visibility_flag |= flag;
+ }
+ }
}
diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c
index 8362e001ea6..82c577d11a0 100644
--- a/source/blender/blenloader/intern/versioning_defaults.c
+++ b/source/blender/blenloader/intern/versioning_defaults.c
@@ -65,38 +65,11 @@
#include "BLO_readfile.h"
+#include "versioning_common.h"
+
/* Make preferences read-only, use versioning_userdef.c. */
#define U (*((const UserDef *)&U))
-/**
- * Rename if the ID doesn't exist.
- */
-static ID *rename_id_for_versioning(Main *bmain,
- const short id_type,
- const char *name_src,
- const char *name_dst)
-{
- /* We can ignore libraries */
- ListBase *lb = which_libbase(bmain, id_type);
- ID *id = NULL;
- LISTBASE_FOREACH (ID *, idtest, lb) {
- if (idtest->lib == NULL) {
- if (STREQ(idtest->name + 2, name_src)) {
- id = idtest;
- }
- if (STREQ(idtest->name + 2, name_dst)) {
- return NULL;
- }
- }
- }
- if (id != NULL) {
- BLI_strncpy(id->name + 2, name_dst, sizeof(id->name) - 2);
- /* We know it's unique, this just sorts. */
- BLI_libblock_ensure_unique_name(bmain, id->name);
- }
- return id;
-}
-
static bool blo_is_builtin_template(const char *app_template)
{
/* For all builtin templates shipped with Blender. */
@@ -217,6 +190,7 @@ static void blo_update_defaults_screen(bScreen *screen,
}
/* Disable Curve Normals. */
v3d->overlay.edit_flag &= ~V3D_OVERLAY_EDIT_CU_NORMALS;
+ v3d->overlay.normals_constant_screen_size = 7.0f;
}
else if (area->spacetype == SPACE_CLIP) {
SpaceClip *sclip = area->spacedata.first;
@@ -406,28 +380,28 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template)
Brush *brush;
/* Pencil brush. */
- rename_id_for_versioning(bmain, ID_BR, "Draw Pencil", "Pencil");
+ do_versions_rename_id(bmain, ID_BR, "Draw Pencil", "Pencil");
/* Pen brush. */
- rename_id_for_versioning(bmain, ID_BR, "Draw Pen", "Pen");
+ do_versions_rename_id(bmain, ID_BR, "Draw Pen", "Pen");
/* Pen Soft brush. */
- brush = (Brush *)rename_id_for_versioning(bmain, ID_BR, "Draw Soft", "Pencil Soft");
+ brush = (Brush *)do_versions_rename_id(bmain, ID_BR, "Draw Soft", "Pencil Soft");
if (brush) {
brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PEN;
}
/* Ink Pen brush. */
- rename_id_for_versioning(bmain, ID_BR, "Draw Ink", "Ink Pen");
+ do_versions_rename_id(bmain, ID_BR, "Draw Ink", "Ink Pen");
/* Ink Pen Rough brush. */
- rename_id_for_versioning(bmain, ID_BR, "Draw Noise", "Ink Pen Rough");
+ do_versions_rename_id(bmain, ID_BR, "Draw Noise", "Ink Pen Rough");
/* Marker Bold brush. */
- rename_id_for_versioning(bmain, ID_BR, "Draw Marker", "Marker Bold");
+ do_versions_rename_id(bmain, ID_BR, "Draw Marker", "Marker Bold");
/* Marker Chisel brush. */
- rename_id_for_versioning(bmain, ID_BR, "Draw Block", "Marker Chisel");
+ do_versions_rename_id(bmain, ID_BR, "Draw Block", "Marker Chisel");
/* Remove useless Fill Area.001 brush. */
brush = BLI_findstring(&bmain->brushes, "Fill Area.001", offsetof(ID, name) + 2);
@@ -438,10 +412,10 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template)
/* Rename and fix materials and enable default object lights on. */
if (app_template && STREQ(app_template, "2D_Animation")) {
Material *ma = NULL;
- rename_id_for_versioning(bmain, ID_MA, "Black", "Solid Stroke");
- rename_id_for_versioning(bmain, ID_MA, "Red", "Squares Stroke");
- rename_id_for_versioning(bmain, ID_MA, "Grey", "Solid Fill");
- rename_id_for_versioning(bmain, ID_MA, "Black Dots", "Dots Stroke");
+ do_versions_rename_id(bmain, ID_MA, "Black", "Solid Stroke");
+ do_versions_rename_id(bmain, ID_MA, "Red", "Squares Stroke");
+ do_versions_rename_id(bmain, ID_MA, "Grey", "Solid Fill");
+ do_versions_rename_id(bmain, ID_MA, "Black Dots", "Dots Stroke");
/* Dots Stroke. */
ma = BLI_findstring(&bmain->materials, "Dots Stroke", offsetof(ID, name) + 2);
@@ -553,8 +527,8 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template)
}
/* Objects */
- rename_id_for_versioning(bmain, ID_OB, "Lamp", "Light");
- rename_id_for_versioning(bmain, ID_LA, "Lamp", "Light");
+ do_versions_rename_id(bmain, ID_OB, "Lamp", "Light");
+ do_versions_rename_id(bmain, ID_LA, "Lamp", "Light");
if (app_template && STREQ(app_template, "2D_Animation")) {
for (Object *object = bmain->objects.first; object; object = object->id.next) {
diff --git a/source/blender/blenloader/intern/versioning_legacy.c b/source/blender/blenloader/intern/versioning_legacy.c
index 95cfc9975d7..6ba27b6ee9e 100644
--- a/source/blender/blenloader/intern/versioning_legacy.c
+++ b/source/blender/blenloader/intern/versioning_legacy.c
@@ -28,8 +28,7 @@
#else
# include "BLI_winstuff.h"
# include "winsock2.h"
-# include <io.h> /* for open close read */
-# include <zlib.h> /* odd include order-issue */
+# include <io.h> /* for open close read */
#endif
/* allow readfile to use deprecated functionality */
@@ -1335,7 +1334,7 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain)
ME_OPT_EDGES = (1 << 8),
};
- if ((me->flag & ME_SUBSURF)) {
+ if (me->flag & ME_SUBSURF) {
SubsurfModifierData *smd = (SubsurfModifierData *)BKE_modifier_new(
eModifierType_Subsurf);
diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c
index c409f0a71fc..0042ff29dc2 100644
--- a/source/blender/blenloader/intern/versioning_userdef.c
+++ b/source/blender/blenloader/intern/versioning_userdef.c
@@ -873,13 +873,6 @@ void blo_do_versions_userdef(UserDef *userdef)
}
}
- if (!USER_VERSION_ATLEAST(293, 2)) {
- /* Enable asset browser features by default for alpha testing.
- * BLO_sanitize_experimental_features_userpref_blend() will disable it again for non-alpha
- * builds. */
- userdef->experimental.use_asset_browser = true;
- }
-
if (!USER_VERSION_ATLEAST(293, 12)) {
if (userdef->gizmo_size_navigate_v3d == 0) {
userdef->gizmo_size_navigate_v3d = 80;
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index 12839a155e4..6f43fbf1fa0 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -78,12 +78,12 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <zlib.h>
#ifdef WIN32
# include "BLI_winstuff.h"
# include "winsock2.h"
# include <io.h>
-# include <zlib.h> /* odd include order-issue */
#else
# include <unistd.h> /* FreeBSD, for write() and close(). */
#endif
@@ -101,7 +101,12 @@
#include "BLI_bitmap.h"
#include "BLI_blenlib.h"
#include "BLI_endian_defines.h"
+#include "BLI_endian_switch.h"
+#include "BLI_link_utils.h"
+#include "BLI_linklist.h"
+#include "BLI_math_base.h"
#include "BLI_mempool.h"
+#include "BLI_threads.h"
#include "MEM_guardedalloc.h" /* MEM_freeN */
#include "BKE_blender_version.h"
@@ -129,14 +134,21 @@
#include <errno.h>
+#include <zstd.h>
+
/* Make preferences read-only. */
#define U (*((const UserDef *)&U))
/* ********* my write, buffered writing with minimum size chunks ************ */
/* Use optimal allocation since blocks of this size are kept in memory for undo. */
-#define MYWRITE_BUFFER_SIZE (MEM_SIZE_OPTIMAL(1 << 17)) /* 128kb */
-#define MYWRITE_MAX_CHUNK (MEM_SIZE_OPTIMAL(1 << 15)) /* ~32kb */
+#define MEM_BUFFER_SIZE (MEM_SIZE_OPTIMAL(1 << 17)) /* 128kb */
+#define MEM_CHUNK_SIZE (MEM_SIZE_OPTIMAL(1 << 15)) /* ~32kb */
+
+#define ZSTD_BUFFER_SIZE (1 << 21) /* 2mb */
+#define ZSTD_CHUNK_SIZE (1 << 20) /* 1mb */
+
+#define ZSTD_COMPRESSION_LEVEL 3
/** Use if we want to store how many bytes have been written to the file. */
// #define USE_WRITE_DATA_LEN
@@ -147,9 +159,16 @@
typedef enum {
WW_WRAP_NONE = 1,
- WW_WRAP_ZLIB,
+ WW_WRAP_ZSTD,
} eWriteWrapType;
+typedef struct ZstdFrame {
+ struct ZstdFrame *next, *prev;
+
+ uint32_t compressed_size;
+ uint32_t uncompressed_size;
+} ZstdFrame;
+
typedef struct WriteWrap WriteWrap;
struct WriteWrap {
/* callbacks */
@@ -161,15 +180,23 @@ struct WriteWrap {
bool use_buf;
/* internal */
- union {
- int file_handle;
- gzFile gz_handle;
- } _user_data;
+ int file_handle;
+ struct {
+ ListBase threadpool;
+ ListBase tasks;
+ ThreadMutex mutex;
+ ThreadCondition condition;
+ int next_frame;
+ int num_frames;
+
+ int level;
+ ListBase frames;
+
+ bool write_error;
+ } zstd;
};
/* none */
-#define FILE_HANDLE(ww) (ww)->_user_data.file_handle
-
static bool ww_open_none(WriteWrap *ww, const char *filepath)
{
int file;
@@ -177,7 +204,7 @@ static bool ww_open_none(WriteWrap *ww, const char *filepath)
file = BLI_open(filepath, O_BINARY + O_WRONLY + O_CREAT + O_TRUNC, 0666);
if (file != -1) {
- FILE_HANDLE(ww) = file;
+ ww->file_handle = file;
return true;
}
@@ -185,39 +212,170 @@ static bool ww_open_none(WriteWrap *ww, const char *filepath)
}
static bool ww_close_none(WriteWrap *ww)
{
- return (close(FILE_HANDLE(ww)) != -1);
+ return (close(ww->file_handle) != -1);
}
static size_t ww_write_none(WriteWrap *ww, const char *buf, size_t buf_len)
{
- return write(FILE_HANDLE(ww), buf, buf_len);
+ return write(ww->file_handle, buf, buf_len);
}
-#undef FILE_HANDLE
-/* zlib */
-#define FILE_HANDLE(ww) (ww)->_user_data.gz_handle
+/* zstd */
-static bool ww_open_zlib(WriteWrap *ww, const char *filepath)
+typedef struct {
+ struct ZstdWriteBlockTask *next, *prev;
+ void *data;
+ size_t size;
+ int frame_number;
+ WriteWrap *ww;
+} ZstdWriteBlockTask;
+
+static void *zstd_write_task(void *userdata)
{
- gzFile file;
+ ZstdWriteBlockTask *task = userdata;
+ WriteWrap *ww = task->ww;
- file = BLI_gzopen(filepath, "wb1");
+ size_t out_buf_len = ZSTD_compressBound(task->size);
+ void *out_buf = MEM_mallocN(out_buf_len, "Zstd out buffer");
+ size_t out_size = ZSTD_compress(
+ out_buf, out_buf_len, task->data, task->size, ZSTD_COMPRESSION_LEVEL);
- if (file != Z_NULL) {
- FILE_HANDLE(ww) = file;
- return true;
+ MEM_freeN(task->data);
+
+ BLI_mutex_lock(&ww->zstd.mutex);
+
+ while (ww->zstd.next_frame != task->frame_number) {
+ BLI_condition_wait(&ww->zstd.condition, &ww->zstd.mutex);
}
- return false;
+ if (ZSTD_isError(out_size)) {
+ ww->zstd.write_error = true;
+ }
+ else {
+ if (ww_write_none(ww, out_buf, out_size) == out_size) {
+ ZstdFrame *frameinfo = MEM_mallocN(sizeof(ZstdFrame), "zstd frameinfo");
+ frameinfo->uncompressed_size = task->size;
+ frameinfo->compressed_size = out_size;
+ BLI_addtail(&ww->zstd.frames, frameinfo);
+ }
+ else {
+ ww->zstd.write_error = true;
+ }
+ }
+
+ ww->zstd.next_frame++;
+
+ BLI_mutex_unlock(&ww->zstd.mutex);
+ BLI_condition_notify_all(&ww->zstd.condition);
+
+ MEM_freeN(out_buf);
+ return NULL;
+}
+
+static bool ww_open_zstd(WriteWrap *ww, const char *filepath)
+{
+ if (!ww_open_none(ww, filepath)) {
+ return false;
+ }
+
+ /* Leave one thread open for the main writing logic, unless we only have one HW thread. */
+ int num_threads = max_ii(1, BLI_system_thread_count() - 1);
+ BLI_threadpool_init(&ww->zstd.threadpool, zstd_write_task, num_threads);
+ BLI_mutex_init(&ww->zstd.mutex);
+ BLI_condition_init(&ww->zstd.condition);
+
+ return true;
+}
+
+static void zstd_write_u32_le(WriteWrap *ww, uint32_t val)
+{
+#ifdef __BIG_ENDIAN__
+ BLI_endian_switch_uint32(&val);
+#endif
+ ww_write_none(ww, (char *)&val, sizeof(uint32_t));
}
-static bool ww_close_zlib(WriteWrap *ww)
+
+/* In order to implement efficient seeking when reading the .blend, we append
+ * a skippable frame that encodes information about the other frames present
+ * in the file.
+ * The format here follows the upstream spec for seekable files:
+ * https://github.com/facebook/zstd/blob/master/contrib/seekable_format/zstd_seekable_compression_format.md
+ * If this information is not present in a file (e.g. if it was compressed
+ * with external tools), it can still be opened in Blender, but seeking will
+ * not be supported, so more memory might be needed. */
+static void zstd_write_seekable_frames(WriteWrap *ww)
{
- return (gzclose(FILE_HANDLE(ww)) == Z_OK);
+ /* Write seek table header (magic number and frame size). */
+ zstd_write_u32_le(ww, 0x184D2A5E);
+
+ /* The actual frame number might not match ww->zstd.num_frames if there was a write error. */
+ const uint32_t num_frames = BLI_listbase_count(&ww->zstd.frames);
+ /* Each frame consists of two u32, so 8 bytes each.
+ * After the frames, a footer containing two u32 and one byte (9 bytes total) is written. */
+ const uint32_t frame_size = num_frames * 8 + 9;
+ zstd_write_u32_le(ww, frame_size);
+
+ /* Write seek table entries. */
+ LISTBASE_FOREACH (ZstdFrame *, frame, &ww->zstd.frames) {
+ zstd_write_u32_le(ww, frame->compressed_size);
+ zstd_write_u32_le(ww, frame->uncompressed_size);
+ }
+
+ /* Write seek table footer (number of frames, option flags and second magic number). */
+ zstd_write_u32_le(ww, num_frames);
+ const char flags = 0; /* We don't store checksums for each frame. */
+ ww_write_none(ww, &flags, 1);
+ zstd_write_u32_le(ww, 0x8F92EAB1);
}
-static size_t ww_write_zlib(WriteWrap *ww, const char *buf, size_t buf_len)
+
+static bool ww_close_zstd(WriteWrap *ww)
{
- return gzwrite(FILE_HANDLE(ww), buf, buf_len);
+ BLI_threadpool_end(&ww->zstd.threadpool);
+ BLI_freelistN(&ww->zstd.tasks);
+
+ BLI_mutex_end(&ww->zstd.mutex);
+ BLI_condition_end(&ww->zstd.condition);
+
+ zstd_write_seekable_frames(ww);
+ BLI_freelistN(&ww->zstd.frames);
+
+ return ww_close_none(ww) && !ww->zstd.write_error;
+}
+
+static size_t ww_write_zstd(WriteWrap *ww, const char *buf, size_t buf_len)
+{
+ if (ww->zstd.write_error) {
+ return 0;
+ }
+
+ ZstdWriteBlockTask *task = MEM_mallocN(sizeof(ZstdWriteBlockTask), __func__);
+ task->data = MEM_mallocN(buf_len, __func__);
+ memcpy(task->data, buf, buf_len);
+ task->size = buf_len;
+ task->frame_number = ww->zstd.num_frames++;
+ task->ww = ww;
+
+ BLI_mutex_lock(&ww->zstd.mutex);
+ BLI_addtail(&ww->zstd.tasks, task);
+
+ /* If there's a free worker thread, just push the block into that thread.
+ * Otherwise, we wait for the earliest thread to finish.
+ * We look up the earliest thread while holding the mutex, but release it
+ * before joining the thread to prevent a deadlock. */
+ ZstdWriteBlockTask *first_task = ww->zstd.tasks.first;
+ BLI_mutex_unlock(&ww->zstd.mutex);
+ if (!BLI_available_threads(&ww->zstd.threadpool)) {
+ BLI_threadpool_remove(&ww->zstd.threadpool, first_task);
+
+ /* If the task list was empty before we pushed our task, there should
+ * always be a free thread. */
+ BLI_assert(first_task != task);
+ BLI_remlink(&ww->zstd.tasks, first_task);
+ MEM_freeN(first_task);
+ }
+ BLI_threadpool_insert(&ww->zstd.threadpool, task);
+
+ return buf_len;
}
-#undef FILE_HANDLE
/* --- end compression types --- */
@@ -226,11 +384,11 @@ static void ww_handle_init(eWriteWrapType ww_type, WriteWrap *r_ww)
memset(r_ww, 0, sizeof(*r_ww));
switch (ww_type) {
- case WW_WRAP_ZLIB: {
- r_ww->open = ww_open_zlib;
- r_ww->close = ww_close_zlib;
- r_ww->write = ww_write_zlib;
- r_ww->use_buf = false;
+ case WW_WRAP_ZSTD: {
+ r_ww->open = ww_open_zstd;
+ r_ww->close = ww_close_zstd;
+ r_ww->write = ww_write_zstd;
+ r_ww->use_buf = true;
break;
}
default: {
@@ -252,10 +410,17 @@ static void ww_handle_init(eWriteWrapType ww_type, WriteWrap *r_ww)
typedef struct {
const struct SDNA *sdna;
- /** Use for file and memory writing (fixed size of #MYWRITE_BUFFER_SIZE). */
- uchar *buf;
- /** Number of bytes used in #WriteData.buf (flushed when exceeded). */
- size_t buf_used_len;
+ struct {
+ /** Use for file and memory writing (size stored in max_size). */
+ uchar *buf;
+ /** Number of bytes used in #WriteData.buf (flushed when exceeded). */
+ size_t used_len;
+
+ /** Maximum size of the buffer. */
+ size_t max_size;
+ /** Threshold above which writes get their own chunk. */
+ size_t chunk_size;
+ } buffer;
#ifdef USE_WRITE_DATA_LEN
/** Total number of bytes written. */
@@ -271,7 +436,7 @@ typedef struct {
bool use_memfile;
/**
- * Wrap writing, so we can use zlib or
+ * Wrap writing, so we can use zstd or
* other compression types later, see: G_FILE_COMPRESS
* Will be NULL for UNDO.
*/
@@ -291,7 +456,15 @@ static WriteData *writedata_new(WriteWrap *ww)
wd->ww = ww;
if ((ww == NULL) || (ww->use_buf)) {
- wd->buf = MEM_mallocN(MYWRITE_BUFFER_SIZE, "wd->buf");
+ if (ww == NULL) {
+ wd->buffer.max_size = MEM_BUFFER_SIZE;
+ wd->buffer.chunk_size = MEM_CHUNK_SIZE;
+ }
+ else {
+ wd->buffer.max_size = ZSTD_BUFFER_SIZE;
+ wd->buffer.chunk_size = ZSTD_CHUNK_SIZE;
+ }
+ wd->buffer.buf = MEM_mallocN(wd->buffer.max_size, "wd->buffer.buf");
}
return wd;
@@ -325,8 +498,8 @@ static void writedata_do_write(WriteData *wd, const void *mem, size_t memlen)
static void writedata_free(WriteData *wd)
{
- if (wd->buf) {
- MEM_freeN(wd->buf);
+ if (wd->buffer.buf) {
+ MEM_freeN(wd->buffer.buf);
}
MEM_freeN(wd);
}
@@ -343,9 +516,9 @@ static void writedata_free(WriteData *wd)
*/
static void mywrite_flush(WriteData *wd)
{
- if (wd->buf_used_len != 0) {
- writedata_do_write(wd, wd->buf, wd->buf_used_len);
- wd->buf_used_len = 0;
+ if (wd->buffer.used_len != 0) {
+ writedata_do_write(wd, wd->buffer.buf, wd->buffer.used_len);
+ wd->buffer.used_len = 0;
}
}
@@ -369,20 +542,20 @@ static void mywrite(WriteData *wd, const void *adr, size_t len)
wd->write_len += len;
#endif
- if (wd->buf == NULL) {
+ if (wd->buffer.buf == NULL) {
writedata_do_write(wd, adr, len);
}
else {
/* if we have a single big chunk, write existing data in
* buffer and write out big chunk in smaller pieces */
- if (len > MYWRITE_MAX_CHUNK) {
- if (wd->buf_used_len != 0) {
- writedata_do_write(wd, wd->buf, wd->buf_used_len);
- wd->buf_used_len = 0;
+ if (len > wd->buffer.chunk_size) {
+ if (wd->buffer.used_len != 0) {
+ writedata_do_write(wd, wd->buffer.buf, wd->buffer.used_len);
+ wd->buffer.used_len = 0;
}
do {
- size_t writelen = MIN2(len, MYWRITE_MAX_CHUNK);
+ size_t writelen = MIN2(len, wd->buffer.chunk_size);
writedata_do_write(wd, adr, writelen);
adr = (const char *)adr + writelen;
len -= writelen;
@@ -392,14 +565,14 @@ static void mywrite(WriteData *wd, const void *adr, size_t len)
}
/* if data would overflow buffer, write out the buffer */
- if (len + wd->buf_used_len > MYWRITE_BUFFER_SIZE - 1) {
- writedata_do_write(wd, wd->buf, wd->buf_used_len);
- wd->buf_used_len = 0;
+ if (len + wd->buffer.used_len > wd->buffer.max_size - 1) {
+ writedata_do_write(wd, wd->buffer.buf, wd->buffer.used_len);
+ wd->buffer.used_len = 0;
}
/* append data at end of buffer */
- memcpy(&wd->buf[wd->buf_used_len], adr, len);
- wd->buf_used_len += len;
+ memcpy(&wd->buffer.buf[wd->buffer.used_len], adr, len);
+ wd->buffer.used_len += len;
}
}
@@ -430,9 +603,9 @@ static WriteData *mywrite_begin(WriteWrap *ww, MemFile *compare, MemFile *curren
*/
static bool mywrite_end(WriteData *wd)
{
- if (wd->buf_used_len != 0) {
- writedata_do_write(wd, wd->buf, wd->buf_used_len);
- wd->buf_used_len = 0;
+ if (wd->buffer.used_len != 0) {
+ writedata_do_write(wd, wd->buffer.buf, wd->buffer.used_len);
+ wd->buffer.used_len = 0;
}
if (wd->use_memfile) {
@@ -757,8 +930,8 @@ static void write_userdef(BlendWriter *writer, const UserDef *userdef)
BLO_write_struct(writer, bPathCompare, path_cmp);
}
- LISTBASE_FOREACH (const bUserAssetLibrary *, asset_library, &userdef->asset_libraries) {
- BLO_write_struct(writer, bUserAssetLibrary, asset_library);
+ LISTBASE_FOREACH (const bUserAssetLibrary *, asset_library_ref, &userdef->asset_libraries) {
+ BLO_write_struct(writer, bUserAssetLibrary, asset_library_ref);
}
LISTBASE_FOREACH (const uiStyle *, style, &userdef->uistyles) {
@@ -982,6 +1155,14 @@ static bool write_file_handle(Main *mainvar,
BLI_assert(
(id->tag & (LIB_TAG_NO_MAIN | LIB_TAG_NO_USER_REFCOUNT | LIB_TAG_NOT_ALLOCATED)) == 0);
+ /* We only write unused IDs in undo case.
+ * NOTE: All Scenes, WindowManagers and WorkSpaces should always be written to disk, so
+ * their usercount should never be NULL currently. */
+ if (id->us == 0 && !wd->use_memfile) {
+ BLI_assert(!ELEM(GS(id->name), ID_SCE, ID_WM, ID_WS));
+ continue;
+ }
+
const bool do_override = !ELEM(override_storage, NULL, bmain) &&
ID_IS_OVERRIDE_LIBRARY_REAL(id);
@@ -1015,12 +1196,23 @@ static bool write_file_handle(Main *mainvar,
memcpy(id_buffer, id, idtype_struct_size);
+ /* Clear runtime data to reduce false detection of changed data in undo/redo context. */
((ID *)id_buffer)->tag = 0;
+ ((ID *)id_buffer)->us = 0;
+ ((ID *)id_buffer)->icon_id = 0;
/* Those listbase data change every time we add/remove an ID, and also often when
* renaming one (due to re-sorting). This avoids generating a lot of false 'is changed'
* detections between undo steps. */
((ID *)id_buffer)->prev = NULL;
((ID *)id_buffer)->next = NULL;
+ /* Those runtime pointers should never be set during writing stage, but just in case clear
+ * them too. */
+ ((ID *)id_buffer)->orig_id = NULL;
+ ((ID *)id_buffer)->newid = NULL;
+ /* Even though in theory we could be able to preserve this python instance across undo even
+ * when we need to re-read the ID into its original address, this is currently cleared in
+ * #direct_link_id_common in `readfile.c` anyway, */
+ ((ID *)id_buffer)->py_instance = NULL;
const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id);
if (id_type->blend_write != NULL) {
@@ -1131,7 +1323,6 @@ bool BLO_write_file(Main *mainvar,
ReportList *reports)
{
char tempname[FILE_MAX + 1];
- eWriteWrapType ww_type;
WriteWrap ww;
eBLO_WritePathRemap remap_mode = params->remap_mode;
@@ -1153,14 +1344,7 @@ bool BLO_write_file(Main *mainvar,
/* open temporary file, so we preserve the original in case we crash */
BLI_snprintf(tempname, sizeof(tempname), "%s@", filepath);
- if (write_flags & G_FILE_COMPRESS) {
- ww_type = WW_WRAP_ZLIB;
- }
- else {
- ww_type = WW_WRAP_NONE;
- }
-
- ww_handle_init(ww_type, &ww);
+ ww_handle_init((write_flags & G_FILE_COMPRESS) ? WW_WRAP_ZSTD : WW_WRAP_NONE, &ww);
if (ww.open(&ww, tempname) == false) {
BKE_reportf(
diff --git a/source/blender/blentranslation/intern/blt_lang.c b/source/blender/blentranslation/intern/blt_lang.c
index 97a1a83792a..91e8a81aec0 100644
--- a/source/blender/blentranslation/intern/blt_lang.c
+++ b/source/blender/blentranslation/intern/blt_lang.c
@@ -74,10 +74,7 @@ static void free_locales(void)
MEM_freeN((void *)locales);
locales = NULL;
}
- if (locales_menu) {
- MEM_freeN(locales_menu);
- locales_menu = NULL;
- }
+ MEM_SAFE_FREE(locales_menu);
num_locales = num_locales_menu = 0;
}
diff --git a/source/blender/bmesh/intern/bmesh_mesh_normals.c b/source/blender/bmesh/intern/bmesh_mesh_normals.c
index 6dfaa0ca688..186c85abe58 100644
--- a/source/blender/bmesh/intern/bmesh_mesh_normals.c
+++ b/source/blender/bmesh/intern/bmesh_mesh_normals.c
@@ -85,10 +85,9 @@ BLI_INLINE void bm_vert_calc_normals_accum_loop(const BMLoop *l_iter,
dotprod = -dotprod;
}
const float fac = saacos(-dotprod);
- /* NAN detection, otherwise this is a degenerated case, ignore that vertex in this case. */
- if (fac == fac) {
- madd_v3_v3fl(v_no, f_no, fac);
- }
+ /* Shouldn't happen as normalizing edge-vectors cause degenerate values to be zeroed out. */
+ BLI_assert(!isnan(fac));
+ madd_v3_v3fl(v_no, f_no, fac);
}
static void bm_vert_calc_normals_impl(BMVert *v)
@@ -754,7 +753,7 @@ static int bm_mesh_loops_calc_normals_for_loop(BMesh *bm,
/* Fix/update all clnors of this fan with computed average value. */
/* Prints continuously when merge custom normals, so commenting. */
- /* printf("Invalid clnors in this fan!\n"); */
+ // printf("Invalid clnors in this fan!\n");
while ((clnor = BLI_SMALLSTACK_POP(clnors))) {
// print_v2("org clnor", clnor);
@@ -820,9 +819,10 @@ BLI_INLINE bool bm_edge_is_smooth_no_angle_test(const BMEdge *e,
const BMLoop *l_a,
const BMLoop *l_b)
{
+ BLI_assert(l_a->radial_next == l_b);
return (
/* The face is manifold. */
- (l_a->radial_next == l_b) &&
+ (l_b->radial_next == l_a) &&
/* Faces have winding that faces the same way. */
(l_a->v != l_b->v) &&
/* The edge is smooth. */
@@ -931,6 +931,7 @@ static void bm_mesh_loops_calc_normals_for_vert_with_clnors(BMesh *bm,
const bool has_clnors = true;
LinkNode *loops_of_vert = NULL;
int loops_of_vert_count = 0;
+ /* When false the caller must have already tagged the edges. */
const bool do_edge_tag = (split_angle_cos != EDGE_TAG_FROM_SPLIT_ANGLE_BYPASS);
/* The loop with the lowest index. */
@@ -1039,6 +1040,7 @@ static void bm_mesh_loops_calc_normals_for_vert_without_clnors(
{
const bool has_clnors = false;
const short(*clnors_data)[2] = NULL;
+ /* When false the caller must have already tagged the edges. */
const bool do_edge_tag = (split_angle_cos != EDGE_TAG_FROM_SPLIT_ANGLE_BYPASS);
const int cd_loop_clnors_offset = -1;
@@ -1113,8 +1115,6 @@ static void bm_mesh_loops_calc_normals__single_threaded(BMesh *bm,
BMIter fiter;
BMFace *f_curr;
const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1);
- const bool check_angle = (split_angle < (float)M_PI);
- const float split_angle_cos = check_angle ? cosf(split_angle) : -1.0f;
MLoopNorSpaceArray _lnors_spacearr = {NULL};
@@ -1138,10 +1138,6 @@ static void bm_mesh_loops_calc_normals__single_threaded(BMesh *bm,
edge_vectors = BLI_stack_new(sizeof(float[3]), __func__);
}
- if (split_angle_cos != -1.0f) {
- bm_mesh_edges_sharp_tag(bm, fnos, has_clnors ? (float)M_PI : split_angle, false);
- }
-
/* Clear all loops' tags (means none are to be skipped for now). */
int index_face, index_loop = 0;
BM_ITER_MESH_INDEX (f_curr, &fiter, bm, BM_FACES_OF_MESH, index_face) {
@@ -1157,6 +1153,10 @@ static void bm_mesh_loops_calc_normals__single_threaded(BMesh *bm,
}
bm->elem_index_dirty &= ~(BM_FACE | BM_LOOP);
+ /* Always tag edges based on winding & sharp edge flag
+ * (even when the auto-smooth angle doesn't need to be calculated). */
+ bm_mesh_edges_sharp_tag(bm, fnos, has_clnors ? (float)M_PI : split_angle, false);
+
/* We now know edges that can be smoothed (they are tagged),
* and edges that will be hard (they aren't).
* Now, time to generate the normals.
@@ -1816,8 +1816,8 @@ void BM_lnorspace_invalidate(BMesh *bm, const bool do_invalidate_all)
BM_mesh_elem_index_ensure(bm, BM_VERT);
/* When we affect a given vertex, we may affect following smooth fans:
- * - all smooth fans of said vertex;
- * - all smooth fans of all immediate loop-neighbors vertices;
+ * - all smooth fans of said vertex;
+ * - all smooth fans of all immediate loop-neighbors vertices;
* This can be simplified as 'all loops of selected vertices and their immediate neighbors'
* need to be tagged for update.
*/
diff --git a/source/blender/bmesh/intern/bmesh_walkers_impl.c b/source/blender/bmesh/intern/bmesh_walkers_impl.c
index e66afcd88d9..7931e953295 100644
--- a/source/blender/bmesh/intern/bmesh_walkers_impl.c
+++ b/source/blender/bmesh/intern/bmesh_walkers_impl.c
@@ -203,9 +203,9 @@ static void *bmw_VertShellWalker_step(BMWalker *walker)
curedge = shellWalk.curedge;
do {
if (!BLI_gset_haskey(walker->visit_set, curedge)) {
- if (!walker->restrictflag ||
- (walker->restrictflag &&
- BMO_edge_flag_test(walker->bm, curedge, walker->restrictflag))) {
+ if (!walker->visibility_flag ||
+ (walker->visibility_flag &&
+ BMO_edge_flag_test(walker->bm, curedge, walker->visibility_flag))) {
BMwShellWalker *newstate;
v_old = BM_edge_other_vert(curedge, shellWalk.base);
@@ -714,7 +714,7 @@ static void *bmw_IslandboundWalker_step(BMWalker *walker)
iwalk->base = owalk.base;
#if 0
- if (!BMO_face_flag_test(walker->bm, l->f, walker->restrictflag)) {
+ if (!BMO_face_flag_test(walker->bm, l->f, walker->visibility_flag)) {
iwalk->curloop = l->radial_next;
}
else {
diff --git a/source/blender/bmesh/operators/bmo_removedoubles.c b/source/blender/bmesh/operators/bmo_removedoubles.c
index 8cc0bfadbda..57760900d45 100644
--- a/source/blender/bmesh/operators/bmo_removedoubles.c
+++ b/source/blender/bmesh/operators/bmo_removedoubles.c
@@ -327,7 +327,6 @@ void bmo_weld_verts_exec(BMesh *bm, BMOperator *op)
}
#define VERT_KEEP 8
-#define VERT_IN 32
#define EDGE_MARK 1
diff --git a/source/blender/bmesh/operators/bmo_smooth_laplacian.c b/source/blender/bmesh/operators/bmo_smooth_laplacian.c
index b2b93bfd003..94856701e72 100644
--- a/source/blender/bmesh/operators/bmo_smooth_laplacian.c
+++ b/source/blender/bmesh/operators/bmo_smooth_laplacian.c
@@ -37,14 +37,14 @@
struct BLaplacianSystem {
float *eweights; /* Length weights per Edge. */
- float (*fweights)[3]; /* Cotangent weights per face. */
+ float (*fweights)[3]; /* Cotangent weights per loop. */
float *ring_areas; /* Total area per ring. */
float *vlengths; /* Total sum of lengths(edges) per vertex. */
float *vweights; /* Total sum of weights per vertex. */
int numEdges; /* Number of edges. */
- int numFaces; /* Number of faces. */
+ int numLoops; /* Number of loops. */
int numVerts; /* Number of verts. */
- short *zerola; /* Is zero area or length. */
+ bool *zerola; /* Is zero area or length. */
/* Pointers to data. */
BMesh *bm;
@@ -57,7 +57,7 @@ struct BLaplacianSystem {
typedef struct BLaplacianSystem LaplacianSystem;
static bool vert_is_boundary(BMVert *v);
-static LaplacianSystem *init_laplacian_system(int a_numEdges, int a_numFaces, int a_numVerts);
+static LaplacianSystem *init_laplacian_system(int a_numEdges, int a_numLoops, int a_numVerts);
static void init_laplacian_matrix(LaplacianSystem *sys);
static void delete_laplacian_system(LaplacianSystem *sys);
static void delete_void_pointer(void *data);
@@ -94,19 +94,19 @@ static void delete_laplacian_system(LaplacianSystem *sys)
static void memset_laplacian_system(LaplacianSystem *sys, int val)
{
memset(sys->eweights, val, sizeof(float) * sys->numEdges);
- memset(sys->fweights, val, sizeof(float) * sys->numFaces * 3);
+ memset(sys->fweights, val, sizeof(float[3]) * sys->numLoops);
memset(sys->ring_areas, val, sizeof(float) * sys->numVerts);
memset(sys->vlengths, val, sizeof(float) * sys->numVerts);
memset(sys->vweights, val, sizeof(float) * sys->numVerts);
- memset(sys->zerola, val, sizeof(short) * sys->numVerts);
+ memset(sys->zerola, val, sizeof(bool) * sys->numVerts);
}
-static LaplacianSystem *init_laplacian_system(int a_numEdges, int a_numFaces, int a_numVerts)
+static LaplacianSystem *init_laplacian_system(int a_numEdges, int a_numLoops, int a_numVerts)
{
LaplacianSystem *sys;
sys = MEM_callocN(sizeof(LaplacianSystem), "ModLaplSmoothSystem");
sys->numEdges = a_numEdges;
- sys->numFaces = a_numFaces;
+ sys->numLoops = a_numLoops;
sys->numVerts = a_numVerts;
sys->eweights = MEM_callocN(sizeof(float) * sys->numEdges, "ModLaplSmoothEWeight");
@@ -115,7 +115,7 @@ static LaplacianSystem *init_laplacian_system(int a_numEdges, int a_numFaces, in
return NULL;
}
- sys->fweights = MEM_callocN(sizeof(float[3]) * sys->numFaces, "ModLaplSmoothFWeight");
+ sys->fweights = MEM_callocN(sizeof(float[3]) * sys->numLoops, "ModLaplSmoothFWeight");
if (!sys->fweights) {
delete_laplacian_system(sys);
return NULL;
@@ -139,7 +139,7 @@ static LaplacianSystem *init_laplacian_system(int a_numEdges, int a_numFaces, in
return NULL;
}
- sys->zerola = MEM_callocN(sizeof(short) * sys->numVerts, "ModLaplSmoothZeloa");
+ sys->zerola = MEM_callocN(sizeof(bool) * sys->numVerts, "ModLaplSmoothZeloa");
if (!sys->zerola) {
delete_laplacian_system(sys);
return NULL;
@@ -166,219 +166,160 @@ static LaplacianSystem *init_laplacian_system(int a_numEdges, int a_numFaces, in
static void init_laplacian_matrix(LaplacianSystem *sys)
{
- float areaf;
- float *v1, *v2, *v3, *v4;
- float w1, w2, w3, w4;
- int i, j;
- bool has_4_vert;
- uint idv1, idv2, idv3, idv4, idv[4];
BMEdge *e;
BMFace *f;
BMIter eiter;
BMIter fiter;
- BMIter vi;
- BMVert *vn;
- BMVert *vf[4];
-
- BM_ITER_MESH_INDEX (e, &eiter, sys->bm, BM_EDGES_OF_MESH, j) {
- if (!BM_elem_flag_test(e, BM_ELEM_SELECT) && BM_edge_is_boundary(e)) {
- v1 = e->v1->co;
- v2 = e->v2->co;
- idv1 = BM_elem_index_get(e->v1);
- idv2 = BM_elem_index_get(e->v2);
-
- w1 = len_v3v3(v1, v2);
- if (w1 > sys->min_area) {
- w1 = 1.0f / w1;
- i = BM_elem_index_get(e);
- sys->eweights[i] = w1;
- sys->vlengths[idv1] += w1;
- sys->vlengths[idv2] += w1;
- }
- else {
- sys->zerola[idv1] = 1;
- sys->zerola[idv2] = 1;
- }
- }
- }
-
- BM_ITER_MESH (f, &fiter, sys->bm, BM_FACES_OF_MESH) {
- if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
+ uint i;
- BM_ITER_ELEM_INDEX (vn, &vi, f, BM_VERTS_OF_FACE, i) {
- vf[i] = vn;
- }
- has_4_vert = (i == 4) ? 1 : 0;
- idv1 = BM_elem_index_get(vf[0]);
- idv2 = BM_elem_index_get(vf[1]);
- idv3 = BM_elem_index_get(vf[2]);
- idv4 = has_4_vert ? BM_elem_index_get(vf[3]) : 0;
-
- v1 = vf[0]->co;
- v2 = vf[1]->co;
- v3 = vf[2]->co;
- v4 = has_4_vert ? vf[3]->co : NULL;
-
- if (has_4_vert) {
- areaf = area_quad_v3(v1, v2, v3, v4);
- }
- else {
- areaf = area_tri_v3(v1, v2, v3);
- }
+ BM_ITER_MESH_INDEX (e, &eiter, sys->bm, BM_EDGES_OF_MESH, i) {
+ if (BM_elem_flag_test(e, BM_ELEM_SELECT) || !BM_edge_is_boundary(e)) {
+ continue;
+ }
- if (fabsf(areaf) < sys->min_area) {
- sys->zerola[idv1] = 1;
- sys->zerola[idv2] = 1;
- sys->zerola[idv3] = 1;
- if (has_4_vert) {
- sys->zerola[idv4] = 1;
- }
- }
+ const float *v1 = e->v1->co;
+ const float *v2 = e->v2->co;
+ const int idv1 = BM_elem_index_get(e->v1);
+ const int idv2 = BM_elem_index_get(e->v2);
+
+ float w1 = len_v3v3(v1, v2);
+ if (w1 > sys->min_area) {
+ w1 = 1.0f / w1;
+ sys->eweights[i] = w1;
+ sys->vlengths[idv1] += w1;
+ sys->vlengths[idv2] += w1;
+ }
+ else {
+ sys->zerola[idv1] = true;
+ sys->zerola[idv2] = true;
+ }
+ }
- sys->ring_areas[idv1] += areaf;
- sys->ring_areas[idv2] += areaf;
- sys->ring_areas[idv3] += areaf;
- if (has_4_vert) {
- sys->ring_areas[idv4] += areaf;
- }
+ uint l_curr_index = 0;
- if (has_4_vert) {
+ BM_ITER_MESH (f, &fiter, sys->bm, BM_FACES_OF_MESH) {
+ if (!BM_elem_flag_test(f, BM_ELEM_SELECT)) {
+ l_curr_index += f->len;
+ continue;
+ }
- idv[0] = idv1;
- idv[1] = idv2;
- idv[2] = idv3;
- idv[3] = idv4;
+ BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
+ BMLoop *l_iter;
- for (j = 0; j < 4; j++) {
- idv1 = idv[j];
- idv2 = idv[(j + 1) % 4];
- idv3 = idv[(j + 2) % 4];
- idv4 = idv[(j + 3) % 4];
+ l_iter = l_first;
+ do {
+ const int vi_prev = BM_elem_index_get(l_iter->prev->v);
+ const int vi_curr = BM_elem_index_get(l_iter->v);
+ const int vi_next = BM_elem_index_get(l_iter->next->v);
- v1 = vf[j]->co;
- v2 = vf[(j + 1) % 4]->co;
- v3 = vf[(j + 2) % 4]->co;
- v4 = vf[(j + 3) % 4]->co;
+ const float *co_prev = l_iter->prev->v->co;
+ const float *co_curr = l_iter->v->co;
+ const float *co_next = l_iter->next->v->co;
- w2 = cotangent_tri_weight_v3(v4, v1, v2) + cotangent_tri_weight_v3(v3, v1, v2);
- w3 = cotangent_tri_weight_v3(v2, v3, v1) + cotangent_tri_weight_v3(v4, v1, v3);
- w4 = cotangent_tri_weight_v3(v2, v4, v1) + cotangent_tri_weight_v3(v3, v4, v1);
+ const float areaf = area_tri_v3(co_prev, co_curr, co_next);
- sys->vweights[idv1] += (w2 + w3 + w4) / 4.0f;
- }
+ if (areaf < sys->min_area) {
+ sys->zerola[vi_curr] = true;
}
- else {
- i = BM_elem_index_get(f);
- w1 = cotangent_tri_weight_v3(v1, v2, v3);
- w2 = cotangent_tri_weight_v3(v2, v3, v1);
- w3 = cotangent_tri_weight_v3(v3, v1, v2);
+ sys->ring_areas[vi_prev] += areaf;
+ sys->ring_areas[vi_curr] += areaf;
+ sys->ring_areas[vi_next] += areaf;
- sys->fweights[i][0] += w1;
- sys->fweights[i][1] += w2;
- sys->fweights[i][2] += w3;
+ const float w1 = cotangent_tri_weight_v3(co_curr, co_next, co_prev) / 2.0f;
+ const float w2 = cotangent_tri_weight_v3(co_next, co_prev, co_curr) / 2.0f;
+ const float w3 = cotangent_tri_weight_v3(co_prev, co_curr, co_next) / 2.0f;
- sys->vweights[idv1] += w2 + w3;
- sys->vweights[idv2] += w1 + w3;
- sys->vweights[idv3] += w1 + w2;
- }
- }
+ sys->fweights[l_curr_index][0] += w1;
+ sys->fweights[l_curr_index][1] += w2;
+ sys->fweights[l_curr_index][2] += w3;
+
+ sys->vweights[vi_prev] += w1 + w2;
+ sys->vweights[vi_curr] += w2 + w3;
+ sys->vweights[vi_next] += w1 + w3;
+ } while (((void)(l_curr_index += 1), (l_iter = l_iter->next) != l_first));
}
}
static void fill_laplacian_matrix(LaplacianSystem *sys)
{
- float *v1, *v2, *v3, *v4;
- float w2, w3, w4;
- int i, j;
- bool has_4_vert;
- uint idv1, idv2, idv3, idv4, idv[4];
-
BMEdge *e;
BMFace *f;
BMIter eiter;
BMIter fiter;
- BMIter vi;
- BMVert *vn;
- BMVert *vf[4];
+ int i;
+
+ uint l_curr_index = 0;
BM_ITER_MESH (f, &fiter, sys->bm, BM_FACES_OF_MESH) {
- if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
- BM_ITER_ELEM_INDEX (vn, &vi, f, BM_VERTS_OF_FACE, i) {
- vf[i] = vn;
+ if (!BM_elem_flag_test(f, BM_ELEM_SELECT)) {
+ l_curr_index += f->len;
+ continue;
+ }
+
+ BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
+ BMLoop *l_iter = l_first;
+
+ int vi_prev = BM_elem_index_get(l_iter->prev->v);
+ int vi_curr = BM_elem_index_get(l_iter->v);
+
+ bool ok_prev = (sys->zerola[vi_prev] == false) && !vert_is_boundary(l_iter->prev->v);
+ bool ok_curr = (sys->zerola[vi_curr] == false) && !vert_is_boundary(l_iter->v);
+
+ do {
+ const int vi_next = BM_elem_index_get(l_iter->next->v);
+ const bool ok_next = (sys->zerola[vi_next] == false) && !vert_is_boundary(l_iter->next->v);
+
+ if (ok_prev) {
+ EIG_linear_solver_matrix_add(sys->context,
+ vi_prev,
+ vi_curr,
+ sys->fweights[l_curr_index][1] * sys->vweights[vi_prev]);
+ EIG_linear_solver_matrix_add(sys->context,
+ vi_prev,
+ vi_next,
+ sys->fweights[l_curr_index][0] * sys->vweights[vi_prev]);
}
- has_4_vert = (i == 4) ? 1 : 0;
- if (has_4_vert) {
- idv[0] = BM_elem_index_get(vf[0]);
- idv[1] = BM_elem_index_get(vf[1]);
- idv[2] = BM_elem_index_get(vf[2]);
- idv[3] = BM_elem_index_get(vf[3]);
- for (j = 0; j < 4; j++) {
- idv1 = idv[j];
- idv2 = idv[(j + 1) % 4];
- idv3 = idv[(j + 2) % 4];
- idv4 = idv[(j + 3) % 4];
-
- v1 = vf[j]->co;
- v2 = vf[(j + 1) % 4]->co;
- v3 = vf[(j + 2) % 4]->co;
- v4 = vf[(j + 3) % 4]->co;
-
- w2 = cotangent_tri_weight_v3(v4, v1, v2) + cotangent_tri_weight_v3(v3, v1, v2);
- w3 = cotangent_tri_weight_v3(v2, v3, v1) + cotangent_tri_weight_v3(v4, v1, v3);
- w4 = cotangent_tri_weight_v3(v2, v4, v1) + cotangent_tri_weight_v3(v3, v4, v1);
-
- w2 = w2 / 4.0f;
- w3 = w3 / 4.0f;
- w4 = w4 / 4.0f;
-
- if (!vert_is_boundary(vf[j]) && sys->zerola[idv1] == 0) {
- EIG_linear_solver_matrix_add(sys->context, idv1, idv2, w2 * sys->vweights[idv1]);
- EIG_linear_solver_matrix_add(sys->context, idv1, idv3, w3 * sys->vweights[idv1]);
- EIG_linear_solver_matrix_add(sys->context, idv1, idv4, w4 * sys->vweights[idv1]);
- }
- }
+ if (ok_curr) {
+ EIG_linear_solver_matrix_add(sys->context,
+ vi_curr,
+ vi_next,
+ sys->fweights[l_curr_index][2] * sys->vweights[vi_curr]);
+ EIG_linear_solver_matrix_add(sys->context,
+ vi_curr,
+ vi_prev,
+ sys->fweights[l_curr_index][1] * sys->vweights[vi_curr]);
}
- else {
- idv1 = BM_elem_index_get(vf[0]);
- idv2 = BM_elem_index_get(vf[1]);
- idv3 = BM_elem_index_get(vf[2]);
- /* Is ring if number of faces == number of edges around vertice. */
- i = BM_elem_index_get(f);
- if (!vert_is_boundary(vf[0]) && sys->zerola[idv1] == 0) {
- EIG_linear_solver_matrix_add(
- sys->context, idv1, idv2, sys->fweights[i][2] * sys->vweights[idv1]);
- EIG_linear_solver_matrix_add(
- sys->context, idv1, idv3, sys->fweights[i][1] * sys->vweights[idv1]);
- }
- if (!vert_is_boundary(vf[1]) && sys->zerola[idv2] == 0) {
- EIG_linear_solver_matrix_add(
- sys->context, idv2, idv1, sys->fweights[i][2] * sys->vweights[idv2]);
- EIG_linear_solver_matrix_add(
- sys->context, idv2, idv3, sys->fweights[i][0] * sys->vweights[idv2]);
- }
- if (!vert_is_boundary(vf[2]) && sys->zerola[idv3] == 0) {
- EIG_linear_solver_matrix_add(
- sys->context, idv3, idv1, sys->fweights[i][1] * sys->vweights[idv3]);
- EIG_linear_solver_matrix_add(
- sys->context, idv3, idv2, sys->fweights[i][0] * sys->vweights[idv3]);
- }
+ if (ok_next) {
+ EIG_linear_solver_matrix_add(sys->context,
+ vi_next,
+ vi_curr,
+ sys->fweights[l_curr_index][2] * sys->vweights[vi_next]);
+ EIG_linear_solver_matrix_add(sys->context,
+ vi_next,
+ vi_prev,
+ sys->fweights[l_curr_index][0] * sys->vweights[vi_next]);
}
- }
+
+ vi_prev = vi_curr;
+ vi_curr = vi_next;
+
+ ok_prev = ok_curr;
+ ok_curr = ok_next;
+
+ } while (((void)(l_curr_index += 1), (l_iter = l_iter->next) != l_first));
}
- BM_ITER_MESH (e, &eiter, sys->bm, BM_EDGES_OF_MESH) {
- if (!BM_elem_flag_test(e, BM_ELEM_SELECT) && BM_edge_is_boundary(e)) {
- v1 = e->v1->co;
- v2 = e->v2->co;
- idv1 = BM_elem_index_get(e->v1);
- idv2 = BM_elem_index_get(e->v2);
- if (sys->zerola[idv1] == 0 && sys->zerola[idv2] == 0) {
- i = BM_elem_index_get(e);
- EIG_linear_solver_matrix_add(
- sys->context, idv1, idv2, sys->eweights[i] * sys->vlengths[idv1]);
- EIG_linear_solver_matrix_add(
- sys->context, idv2, idv1, sys->eweights[i] * sys->vlengths[idv2]);
- }
+ BM_ITER_MESH_INDEX (e, &eiter, sys->bm, BM_EDGES_OF_MESH, i) {
+ if (BM_elem_flag_test(e, BM_ELEM_SELECT) || !BM_edge_is_boundary(e)) {
+ continue;
+ }
+ const uint idv1 = BM_elem_index_get(e->v1);
+ const uint idv2 = BM_elem_index_get(e->v2);
+ if (sys->zerola[idv1] == false && sys->zerola[idv2] == false) {
+ EIG_linear_solver_matrix_add(
+ sys->context, idv1, idv2, sys->eweights[i] * sys->vlengths[idv1]);
+ EIG_linear_solver_matrix_add(
+ sys->context, idv2, idv1, sys->eweights[i] * sys->vlengths[idv2]);
}
}
}
@@ -453,8 +394,8 @@ static void validate_solution(
lene = len_v3v3(ve1, ve2);
if (lene > leni * SMOOTH_LAPLACIAN_MAX_EDGE_PERCENTAGE ||
lene < leni * SMOOTH_LAPLACIAN_MIN_EDGE_PERCENTAGE) {
- sys->zerola[idv1] = 1;
- sys->zerola[idv2] = 1;
+ sys->zerola[idv1] = true;
+ sys->zerola[idv2] = true;
}
}
@@ -463,7 +404,7 @@ static void validate_solution(
}
BMO_ITER (v, &siter, sys->op->slots_in, "verts", BM_VERT) {
m_vertex_id = BM_elem_index_get(v);
- if (sys->zerola[m_vertex_id] == 0) {
+ if (sys->zerola[m_vertex_id] == false) {
if (usex) {
v->co[0] = EIG_linear_solver_variable_get(sys->context, 0, m_vertex_id);
}
@@ -495,7 +436,7 @@ void bmo_smooth_laplacian_vert_exec(BMesh *bm, BMOperator *op)
if (bm->totface == 0) {
return;
}
- sys = init_laplacian_system(bm->totedge, bm->totface, bm->totvert);
+ sys = init_laplacian_system(bm->totedge, bm->totloop, bm->totvert);
if (!sys) {
return;
}
@@ -533,7 +474,10 @@ void bmo_smooth_laplacian_vert_exec(BMesh *bm, BMOperator *op)
EIG_linear_solver_right_hand_side_add(sys->context, 1, m_vertex_id, v->co[1]);
EIG_linear_solver_right_hand_side_add(sys->context, 2, m_vertex_id, v->co[2]);
i = m_vertex_id;
- if (sys->zerola[i] == 0) {
+ if ((sys->zerola[i] == false) &&
+ /* Non zero check is to account for vertices that aren't connected to a selected face.
+ * Without this wire edges become `nan`, see T89214. */
+ (sys->ring_areas[i] != 0.0f)) {
w = sys->vweights[i] * sys->ring_areas[i];
sys->vweights[i] = (w == 0.0f) ? 0.0f : -lambda_factor / (4.0f * w);
w = sys->vlengths[i];
diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c
index 0f99f04ad57..e306fe47770 100644
--- a/source/blender/bmesh/tools/bmesh_bevel.c
+++ b/source/blender/bmesh/tools/bmesh_bevel.c
@@ -2511,7 +2511,7 @@ static void bevel_harden_normals(BevelParams *bp, BMesh *bm)
pnorm = lnext->f->no;
}
else {
- /* printf("unexpected harden case (edge)\n"); */
+ // printf("unexpected harden case (edge)\n");
}
}
else if (fkind == F_VERT) {
@@ -2554,7 +2554,7 @@ static void bevel_harden_normals(BevelParams *bp, BMesh *bm)
pnorm = norm;
}
else {
- /* printf("unexpected harden case (vert)\n"); */
+ // printf("unexpected harden case (vert)\n");
}
}
}
diff --git a/source/blender/bmesh/tools/bmesh_decimate_collapse.c b/source/blender/bmesh/tools/bmesh_decimate_collapse.c
index 20b6903b239..97fccbe01fd 100644
--- a/source/blender/bmesh/tools/bmesh_decimate_collapse.c
+++ b/source/blender/bmesh/tools/bmesh_decimate_collapse.c
@@ -935,9 +935,9 @@ static bool bm_edge_collapse_is_degenerate_topology(BMEdge *e_first)
}
/**
- * special, highly limited edge collapse function
+ * Special, highly limited edge collapse function
* intended for speed over flexibility.
- * can only collapse edges connected to (1, 2) tris.
+ * can only collapse edges connected to (1, 2) triangles.
*
* Important - don't add vert/edge/face data on collapsing!
*
diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt
index 000ba298c2d..ee287c65fe9 100644
--- a/source/blender/compositor/CMakeLists.txt
+++ b/source/blender/compositor/CMakeLists.txt
@@ -597,16 +597,16 @@ add_definitions(-DCL_USE_DEPRECATED_OPENCL_1_1_APIS)
set(GENSRC_DIR ${CMAKE_CURRENT_BINARY_DIR}/operations)
set(GENSRC ${GENSRC_DIR}/COM_SMAAAreaTexture.h)
add_custom_command(
- OUTPUT ${GENSRC}
- COMMAND ${CMAKE_COMMAND} -E make_directory ${GENSRC_DIR}
- COMMAND "$<TARGET_FILE:smaa_areatex>" ${GENSRC}
- DEPENDS smaa_areatex
+ OUTPUT ${GENSRC}
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${GENSRC_DIR}
+ COMMAND "$<TARGET_FILE:smaa_areatex>" ${GENSRC}
+ DEPENDS smaa_areatex
)
add_custom_target(smaa_areatex_header
- SOURCES ${GENSRC}
+ SOURCES ${GENSRC}
)
list(APPEND SRC
- ${GENSRC}
+ ${GENSRC}
)
unset(GENSRC)
unset(GENSRC_DIR)
@@ -650,4 +650,3 @@ if(WITH_GTESTS)
include(GTestTesting)
blender_add_test_lib(bf_compositor_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}")
endif()
-
diff --git a/source/blender/compositor/COM_defines.h b/source/blender/compositor/COM_defines.h
index 900f29db44c..40a1e0da2a8 100644
--- a/source/blender/compositor/COM_defines.h
+++ b/source/blender/compositor/COM_defines.h
@@ -62,12 +62,18 @@ constexpr int COM_data_type_num_channels(const DataType datatype)
}
}
+constexpr int COM_data_type_bytes_len(DataType data_type)
+{
+ return COM_data_type_num_channels(data_type) * sizeof(float);
+}
+
constexpr int COM_DATA_TYPE_VALUE_CHANNELS = COM_data_type_num_channels(DataType::Value);
constexpr int COM_DATA_TYPE_VECTOR_CHANNELS = COM_data_type_num_channels(DataType::Vector);
constexpr int COM_DATA_TYPE_COLOR_CHANNELS = COM_data_type_num_channels(DataType::Color);
constexpr float COM_COLOR_TRANSPARENT[4] = {0.0f, 0.0f, 0.0f, 0.0f};
constexpr float COM_VECTOR_ZERO[3] = {0.0f, 0.0f, 0.0f};
+constexpr float COM_COLOR_BLACK[4] = {0.0f, 0.0f, 0.0f, 1.0f};
constexpr float COM_VALUE_ZERO[1] = {0.0f};
constexpr float COM_VALUE_ONE[1] = {1.0f};
diff --git a/source/blender/compositor/intern/COM_BufferOperation.cc b/source/blender/compositor/intern/COM_BufferOperation.cc
index 90c97f2a9c7..cafdff89c8e 100644
--- a/source/blender/compositor/intern/COM_BufferOperation.cc
+++ b/source/blender/compositor/intern/COM_BufferOperation.cc
@@ -32,6 +32,7 @@ BufferOperation::BufferOperation(MemoryBuffer *buffer, DataType data_type)
setResolution(resolution);
addOutputSocket(data_type);
flags.is_constant_operation = buffer_->is_a_single_elem();
+ flags.is_fullframe_operation = false;
}
const float *BufferOperation::get_constant_elem()
@@ -40,20 +41,32 @@ const float *BufferOperation::get_constant_elem()
return buffer_->getBuffer();
}
+void BufferOperation::initExecution()
+{
+ if (buffer_->is_a_single_elem()) {
+ initMutex();
+ }
+}
+
void *BufferOperation::initializeTileData(rcti * /*rect*/)
{
if (buffer_->is_a_single_elem() == false) {
return buffer_;
}
+ lockMutex();
if (!inflated_buffer_) {
inflated_buffer_ = buffer_->inflate();
}
+ unlockMutex();
return inflated_buffer_;
}
void BufferOperation::deinitExecution()
{
+ if (buffer_->is_a_single_elem()) {
+ deinitMutex();
+ }
delete inflated_buffer_;
}
diff --git a/source/blender/compositor/intern/COM_BufferOperation.h b/source/blender/compositor/intern/COM_BufferOperation.h
index 705264c37b7..b4cbc0a56b6 100644
--- a/source/blender/compositor/intern/COM_BufferOperation.h
+++ b/source/blender/compositor/intern/COM_BufferOperation.h
@@ -32,6 +32,7 @@ class BufferOperation : public ConstantOperation {
const float *get_constant_elem() override;
void *initializeTileData(rcti *rect) override;
+ void initExecution() override;
void deinitExecution() override;
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
void executePixelFiltered(float output[4], float x, float y, float dx[2], float dy[2]) override;
diff --git a/source/blender/compositor/intern/COM_ConstantFolder.cc b/source/blender/compositor/intern/COM_ConstantFolder.cc
index 5b48ff8fc08..445a9ce7433 100644
--- a/source/blender/compositor/intern/COM_ConstantFolder.cc
+++ b/source/blender/compositor/intern/COM_ConstantFolder.cc
@@ -44,7 +44,9 @@ static bool is_constant_foldable(NodeOperation *operation)
{
if (operation->get_flags().can_be_constant && !operation->get_flags().is_constant_operation) {
for (int i = 0; i < operation->getNumberOfInputSockets(); i++) {
- if (!operation->get_input_operation(i)->get_flags().is_constant_operation) {
+ NodeOperation *input = operation->get_input_operation(i);
+ if (!input->get_flags().is_constant_operation ||
+ !static_cast<ConstantOperation *>(input)->can_get_constant_elem()) {
return false;
}
}
diff --git a/source/blender/compositor/intern/COM_Debug.cc b/source/blender/compositor/intern/COM_Debug.cc
index 5443974cbb0..a0333cf96cf 100644
--- a/source/blender/compositor/intern/COM_Debug.cc
+++ b/source/blender/compositor/intern/COM_Debug.cc
@@ -178,21 +178,27 @@ int DebugInfo::graphviz_operation(const ExecutionSystem *system,
}
len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "<OUT_%p>", socket);
switch (socket->getDataType()) {
- case DataType::Value:
- if (typeid(*operation) == typeid(SetValueOperation)) {
- const float value = ((SetValueOperation *)operation)->getValue();
+ case DataType::Value: {
+ ConstantOperation *constant = operation->get_flags().is_constant_operation ?
+ static_cast<ConstantOperation *>(operation) :
+ nullptr;
+ if (constant && constant->can_get_constant_elem()) {
+ const float value = *constant->get_constant_elem();
len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Value\\n%12.4g", value);
}
else {
len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Value");
}
break;
- case DataType::Vector:
+ }
+ case DataType::Vector: {
len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Vector");
break;
- case DataType::Color:
+ }
+ case DataType::Color: {
len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Color");
break;
+ }
}
}
len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "}");
diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h
index ae12c444dc1..310e87b6a4b 100644
--- a/source/blender/compositor/intern/COM_MemoryBuffer.h
+++ b/source/blender/compositor/intern/COM_MemoryBuffer.h
@@ -260,6 +260,11 @@ class MemoryBuffer {
return this->m_num_channels;
}
+ uint8_t get_elem_bytes_len() const
+ {
+ return this->m_num_channels * sizeof(float);
+ }
+
/**
* Get all buffer elements as a range with no offsets.
*/
diff --git a/source/blender/compositor/intern/COM_TiledExecutionModel.cc b/source/blender/compositor/intern/COM_TiledExecutionModel.cc
index d025ce53330..a081b80349d 100644
--- a/source/blender/compositor/intern/COM_TiledExecutionModel.cc
+++ b/source/blender/compositor/intern/COM_TiledExecutionModel.cc
@@ -45,7 +45,7 @@ TiledExecutionModel::TiledExecutionModel(CompositorContext &context,
group->determineResolution(resolution);
if (border_.use_render_border) {
- const rctf *render_border = border_.viewer_border;
+ const rctf *render_border = border_.render_border;
group->setRenderBorder(
render_border->xmin, render_border->xmax, render_border->ymin, render_border->ymax);
}
diff --git a/source/blender/compositor/nodes/COM_MaskNode.cc b/source/blender/compositor/nodes/COM_MaskNode.cc
index ef171c01653..b5b23798160 100644
--- a/source/blender/compositor/nodes/COM_MaskNode.cc
+++ b/source/blender/compositor/nodes/COM_MaskNode.cc
@@ -41,7 +41,7 @@ void MaskNode::convertToOperations(NodeConverter &converter,
NodeMask *data = (NodeMask *)editorNode->storage;
Mask *mask = (Mask *)editorNode->id;
- // always connect the output image
+ /* Always connect the output image. */
MaskOperation *operation = new MaskOperation();
if (editorNode->custom1 & CMP_NODEFLAG_MASK_FIXED) {
diff --git a/source/blender/compositor/nodes/COM_MovieClipNode.cc b/source/blender/compositor/nodes/COM_MovieClipNode.cc
index 50bd9b4d71b..b80071d27c7 100644
--- a/source/blender/compositor/nodes/COM_MovieClipNode.cc
+++ b/source/blender/compositor/nodes/COM_MovieClipNode.cc
@@ -62,7 +62,7 @@ void MovieClipNode::convertToOperations(NodeConverter &converter,
}
}
- // always connect the output image
+ /* Always connect the output image. */
MovieClipOperation *operation = new MovieClipOperation();
operation->setMovieClip(movieClip);
operation->setMovieClipUser(movieClipUser);
diff --git a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc
index 0c656753a51..30e7fab4027 100644
--- a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc
+++ b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc
@@ -20,6 +20,11 @@
namespace blender::compositor {
+AlphaOverKeyOperation::AlphaOverKeyOperation()
+{
+ this->flags.can_be_constant = true;
+}
+
void AlphaOverKeyOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -50,4 +55,29 @@ void AlphaOverKeyOperation::executePixelSampled(float output[4],
}
}
+void AlphaOverKeyOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ for (; p.out < p.row_end; p.next()) {
+ const float *color1 = p.color1;
+ const float *over_color = p.color2;
+ const float value = *p.value;
+
+ if (over_color[3] <= 0.0f) {
+ copy_v4_v4(p.out, color1);
+ }
+ else if (value == 1.0f && over_color[3] >= 1.0f) {
+ copy_v4_v4(p.out, over_color);
+ }
+ else {
+ const float premul = value * over_color[3];
+ const float mul = 1.0f - premul;
+
+ p.out[0] = (mul * color1[0]) + premul * over_color[0];
+ p.out[1] = (mul * color1[1]) + premul * over_color[1];
+ p.out[2] = (mul * color1[2]) + premul * over_color[2];
+ p.out[3] = (mul * color1[3]) + value * over_color[3];
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.h b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.h
index 83713d18971..960fbc98fe9 100644
--- a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.h
+++ b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.h
@@ -28,10 +28,14 @@ namespace blender::compositor {
*/
class AlphaOverKeyOperation : public MixBaseOperation {
public:
+ AlphaOverKeyOperation();
+
/**
* The inner loop of this operation.
*/
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ void update_memory_buffer_row(PixelCursor &p) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc
index c68c79d2263..0cc179ea209 100644
--- a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc
+++ b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc
@@ -23,6 +23,7 @@ namespace blender::compositor {
AlphaOverMixedOperation::AlphaOverMixedOperation()
{
this->m_x = 0.0f;
+ this->flags.can_be_constant = true;
}
void AlphaOverMixedOperation::executePixelSampled(float output[4],
@@ -56,4 +57,30 @@ void AlphaOverMixedOperation::executePixelSampled(float output[4],
}
}
+void AlphaOverMixedOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ for (; p.out < p.row_end; p.next()) {
+ const float *color1 = p.color1;
+ const float *over_color = p.color2;
+ const float value = *p.value;
+
+ if (over_color[3] <= 0.0f) {
+ copy_v4_v4(p.out, color1);
+ }
+ else if (value == 1.0f && over_color[3] >= 1.0f) {
+ copy_v4_v4(p.out, over_color);
+ }
+ else {
+ const float addfac = 1.0f - this->m_x + over_color[3] * this->m_x;
+ const float premul = value * addfac;
+ const float mul = 1.0f - value * over_color[3];
+
+ p.out[0] = (mul * color1[0]) + premul * over_color[0];
+ p.out[1] = (mul * color1[1]) + premul * over_color[1];
+ p.out[2] = (mul * color1[2]) + premul * over_color[2];
+ p.out[3] = (mul * color1[3]) + value * over_color[3];
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h
index e2b3af84162..2b88cd5f421 100644
--- a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h
+++ b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h
@@ -45,6 +45,8 @@ class AlphaOverMixedOperation : public MixBaseOperation {
{
this->m_x = x;
}
+
+ void update_memory_buffer_row(PixelCursor &p) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc
index 3dd4607e273..a57e8c7f8a3 100644
--- a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc
+++ b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc
@@ -20,6 +20,11 @@
namespace blender::compositor {
+AlphaOverPremultiplyOperation::AlphaOverPremultiplyOperation()
+{
+ this->flags.can_be_constant = true;
+}
+
void AlphaOverPremultiplyOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -50,4 +55,28 @@ void AlphaOverPremultiplyOperation::executePixelSampled(float output[4],
}
}
+void AlphaOverPremultiplyOperation::update_memory_buffer_row(PixelCursor &p)
+{
+ for (; p.out < p.row_end; p.next()) {
+ const float *color1 = p.color1;
+ const float *over_color = p.color2;
+ const float value = *p.value;
+
+ if (over_color[3] <= 0.0f) {
+ copy_v4_v4(p.out, color1);
+ }
+ else if (value == 1.0f && over_color[3] >= 1.0f) {
+ copy_v4_v4(p.out, over_color);
+ }
+ else {
+ const float mul = 1.0f - value * over_color[3];
+
+ p.out[0] = (mul * color1[0]) + value * over_color[0];
+ p.out[1] = (mul * color1[1]) + value * over_color[1];
+ p.out[2] = (mul * color1[2]) + value * over_color[2];
+ p.out[3] = (mul * color1[3]) + value * over_color[3];
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h
index f1d4b668fce..701bc07cc27 100644
--- a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h
+++ b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h
@@ -28,10 +28,14 @@ namespace blender::compositor {
*/
class AlphaOverPremultiplyOperation : public MixBaseOperation {
public:
+ AlphaOverPremultiplyOperation();
+
/**
* The inner loop of this operation.
*/
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ void update_memory_buffer_row(PixelCursor &p) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_BilateralBlurOperation.cc b/source/blender/compositor/operations/COM_BilateralBlurOperation.cc
index 64448e2ae95..0c1bb688d4e 100644
--- a/source/blender/compositor/operations/COM_BilateralBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_BilateralBlurOperation.cc
@@ -38,7 +38,6 @@ void BilateralBlurOperation::initExecution()
{
this->m_inputColorProgram = getInputSocketReader(0);
this->m_inputDeterminatorProgram = getInputSocketReader(1);
- this->m_space = this->m_data->sigma_space + this->m_data->iter;
QualityStepHelper::initExecution(COM_QH_INCREASE);
}
@@ -115,4 +114,89 @@ bool BilateralBlurOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
+void BilateralBlurOperation::get_area_of_interest(const int UNUSED(input_idx),
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ const int add = ceil(this->m_space) + 1;
+
+ r_input_area.xmax = output_area.xmax + (add);
+ r_input_area.xmin = output_area.xmin - (add);
+ r_input_area.ymax = output_area.ymax + (add);
+ r_input_area.ymin = output_area.ymin - (add);
+}
+
+struct PixelCursor {
+ MemoryBuffer *input_determinator;
+ MemoryBuffer *input_color;
+ int step;
+ float sigma_color;
+ const float *determ_reference_color;
+ float temp_color[4];
+ float *out;
+ int min_x, max_x;
+ int min_y, max_y;
+};
+
+static void blur_pixel(PixelCursor &p)
+{
+ float blur_divider = 0.0f;
+ zero_v4(p.out);
+
+ /* TODO(sergey): This isn't really good bilateral filter, it should be
+ * using gaussian bell for weights. Also sigma_color doesn't seem to be
+ * used correct at all.
+ */
+ for (int yi = p.min_y; yi < p.max_y; yi += p.step) {
+ for (int xi = p.min_x; xi < p.max_x; xi += p.step) {
+ p.input_determinator->read(p.temp_color, xi, yi);
+ /* Do not take the alpha channel into account. */
+ const float delta_color = (fabsf(p.determ_reference_color[0] - p.temp_color[0]) +
+ fabsf(p.determ_reference_color[1] - p.temp_color[1]) +
+ fabsf(p.determ_reference_color[2] - p.temp_color[2]));
+ if (delta_color < p.sigma_color) {
+ /* Add this to the blur. */
+ p.input_color->read(p.temp_color, xi, yi);
+ add_v4_v4(p.out, p.temp_color);
+ blur_divider += 1.0f;
+ }
+ }
+ }
+
+ if (blur_divider > 0.0f) {
+ mul_v4_fl(p.out, 1.0f / blur_divider);
+ }
+ else {
+ copy_v4_v4(p.out, COM_COLOR_BLACK);
+ }
+}
+
+void BilateralBlurOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ PixelCursor p = {};
+ p.step = QualityStepHelper::getStep();
+ p.sigma_color = this->m_data->sigma_color;
+ p.input_color = inputs[0];
+ p.input_determinator = inputs[1];
+ const float space = this->m_space;
+ for (int y = area.ymin; y < area.ymax; y++) {
+ p.out = output->get_elem(area.xmin, y);
+ /* This will be used as the reference color for the determinator. */
+ p.determ_reference_color = p.input_determinator->get_elem(area.xmin, y);
+ p.min_y = floor(y - space);
+ p.max_y = ceil(y + space);
+ for (int x = area.xmin; x < area.xmax; x++) {
+ p.min_x = floor(x - space);
+ p.max_x = ceil(x + space);
+
+ blur_pixel(p);
+
+ p.determ_reference_color += p.input_determinator->elem_stride;
+ p.out += output->elem_stride;
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_BilateralBlurOperation.h b/source/blender/compositor/operations/COM_BilateralBlurOperation.h
index c56cef35050..517c5292827 100644
--- a/source/blender/compositor/operations/COM_BilateralBlurOperation.h
+++ b/source/blender/compositor/operations/COM_BilateralBlurOperation.h
@@ -18,12 +18,12 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
#include "COM_QualityStepHelper.h"
namespace blender::compositor {
-class BilateralBlurOperation : public NodeOperation, public QualityStepHelper {
+class BilateralBlurOperation : public MultiThreadedOperation, public QualityStepHelper {
private:
SocketReader *m_inputColorProgram;
SocketReader *m_inputDeterminatorProgram;
@@ -55,7 +55,14 @@ class BilateralBlurOperation : public NodeOperation, public QualityStepHelper {
void setData(NodeBilateralBlurData *data)
{
this->m_data = data;
+ this->m_space = data->sigma_space + data->iter;
}
+
+ void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_BokehImageOperation.cc b/source/blender/compositor/operations/COM_BokehImageOperation.cc
index 63f283b6acc..bd5b25b5af8 100644
--- a/source/blender/compositor/operations/COM_BokehImageOperation.cc
+++ b/source/blender/compositor/operations/COM_BokehImageOperation.cc
@@ -110,6 +110,31 @@ void BokehImageOperation::executePixelSampled(float output[4],
output[3] = (insideBokehMax + insideBokehMed + insideBokehMin) / 3.0f;
}
+void BokehImageOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> UNUSED(inputs))
+{
+ const float shift = this->m_data->lensshift;
+ const float shift2 = shift / 2.0f;
+ const float distance = this->m_circularDistance;
+ for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
+ const float insideBokehMax = isInsideBokeh(distance, it.x, it.y);
+ const float insideBokehMed = isInsideBokeh(distance - fabsf(shift2 * distance), it.x, it.y);
+ const float insideBokehMin = isInsideBokeh(distance - fabsf(shift * distance), it.x, it.y);
+ if (shift < 0) {
+ it.out[0] = insideBokehMax;
+ it.out[1] = insideBokehMed;
+ it.out[2] = insideBokehMin;
+ }
+ else {
+ it.out[0] = insideBokehMin;
+ it.out[1] = insideBokehMed;
+ it.out[2] = insideBokehMax;
+ }
+ it.out[3] = (insideBokehMax + insideBokehMed + insideBokehMin) / 3.0f;
+ }
+}
+
void BokehImageOperation::deinitExecution()
{
if (this->m_deleteData) {
diff --git a/source/blender/compositor/operations/COM_BokehImageOperation.h b/source/blender/compositor/operations/COM_BokehImageOperation.h
index 2e0bc8a34dc..2527233fabd 100644
--- a/source/blender/compositor/operations/COM_BokehImageOperation.h
+++ b/source/blender/compositor/operations/COM_BokehImageOperation.h
@@ -18,7 +18,7 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
@@ -49,7 +49,7 @@ namespace blender::compositor {
* With a simple compare it can be detected if the evaluated pixel is between the outer and inner
*edge.
*/
-class BokehImageOperation : public NodeOperation {
+class BokehImageOperation : public MultiThreadedOperation {
private:
/**
* \brief Settings of the bokeh image
@@ -151,6 +151,10 @@ class BokehImageOperation : public NodeOperation {
{
this->m_deleteData = true;
}
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ChangeHSVOperation.cc b/source/blender/compositor/operations/COM_ChangeHSVOperation.cc
index eee007ce9e6..1e3e7806968 100644
--- a/source/blender/compositor/operations/COM_ChangeHSVOperation.cc
+++ b/source/blender/compositor/operations/COM_ChangeHSVOperation.cc
@@ -28,6 +28,7 @@ ChangeHSVOperation::ChangeHSVOperation()
this->addInputSocket(DataType::Value);
this->addOutputSocket(DataType::Color);
this->m_inputOperation = nullptr;
+ this->flags.can_be_constant = true;
}
void ChangeHSVOperation::initExecution()
@@ -71,4 +72,26 @@ void ChangeHSVOperation::executePixelSampled(float output[4],
output[3] = inputColor1[3];
}
+void ChangeHSVOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ const float *color = it.in(0);
+ const float hue = *it.in(1);
+ it.out[0] = color[0] + (hue - 0.5f);
+ if (it.out[0] > 1.0f) {
+ it.out[0] -= 1.0f;
+ }
+ else if (it.out[0] < 0.0f) {
+ it.out[0] += 1.0f;
+ }
+ const float saturation = *it.in(2);
+ const float value = *it.in(3);
+ it.out[1] = color[1] * saturation;
+ it.out[2] = color[2] * value;
+ it.out[3] = color[3];
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ChangeHSVOperation.h b/source/blender/compositor/operations/COM_ChangeHSVOperation.h
index d38b4be3efe..e7bc3274f25 100644
--- a/source/blender/compositor/operations/COM_ChangeHSVOperation.h
+++ b/source/blender/compositor/operations/COM_ChangeHSVOperation.h
@@ -18,7 +18,7 @@
#pragma once
-#include "COM_MixOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
@@ -26,7 +26,7 @@ namespace blender::compositor {
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
*/
-class ChangeHSVOperation : public NodeOperation {
+class ChangeHSVOperation : public MultiThreadedOperation {
private:
SocketReader *m_inputOperation;
SocketReader *m_hueOperation;
@@ -46,6 +46,10 @@ class ChangeHSVOperation : public NodeOperation {
* The inner loop of this operation.
*/
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorCurveOperation.cc b/source/blender/compositor/operations/COM_ColorCurveOperation.cc
index cb0565a81a2..1b7ad0ea608 100644
--- a/source/blender/compositor/operations/COM_ColorCurveOperation.cc
+++ b/source/blender/compositor/operations/COM_ColorCurveOperation.cc
@@ -98,6 +98,36 @@ void ColorCurveOperation::deinitExecution()
this->m_inputWhiteProgram = nullptr;
}
+void ColorCurveOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ CurveMapping *cumap = this->m_curveMapping;
+ float bwmul[3];
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ /* Local versions of `cumap->black` and `cumap->white`. */
+ const float *black = it.in(2);
+ const float *white = it.in(3);
+ /* Get a local `bwmul` value, it's not threadsafe using `cumap->bwmul` and others. */
+ BKE_curvemapping_set_black_white_ex(black, white, bwmul);
+
+ const float fac = *it.in(0);
+ const float *image = it.in(1);
+ if (fac >= 1.0f) {
+ BKE_curvemapping_evaluate_premulRGBF_ex(cumap, it.out, image, black, bwmul);
+ }
+ else if (fac <= 0.0f) {
+ copy_v3_v3(it.out, image);
+ }
+ else {
+ float col[4];
+ BKE_curvemapping_evaluate_premulRGBF_ex(cumap, col, image, black, bwmul);
+ interp_v3_v3v3(it.out, image, col, fac);
+ }
+ it.out[3] = image[3];
+ }
+}
+
// Constant level curve mapping
ConstantLevelColorCurveOperation::ConstantLevelColorCurveOperation()
@@ -154,4 +184,27 @@ void ConstantLevelColorCurveOperation::deinitExecution()
this->m_inputImageProgram = nullptr;
}
+void ConstantLevelColorCurveOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ CurveMapping *cumap = this->m_curveMapping;
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ const float fac = *it.in(0);
+ const float *image = it.in(1);
+ if (fac >= 1.0f) {
+ BKE_curvemapping_evaluate_premulRGBF(cumap, it.out, image);
+ }
+ else if (fac <= 0.0f) {
+ copy_v3_v3(it.out, image);
+ }
+ else {
+ float col[4];
+ BKE_curvemapping_evaluate_premulRGBF(cumap, col, image);
+ interp_v3_v3v3(it.out, image, col, fac);
+ }
+ it.out[3] = image[3];
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ColorCurveOperation.h b/source/blender/compositor/operations/COM_ColorCurveOperation.h
index 6fc7759b8d2..d8271e56d1d 100644
--- a/source/blender/compositor/operations/COM_ColorCurveOperation.h
+++ b/source/blender/compositor/operations/COM_ColorCurveOperation.h
@@ -51,6 +51,10 @@ class ColorCurveOperation : public CurveBaseOperation {
* Deinitialize the execution
*/
void deinitExecution() override;
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
class ConstantLevelColorCurveOperation : public CurveBaseOperation {
@@ -89,6 +93,10 @@ class ConstantLevelColorCurveOperation : public CurveBaseOperation {
{
copy_v3_v3(this->m_white, white);
}
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CompositorOperation.cc b/source/blender/compositor/operations/COM_CompositorOperation.cc
index 94d41b28f5d..8752d764107 100644
--- a/source/blender/compositor/operations/COM_CompositorOperation.cc
+++ b/source/blender/compositor/operations/COM_CompositorOperation.cc
@@ -220,6 +220,22 @@ void CompositorOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/)
}
}
+void CompositorOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output),
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ if (!m_outputBuffer) {
+ return;
+ }
+ MemoryBuffer output_buf(m_outputBuffer, COM_DATA_TYPE_COLOR_CHANNELS, getWidth(), getHeight());
+ output_buf.copy_from(inputs[0], area);
+ if (this->m_useAlphaInput) {
+ output_buf.copy_from(inputs[1], area, 0, COM_DATA_TYPE_VALUE_CHANNELS, 3);
+ }
+ MemoryBuffer depth_buf(m_depthBuffer, COM_DATA_TYPE_VALUE_CHANNELS, getWidth(), getHeight());
+ depth_buf.copy_from(inputs[2], area);
+}
+
void CompositorOperation::determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2])
{
diff --git a/source/blender/compositor/operations/COM_CompositorOperation.h b/source/blender/compositor/operations/COM_CompositorOperation.h
index 65988c86cc5..66367ec8bae 100644
--- a/source/blender/compositor/operations/COM_CompositorOperation.h
+++ b/source/blender/compositor/operations/COM_CompositorOperation.h
@@ -20,7 +20,7 @@
#include "BLI_rect.h"
#include "BLI_string.h"
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
struct Scene;
@@ -29,7 +29,7 @@ namespace blender::compositor {
/**
* \brief Compositor output operation
*/
-class CompositorOperation : public NodeOperation {
+class CompositorOperation : public MultiThreadedOperation {
private:
const struct Scene *m_scene;
/**
@@ -125,6 +125,10 @@ class CompositorOperation : public NodeOperation {
{
this->m_active = active;
}
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ConstantOperation.cc b/source/blender/compositor/operations/COM_ConstantOperation.cc
index f905edbde76..33d51cca432 100644
--- a/source/blender/compositor/operations/COM_ConstantOperation.cc
+++ b/source/blender/compositor/operations/COM_ConstantOperation.cc
@@ -22,7 +22,24 @@ namespace blender::compositor {
ConstantOperation::ConstantOperation()
{
+ needs_resolution_to_get_constant_ = false;
flags.is_constant_operation = true;
+ flags.is_fullframe_operation = true;
+}
+
+bool ConstantOperation::can_get_constant_elem() const
+{
+ return !needs_resolution_to_get_constant_ || this->flags.is_resolution_set;
+}
+
+void ConstantOperation::update_memory_buffer(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> UNUSED(inputs))
+{
+ BLI_assert(output->is_a_single_elem());
+ const float *constant = get_constant_elem();
+ float *out = output->get_elem(area.xmin, area.ymin);
+ memcpy(out, constant, output->get_elem_bytes_len());
}
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_ConstantOperation.h b/source/blender/compositor/operations/COM_ConstantOperation.h
index 2709efeebd8..31b8d30254b 100644
--- a/source/blender/compositor/operations/COM_ConstantOperation.h
+++ b/source/blender/compositor/operations/COM_ConstantOperation.h
@@ -22,15 +22,27 @@
namespace blender::compositor {
+/* TODO(manzanilla): After removing tiled implementation, implement a default #determineResolution
+ * for all constant operations and make all initialization and deinitilization methods final. */
/**
- * Base class for primitive constant operations (Color/Vector/Value). The rest of operations that
- * can be constant are evaluated into primitives during constant folding.
+ * Base class for operations that are always constant. Operations that can be constant only when
+ * all their inputs are so, are evaluated into primitive constants (Color/Vector/Value) during
+ * constant folding.
*/
class ConstantOperation : public NodeOperation {
+ protected:
+ bool needs_resolution_to_get_constant_;
+
public:
ConstantOperation();
+ /** May require resolution to be already determined. */
virtual const float *get_constant_elem() = 0;
+ bool can_get_constant_elem() const;
+
+ void update_memory_buffer(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) final;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_CryptomatteOperation.cc b/source/blender/compositor/operations/COM_CryptomatteOperation.cc
index 52ae1d6d5b5..1a86fadad76 100644
--- a/source/blender/compositor/operations/COM_CryptomatteOperation.cc
+++ b/source/blender/compositor/operations/COM_CryptomatteOperation.cc
@@ -57,8 +57,8 @@ void CryptomatteOperation::executePixel(float output[4], int x, int y, void *dat
::memcpy(&m3hash, &input[0], sizeof(uint32_t));
/* Since the red channel is likely to be out of display range,
* setting green and blue gives more meaningful images. */
- output[1] = ((float)((m3hash << 8)) / (float)UINT32_MAX);
- output[2] = ((float)((m3hash << 16)) / (float)UINT32_MAX);
+ output[1] = ((float)(m3hash << 8) / (float)UINT32_MAX);
+ output[2] = ((float)(m3hash << 16) / (float)UINT32_MAX);
}
for (float hash : m_objectIndex) {
if (input[0] == hash) {
diff --git a/source/blender/compositor/operations/COM_CurveBaseOperation.cc b/source/blender/compositor/operations/COM_CurveBaseOperation.cc
index 8f655964570..3c4b27aa4cf 100644
--- a/source/blender/compositor/operations/COM_CurveBaseOperation.cc
+++ b/source/blender/compositor/operations/COM_CurveBaseOperation.cc
@@ -25,6 +25,7 @@ namespace blender::compositor {
CurveBaseOperation::CurveBaseOperation()
{
this->m_curveMapping = nullptr;
+ this->flags.can_be_constant = true;
}
CurveBaseOperation::~CurveBaseOperation()
diff --git a/source/blender/compositor/operations/COM_CurveBaseOperation.h b/source/blender/compositor/operations/COM_CurveBaseOperation.h
index fff0f3168ba..da665e7ea60 100644
--- a/source/blender/compositor/operations/COM_CurveBaseOperation.h
+++ b/source/blender/compositor/operations/COM_CurveBaseOperation.h
@@ -18,12 +18,12 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
#include "DNA_color_types.h"
namespace blender::compositor {
-class CurveBaseOperation : public NodeOperation {
+class CurveBaseOperation : public MultiThreadedOperation {
protected:
/**
* Cached reference to the inputProgram
diff --git a/source/blender/compositor/operations/COM_DilateErodeOperation.cc b/source/blender/compositor/operations/COM_DilateErodeOperation.cc
index 2454f507664..a27148f967d 100644
--- a/source/blender/compositor/operations/COM_DilateErodeOperation.cc
+++ b/source/blender/compositor/operations/COM_DilateErodeOperation.cc
@@ -24,7 +24,7 @@
namespace blender::compositor {
-// DilateErode Distance Threshold
+/* DilateErode Distance Threshold */
DilateErodeThresholdOperation::DilateErodeThresholdOperation()
{
this->addInputSocket(DataType::Value);
@@ -258,7 +258,7 @@ void DilateDistanceOperation::executeOpenCL(OpenCLDevice *device,
device->COM_clEnqueueRange(dilateKernel, outputMemoryBuffer, 7, this);
}
-// Erode Distance
+/* Erode Distance */
ErodeDistanceOperation::ErodeDistanceOperation() : DilateDistanceOperation()
{
/* pass */
@@ -318,7 +318,7 @@ void ErodeDistanceOperation::executeOpenCL(OpenCLDevice *device,
device->COM_clEnqueueRange(erodeKernel, outputMemoryBuffer, 7, this);
}
-// Dilate step
+/* Dilate step */
DilateStepOperation::DilateStepOperation()
{
this->addInputSocket(DataType::Value);
@@ -331,7 +331,7 @@ void DilateStepOperation::initExecution()
this->m_inputProgram = this->getInputSocketReader(0);
}
-// small helper to pass data from initializeTileData to executePixel
+/* Small helper to pass data from initializeTileData to executePixel. */
struct tile_info {
rcti rect;
int width;
@@ -370,21 +370,21 @@ void *DilateStepOperation::initializeTileData(rcti *rect)
int bwidth = rect->xmax - rect->xmin;
int bheight = rect->ymax - rect->ymin;
- // NOTE: Cache buffer has original tilesize width, but new height.
- // We have to calculate the additional rows in the first pass,
- // to have valid data available for the second pass.
+ /* NOTE: Cache buffer has original tile-size width, but new height.
+ * We have to calculate the additional rows in the first pass,
+ * to have valid data available for the second pass. */
tile_info *result = create_cache(rect->xmin, rect->xmax, ymin, ymax);
float *rectf = result->buffer;
- // temp holds maxima for every step in the algorithm, buf holds a
- // single row or column of input values, padded with FLT_MAX's to
- // simplify the logic.
+ /* temp holds maxima for every step in the algorithm, buf holds a
+ * single row or column of input values, padded with FLT_MAX's to
+ * simplify the logic. */
float *temp = (float *)MEM_mallocN(sizeof(float) * (2 * window - 1), "dilate erode temp");
float *buf = (float *)MEM_mallocN(sizeof(float) * (MAX2(bwidth, bheight) + 5 * half_window),
"dilate erode buf");
- // The following is based on the van Herk/Gil-Werman algorithm for morphology operations.
- // first pass, horizontal dilate/erode
+ /* The following is based on the van Herk/Gil-Werman algorithm for morphology operations.
+ * first pass, horizontal dilate/erode. */
for (y = ymin; y < ymax; y++) {
for (x = 0; x < bwidth + 5 * half_window; x++) {
buf[x] = -FLT_MAX;
@@ -409,7 +409,7 @@ void *DilateStepOperation::initializeTileData(rcti *rect)
}
}
- // second pass, vertical dilate/erode
+ /* Second pass, vertical dilate/erode. */
for (x = 0; x < bwidth; x++) {
for (y = 0; y < bheight + 5 * half_window; y++) {
buf[y] = -FLT_MAX;
@@ -475,7 +475,7 @@ bool DilateStepOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
-// Erode step
+/* Erode step */
ErodeStepOperation::ErodeStepOperation() : DilateStepOperation()
{
/* pass */
@@ -500,21 +500,21 @@ void *ErodeStepOperation::initializeTileData(rcti *rect)
int bwidth = rect->xmax - rect->xmin;
int bheight = rect->ymax - rect->ymin;
- // NOTE: Cache buffer has original tilesize width, but new height.
- // We have to calculate the additional rows in the first pass,
- // to have valid data available for the second pass.
+ /* NOTE: Cache buffer has original tile-size width, but new height.
+ * We have to calculate the additional rows in the first pass,
+ * to have valid data available for the second pass. */
tile_info *result = create_cache(rect->xmin, rect->xmax, ymin, ymax);
float *rectf = result->buffer;
- // temp holds maxima for every step in the algorithm, buf holds a
- // single row or column of input values, padded with FLT_MAX's to
- // simplify the logic.
+ /* temp holds maxima for every step in the algorithm, buf holds a
+ * single row or column of input values, padded with FLT_MAX's to
+ * simplify the logic. */
float *temp = (float *)MEM_mallocN(sizeof(float) * (2 * window - 1), "dilate erode temp");
float *buf = (float *)MEM_mallocN(sizeof(float) * (MAX2(bwidth, bheight) + 5 * half_window),
"dilate erode buf");
- // The following is based on the van Herk/Gil-Werman algorithm for morphology operations.
- // first pass, horizontal dilate/erode
+ /* The following is based on the van Herk/Gil-Werman algorithm for morphology operations.
+ * first pass, horizontal dilate/erode */
for (y = ymin; y < ymax; y++) {
for (x = 0; x < bwidth + 5 * half_window; x++) {
buf[x] = FLT_MAX;
@@ -539,7 +539,7 @@ void *ErodeStepOperation::initializeTileData(rcti *rect)
}
}
- // second pass, vertical dilate/erode
+ /* Second pass, vertical dilate/erode. */
for (x = 0; x < bwidth; x++) {
for (y = 0; y < bheight + 5 * half_window; y++) {
buf[y] = FLT_MAX;
diff --git a/source/blender/compositor/operations/COM_GlareBaseOperation.h b/source/blender/compositor/operations/COM_GlareBaseOperation.h
index 50db4e02940..6dac6f5ecc7 100644
--- a/source/blender/compositor/operations/COM_GlareBaseOperation.h
+++ b/source/blender/compositor/operations/COM_GlareBaseOperation.h
@@ -23,8 +23,8 @@
namespace blender::compositor {
-/* utility functions used by glare, tonemap and lens distortion */
-/* soms macros for color handling */
+/* Utility functions used by glare, tone-map and lens distortion. */
+/* Some macros for color handling. */
typedef float fRGB[4];
/* TODO: replace with BLI_math_vector. */
diff --git a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc
index e341a88ff71..5ae868c5964 100644
--- a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc
+++ b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc
@@ -73,4 +73,31 @@ void HueSaturationValueCorrectOperation::deinitExecution()
this->m_inputProgram = nullptr;
}
+void HueSaturationValueCorrectOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ float hsv[4];
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ copy_v4_v4(hsv, it.in(0));
+
+ /* Adjust hue, scaling returned default 0.5 up to 1. */
+ float f = BKE_curvemapping_evaluateF(this->m_curveMapping, 0, hsv[0]);
+ hsv[0] += f - 0.5f;
+
+ /* Adjust saturation, scaling returned default 0.5 up to 1. */
+ f = BKE_curvemapping_evaluateF(this->m_curveMapping, 1, hsv[0]);
+ hsv[1] *= (f * 2.0f);
+
+ /* Adjust value, scaling returned default 0.5 up to 1. */
+ f = BKE_curvemapping_evaluateF(this->m_curveMapping, 2, hsv[0]);
+ hsv[2] *= (f * 2.0f);
+
+ hsv[0] = hsv[0] - floorf(hsv[0]); /* Mod 1.0. */
+ CLAMP(hsv[1], 0.0f, 1.0f);
+
+ copy_v4_v4(it.out, hsv);
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h
index 703b2894bdb..6c1b66aba1f 100644
--- a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h
+++ b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h
@@ -47,6 +47,10 @@ class HueSaturationValueCorrectOperation : public CurveBaseOperation {
* Deinitialize the execution
*/
void deinitExecution() override;
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_InvertOperation.cc b/source/blender/compositor/operations/COM_InvertOperation.cc
index 339e40a5d1f..4f71a1d0d1d 100644
--- a/source/blender/compositor/operations/COM_InvertOperation.cc
+++ b/source/blender/compositor/operations/COM_InvertOperation.cc
@@ -30,6 +30,7 @@ InvertOperation::InvertOperation()
this->m_color = true;
this->m_alpha = false;
setResolutionInputSocketIndex(1);
+ this->flags.can_be_constant = true;
}
void InvertOperation::initExecution()
{
@@ -70,4 +71,31 @@ void InvertOperation::deinitExecution()
this->m_inputColorProgram = nullptr;
}
+void InvertOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ const float value = *it.in(0);
+ const float inverted_value = 1.0f - value;
+ const float *color = it.in(1);
+
+ if (this->m_color) {
+ it.out[0] = (1.0f - color[0]) * value + color[0] * inverted_value;
+ it.out[1] = (1.0f - color[1]) * value + color[1] * inverted_value;
+ it.out[2] = (1.0f - color[2]) * value + color[2] * inverted_value;
+ }
+ else {
+ copy_v3_v3(it.out, color);
+ }
+
+ if (this->m_alpha) {
+ it.out[3] = (1.0f - color[3]) * value + color[3] * inverted_value;
+ }
+ else {
+ it.out[3] = color[3];
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_InvertOperation.h b/source/blender/compositor/operations/COM_InvertOperation.h
index 17e5eb95f3e..a084bf5d559 100644
--- a/source/blender/compositor/operations/COM_InvertOperation.h
+++ b/source/blender/compositor/operations/COM_InvertOperation.h
@@ -18,11 +18,11 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
-class InvertOperation : public NodeOperation {
+class InvertOperation : public MultiThreadedOperation {
private:
/**
* Cached reference to the inputProgram
@@ -59,6 +59,10 @@ class InvertOperation : public NodeOperation {
{
this->m_alpha = alpha;
}
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MaskOperation.cc b/source/blender/compositor/operations/COM_MaskOperation.cc
index c7763f08e71..84992f23924 100644
--- a/source/blender/compositor/operations/COM_MaskOperation.cc
+++ b/source/blender/compositor/operations/COM_MaskOperation.cc
@@ -161,4 +161,41 @@ void MaskOperation::executePixelSampled(float output[4],
}
}
+void MaskOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> UNUSED(inputs))
+{
+ Vector<MaskRasterHandle *> handles = get_non_null_handles();
+ if (handles.size() == 0) {
+ output->fill(area, COM_VALUE_ZERO);
+ return;
+ }
+
+ float xy[2];
+ for (BuffersIterator<float> it = output->iterate_with({}, area); !it.is_end(); ++it) {
+ xy[0] = it.x * m_maskWidthInv + m_mask_px_ofs[0];
+ xy[1] = it.y * m_maskHeightInv + m_mask_px_ofs[1];
+ *it.out = 0.0f;
+ for (MaskRasterHandle *handle : handles) {
+ *it.out += BKE_maskrasterize_handle_sample(handle, xy);
+ }
+
+ /* Until we get better falloff. */
+ *it.out /= m_rasterMaskHandleTot;
+ }
+}
+
+Vector<MaskRasterHandle *> MaskOperation::get_non_null_handles() const
+{
+ Vector<MaskRasterHandle *> handles;
+ for (int i = 0; i < m_rasterMaskHandleTot; i++) {
+ MaskRasterHandle *handle = m_rasterMaskHandles[i];
+ if (handle == nullptr) {
+ continue;
+ }
+ handles.append(handle);
+ }
+ return handles;
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MaskOperation.h b/source/blender/compositor/operations/COM_MaskOperation.h
index e8cd9c722df..81e344c0451 100644
--- a/source/blender/compositor/operations/COM_MaskOperation.h
+++ b/source/blender/compositor/operations/COM_MaskOperation.h
@@ -19,7 +19,7 @@
#pragma once
#include "BLI_listbase.h"
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
#include "DNA_mask_types.h"
#include "IMB_imbuf_types.h"
@@ -31,7 +31,7 @@ namespace blender::compositor {
/**
* Class with implementation of mask rasterization
*/
-class MaskOperation : public NodeOperation {
+class MaskOperation : public MultiThreadedOperation {
protected:
Mask *m_mask;
@@ -98,6 +98,13 @@ class MaskOperation : public NodeOperation {
}
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
+
+ private:
+ Vector<MaskRasterHandle *> get_non_null_handles() const;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc
index a9f187258b2..e36e93984fb 100644
--- a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc
+++ b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc
@@ -29,10 +29,21 @@ MovieClipAttributeOperation::MovieClipAttributeOperation()
this->m_framenumber = 0;
this->m_attribute = MCA_X;
this->m_invert = false;
+ needs_resolution_to_get_constant_ = true;
+ is_value_calculated_ = false;
}
void MovieClipAttributeOperation::initExecution()
{
+ if (!is_value_calculated_) {
+ calc_value();
+ }
+}
+
+void MovieClipAttributeOperation::calc_value()
+{
+ BLI_assert(this->get_flags().is_resolution_set);
+ is_value_calculated_ = true;
if (this->m_clip == nullptr) {
return;
}
@@ -83,4 +94,12 @@ void MovieClipAttributeOperation::determineResolution(unsigned int resolution[2]
resolution[1] = preferredResolution[1];
}
+const float *MovieClipAttributeOperation::get_constant_elem()
+{
+ if (!is_value_calculated_) {
+ calc_value();
+ }
+ return &m_value;
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h
index 8507e98d08f..28c39d4dad3 100644
--- a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h
+++ b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h
@@ -18,6 +18,7 @@
#pragma once
+#include "COM_ConstantOperation.h"
#include "COM_NodeOperation.h"
#include "DNA_movieclip_types.h"
@@ -33,13 +34,14 @@ typedef enum MovieClipAttribute {
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
*/
-class MovieClipAttributeOperation : public NodeOperation {
+class MovieClipAttributeOperation : public ConstantOperation {
private:
MovieClip *m_clip;
float m_value;
int m_framenumber;
bool m_invert;
MovieClipAttribute m_attribute;
+ bool is_value_calculated_;
public:
/**
@@ -56,6 +58,8 @@ class MovieClipAttributeOperation : public NodeOperation {
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
+ const float *get_constant_elem() override;
+
void setMovieClip(MovieClip *clip)
{
this->m_clip = clip;
@@ -72,6 +76,9 @@ class MovieClipAttributeOperation : public NodeOperation {
{
this->m_invert = invert;
}
+
+ private:
+ void calc_value();
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.cc b/source/blender/compositor/operations/COM_OutputFileOperation.cc
index 7e896046f01..402d29893a4 100644
--- a/source/blender/compositor/operations/COM_OutputFileOperation.cc
+++ b/source/blender/compositor/operations/COM_OutputFileOperation.cc
@@ -160,7 +160,7 @@ int get_datatype_size(DataType datatype)
static float *init_buffer(unsigned int width, unsigned int height, DataType datatype)
{
- // When initializing the tree during initial load the width and height can be zero.
+ /* When initializing the tree during initial load the width and height can be zero. */
if (width != 0 && height != 0) {
int size = get_datatype_size(datatype);
return (float *)MEM_callocN(width * height * size * sizeof(float), "OutputFile buffer");
@@ -294,6 +294,22 @@ void OutputSingleLayerOperation::deinitExecution()
this->m_imageInput = nullptr;
}
+void OutputSingleLayerOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output),
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ if (!m_outputBuffer) {
+ return;
+ }
+
+ MemoryBuffer output_buf(m_outputBuffer,
+ COM_data_type_num_channels(this->m_datatype),
+ this->getWidth(),
+ this->getHeight());
+ const MemoryBuffer *input_image = inputs[0];
+ output_buf.copy_from(input_image, area);
+}
+
/******************************* MultiLayer *******************************/
OutputOpenExrLayer::OutputOpenExrLayer(const char *name_, DataType datatype_, bool use_layer_)
@@ -444,4 +460,21 @@ void OutputOpenExrMultiLayerOperation::deinitExecution()
}
}
+void OutputOpenExrMultiLayerOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output),
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const MemoryBuffer *input_image = inputs[0];
+ for (int i = 0; i < this->m_layers.size(); i++) {
+ OutputOpenExrLayer &layer = this->m_layers[i];
+ if (layer.outputBuffer) {
+ MemoryBuffer output_buf(layer.outputBuffer,
+ COM_data_type_num_channels(layer.datatype),
+ this->getWidth(),
+ this->getHeight());
+ output_buf.copy_from(input_image, area);
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.h b/source/blender/compositor/operations/COM_OutputFileOperation.h
index 64ab4c06e7c..057cee0c43e 100644
--- a/source/blender/compositor/operations/COM_OutputFileOperation.h
+++ b/source/blender/compositor/operations/COM_OutputFileOperation.h
@@ -18,7 +18,7 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
#include "BLI_path_util.h"
#include "BLI_rect.h"
@@ -30,7 +30,7 @@
namespace blender::compositor {
/* Writes the image to a single-layer file. */
-class OutputSingleLayerOperation : public NodeOperation {
+class OutputSingleLayerOperation : public MultiThreadedOperation {
protected:
const RenderData *m_rd;
const bNodeTree *m_tree;
@@ -70,6 +70,10 @@ class OutputSingleLayerOperation : public NodeOperation {
{
return eCompositorPriority::Low;
}
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
/* extra info for OpenEXR layers */
@@ -86,7 +90,7 @@ struct OutputOpenExrLayer {
};
/* Writes inputs into OpenEXR multilayer channels. */
-class OutputOpenExrMultiLayerOperation : public NodeOperation {
+class OutputOpenExrMultiLayerOperation : public MultiThreadedOperation {
protected:
const Scene *m_scene;
const RenderData *m_rd;
@@ -122,6 +126,10 @@ class OutputOpenExrMultiLayerOperation : public NodeOperation {
{
return eCompositorPriority::Low;
}
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
void add_exr_channels(void *exrhandle,
diff --git a/source/blender/compositor/operations/COM_SetColorOperation.cc b/source/blender/compositor/operations/COM_SetColorOperation.cc
index bfe735aab15..dbe45fa60db 100644
--- a/source/blender/compositor/operations/COM_SetColorOperation.cc
+++ b/source/blender/compositor/operations/COM_SetColorOperation.cc
@@ -24,7 +24,6 @@ SetColorOperation::SetColorOperation()
{
this->addOutputSocket(DataType::Color);
flags.is_set_operation = true;
- flags.is_fullframe_operation = true;
}
void SetColorOperation::executePixelSampled(float output[4],
@@ -42,13 +41,4 @@ void SetColorOperation::determineResolution(unsigned int resolution[2],
resolution[1] = preferredResolution[1];
}
-void SetColorOperation::update_memory_buffer(MemoryBuffer *output,
- const rcti &area,
- Span<MemoryBuffer *> UNUSED(inputs))
-{
- BLI_assert(output->is_a_single_elem());
- float *out_elem = output->get_elem(area.xmin, area.ymin);
- copy_v4_v4(out_elem, m_color);
-}
-
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetColorOperation.h b/source/blender/compositor/operations/COM_SetColorOperation.h
index f4c0948ee1b..f546d5e7668 100644
--- a/source/blender/compositor/operations/COM_SetColorOperation.h
+++ b/source/blender/compositor/operations/COM_SetColorOperation.h
@@ -85,10 +85,6 @@ class SetColorOperation : public ConstantOperation {
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
-
- void update_memory_buffer(MemoryBuffer *output,
- const rcti &area,
- Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetValueOperation.cc b/source/blender/compositor/operations/COM_SetValueOperation.cc
index c12fb106afd..ef43cf64653 100644
--- a/source/blender/compositor/operations/COM_SetValueOperation.cc
+++ b/source/blender/compositor/operations/COM_SetValueOperation.cc
@@ -24,7 +24,6 @@ SetValueOperation::SetValueOperation()
{
this->addOutputSocket(DataType::Value);
flags.is_set_operation = true;
- flags.is_fullframe_operation = true;
}
void SetValueOperation::executePixelSampled(float output[4],
@@ -42,13 +41,4 @@ void SetValueOperation::determineResolution(unsigned int resolution[2],
resolution[1] = preferredResolution[1];
}
-void SetValueOperation::update_memory_buffer(MemoryBuffer *output,
- const rcti &area,
- Span<MemoryBuffer *> UNUSED(inputs))
-{
- BLI_assert(output->is_a_single_elem());
- float *out_elem = output->get_elem(area.xmin, area.ymin);
- *out_elem = m_value;
-}
-
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SetValueOperation.h b/source/blender/compositor/operations/COM_SetValueOperation.h
index f18d44d9554..726624c1c6a 100644
--- a/source/blender/compositor/operations/COM_SetValueOperation.h
+++ b/source/blender/compositor/operations/COM_SetValueOperation.h
@@ -56,9 +56,6 @@ class SetValueOperation : public ConstantOperation {
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
void determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2]) override;
- void update_memory_buffer(MemoryBuffer *output,
- const rcti &area,
- Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SplitOperation.cc b/source/blender/compositor/operations/COM_SplitOperation.cc
index a4754de370d..d18ed3b8e14 100644
--- a/source/blender/compositor/operations/COM_SplitOperation.cc
+++ b/source/blender/compositor/operations/COM_SplitOperation.cc
@@ -79,4 +79,17 @@ void SplitOperation::determineResolution(unsigned int resolution[2],
NodeOperation::determineResolution(resolution, preferredResolution);
}
+void SplitOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ const int percent = this->m_xSplit ? this->m_splitPercentage * this->getWidth() / 100.0f :
+ this->m_splitPercentage * this->getHeight() / 100.0f;
+ const size_t elem_bytes = COM_data_type_bytes_len(getOutputSocket()->getDataType());
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ const bool is_image1 = this->m_xSplit ? it.x > percent : it.y > percent;
+ memcpy(it.out, it.in(is_image1 ? 0 : 1), elem_bytes);
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SplitOperation.h b/source/blender/compositor/operations/COM_SplitOperation.h
index 09e48821dd0..2d09d2a07dc 100644
--- a/source/blender/compositor/operations/COM_SplitOperation.h
+++ b/source/blender/compositor/operations/COM_SplitOperation.h
@@ -18,11 +18,11 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
-class SplitOperation : public NodeOperation {
+class SplitOperation : public MultiThreadedOperation {
private:
SocketReader *m_image1Input;
SocketReader *m_image2Input;
@@ -45,6 +45,10 @@ class SplitOperation : public NodeOperation {
{
this->m_xSplit = xsplit;
}
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SunBeamsOperation.cc b/source/blender/compositor/operations/COM_SunBeamsOperation.cc
index bd82b6397ad..ad96e3a02ba 100644
--- a/source/blender/compositor/operations/COM_SunBeamsOperation.cc
+++ b/source/blender/compositor/operations/COM_SunBeamsOperation.cc
@@ -30,7 +30,7 @@ SunBeamsOperation::SunBeamsOperation()
this->flags.complex = true;
}
-void SunBeamsOperation::initExecution()
+void SunBeamsOperation::calc_rays_common_data()
{
/* convert to pixels */
this->m_source_px[0] = this->m_data.source[0] * this->getWidth();
@@ -38,6 +38,11 @@ void SunBeamsOperation::initExecution()
this->m_ray_length_px = this->m_data.ray_length * MAX2(this->getWidth(), this->getHeight());
}
+void SunBeamsOperation::initExecution()
+{
+ calc_rays_common_data();
+}
+
/**
* Defines a line accumulator for a specific sector,
* given by the four matrix entries that rotate from buffer space into the sector
@@ -140,7 +145,7 @@ template<int fxu, int fxv, int fyu, int fyv> struct BufferLineAccumulator {
falloff_factor = dist_max > dist_min ? dr / (float)(dist_max - dist_min) : 0.0f;
- float *iter = input->getBuffer() + COM_DATA_TYPE_COLOR_CHANNELS * (x + input->getWidth() * y);
+ float *iter = input->getBuffer() + input->get_coords_offset(x, y);
return iter;
}
@@ -159,7 +164,6 @@ template<int fxu, int fxv, int fyu, int fyv> struct BufferLineAccumulator {
float dist_max)
{
const rcti &rect = input->get_rect();
- int buffer_width = input->getWidth();
int x, y, num;
float v, dv;
float falloff_factor;
@@ -168,9 +172,7 @@ template<int fxu, int fxv, int fyu, int fyv> struct BufferLineAccumulator {
zero_v4(output);
if ((int)(co[0] - source[0]) == 0 && (int)(co[1] - source[1]) == 0) {
- copy_v4_v4(output,
- input->getBuffer() + COM_DATA_TYPE_COLOR_CHANNELS *
- ((int)source[0] + input->getWidth() * (int)source[1]));
+ copy_v4_v4(output, input->get_elem(source[0], source[1]));
return;
}
@@ -210,7 +212,7 @@ template<int fxu, int fxv, int fyu, int fyv> struct BufferLineAccumulator {
/* decrement u */
x -= fxu;
y -= fyu;
- buffer -= (fxu + fyu * buffer_width) * COM_DATA_TYPE_COLOR_CHANNELS;
+ buffer -= fxu * input->elem_stride + fyu * input->row_stride;
/* decrement v (in steps of dv < 1) */
v_local -= dv;
@@ -219,7 +221,7 @@ template<int fxu, int fxv, int fyu, int fyv> struct BufferLineAccumulator {
x -= fxv;
y -= fyv;
- buffer -= (fxv + fyv * buffer_width) * COM_DATA_TYPE_COLOR_CHANNELS;
+ buffer -= fxv * input->elem_stride + fyv * input->row_stride;
}
}
@@ -356,4 +358,39 @@ bool SunBeamsOperation::determineDependingAreaOfInterest(rcti *input,
return NodeOperation::determineDependingAreaOfInterest(&rect, readOperation, output);
}
+void SunBeamsOperation::get_area_of_interest(const int input_idx,
+ const rcti &output_area,
+ rcti &r_input_area)
+{
+ BLI_assert(input_idx == 0);
+ UNUSED_VARS(input_idx);
+ calc_rays_common_data();
+
+ r_input_area = output_area;
+ /* Enlarges the rect by moving each corner toward the source.
+ * This is the maximum distance that pixels can influence each other
+ * and gives a rect that contains all possible accumulated pixels. */
+ calc_ray_shift(&r_input_area, output_area.xmin, output_area.ymin, m_source_px, m_ray_length_px);
+ calc_ray_shift(&r_input_area, output_area.xmin, output_area.ymax, m_source_px, m_ray_length_px);
+ calc_ray_shift(&r_input_area, output_area.xmax, output_area.ymin, m_source_px, m_ray_length_px);
+ calc_ray_shift(&r_input_area, output_area.xmax, output_area.ymax, m_source_px, m_ray_length_px);
+}
+
+void SunBeamsOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ MemoryBuffer *input = inputs[0];
+ float coords[2];
+ for (int y = area.ymin; y < area.ymax; y++) {
+ coords[1] = y;
+ float *out_elem = output->get_elem(area.xmin, y);
+ for (int x = area.xmin; x < area.xmax; x++) {
+ coords[0] = x;
+ accumulate_line(input, out_elem, coords, m_source_px, 0.0f, m_ray_length_px);
+ out_elem += output->elem_stride;
+ }
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_SunBeamsOperation.h b/source/blender/compositor/operations/COM_SunBeamsOperation.h
index d3725021cde..71fc04453fe 100644
--- a/source/blender/compositor/operations/COM_SunBeamsOperation.h
+++ b/source/blender/compositor/operations/COM_SunBeamsOperation.h
@@ -17,11 +17,11 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
namespace blender::compositor {
-class SunBeamsOperation : public NodeOperation {
+class SunBeamsOperation : public MultiThreadedOperation {
public:
SunBeamsOperation();
@@ -40,6 +40,14 @@ class SunBeamsOperation : public NodeOperation {
m_data = data;
}
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
+ void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
+
+ private:
+ void calc_rays_common_data();
+
private:
NodeSunBeams m_data;
diff --git a/source/blender/compositor/operations/COM_TonemapOperation.cc b/source/blender/compositor/operations/COM_TonemapOperation.cc
index 6bfacb0c75d..20da468eeb1 100644
--- a/source/blender/compositor/operations/COM_TonemapOperation.cc
+++ b/source/blender/compositor/operations/COM_TonemapOperation.cc
@@ -17,6 +17,8 @@
*/
#include "COM_TonemapOperation.h"
+#include "COM_ExecutionSystem.h"
+
#include "BLI_math.h"
#include "BLI_utildefines.h"
@@ -153,4 +155,126 @@ void TonemapOperation::deinitializeTileData(rcti * /*rect*/, void * /*data*/)
/* pass */
}
+void TonemapOperation::get_area_of_interest(const int input_idx,
+ const rcti &UNUSED(output_area),
+ rcti &r_input_area)
+{
+ BLI_assert(input_idx == 0);
+ NodeOperation *operation = getInputOperation(input_idx);
+ r_input_area.xmin = 0;
+ r_input_area.ymin = 0;
+ r_input_area.xmax = operation->getWidth();
+ r_input_area.ymax = operation->getHeight();
+}
+
+struct Luminance {
+ float sum;
+ float color_sum[3];
+ float log_sum;
+ float min;
+ float max;
+ int num_pixels;
+};
+
+static Luminance calc_area_luminance(const MemoryBuffer *input, const rcti &area)
+{
+ Luminance lum = {0};
+ for (const float *elem : input->get_buffer_area(area)) {
+ const float lu = IMB_colormanagement_get_luminance(elem);
+ lum.sum += lu;
+ add_v3_v3(lum.color_sum, elem);
+ lum.log_sum += logf(MAX2(lu, 0.0f) + 1e-5f);
+ lum.max = MAX2(lu, lum.max);
+ lum.min = MIN2(lu, lum.min);
+ lum.num_pixels++;
+ }
+ return lum;
+}
+
+void TonemapOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output),
+ const rcti &UNUSED(area),
+ Span<MemoryBuffer *> inputs)
+{
+ if (this->m_cachedInstance == nullptr) {
+ Luminance lum = {0};
+ const MemoryBuffer *input = inputs[0];
+ exec_system_->execute_work<Luminance>(
+ input->get_rect(),
+ [=](const rcti &split) { return calc_area_luminance(input, split); },
+ lum,
+ [](Luminance &join, const Luminance &chunk) {
+ join.sum += chunk.sum;
+ add_v3_v3(join.color_sum, chunk.color_sum);
+ join.log_sum += chunk.log_sum;
+ join.max = MAX2(join.max, chunk.max);
+ join.min = MIN2(join.min, chunk.min);
+ join.num_pixels += chunk.num_pixels;
+ });
+
+ AvgLogLum *avg = new AvgLogLum();
+ avg->lav = lum.sum / lum.num_pixels;
+ mul_v3_v3fl(avg->cav, lum.color_sum, 1.0f / lum.num_pixels);
+ const float max_log = log((double)lum.max + 1e-5);
+ const float min_log = log((double)lum.min + 1e-5);
+ const float avg_log = lum.log_sum / lum.num_pixels;
+ avg->auto_key = (max_log > min_log) ? ((max_log - avg_log) / (max_log - min_log)) : 1.0f;
+ const float al = exp((double)avg_log);
+ avg->al = (al == 0.0f) ? 0.0f : (this->m_data->key / al);
+ avg->igm = (this->m_data->gamma == 0.0f) ? 1 : (1.0f / this->m_data->gamma);
+ this->m_cachedInstance = avg;
+ }
+}
+
+void TonemapOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ AvgLogLum *avg = m_cachedInstance;
+ const float igm = avg->igm;
+ const float offset = this->m_data->offset;
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ copy_v4_v4(it.out, it.in(0));
+ mul_v3_fl(it.out, avg->al);
+ float dr = it.out[0] + offset;
+ float dg = it.out[1] + offset;
+ float db = it.out[2] + offset;
+ it.out[0] /= ((dr == 0.0f) ? 1.0f : dr);
+ it.out[1] /= ((dg == 0.0f) ? 1.0f : dg);
+ it.out[2] /= ((db == 0.0f) ? 1.0f : db);
+ if (igm != 0.0f) {
+ it.out[0] = powf(MAX2(it.out[0], 0.0f), igm);
+ it.out[1] = powf(MAX2(it.out[1], 0.0f), igm);
+ it.out[2] = powf(MAX2(it.out[2], 0.0f), igm);
+ }
+ }
+}
+
+void PhotoreceptorTonemapOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ AvgLogLum *avg = m_cachedInstance;
+ NodeTonemap *ntm = this->m_data;
+ const float f = expf(-this->m_data->f);
+ const float m = (ntm->m > 0.0f) ? ntm->m : (0.3f + 0.7f * powf(avg->auto_key, 1.4f));
+ const float ic = 1.0f - ntm->c;
+ const float ia = 1.0f - ntm->a;
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ copy_v4_v4(it.out, it.in(0));
+ const float L = IMB_colormanagement_get_luminance(it.out);
+ float I_l = it.out[0] + ic * (L - it.out[0]);
+ float I_g = avg->cav[0] + ic * (avg->lav - avg->cav[0]);
+ float I_a = I_l + ia * (I_g - I_l);
+ it.out[0] /= (it.out[0] + powf(f * I_a, m));
+ I_l = it.out[1] + ic * (L - it.out[1]);
+ I_g = avg->cav[1] + ic * (avg->lav - avg->cav[1]);
+ I_a = I_l + ia * (I_g - I_l);
+ it.out[1] /= (it.out[1] + powf(f * I_a, m));
+ I_l = it.out[2] + ic * (L - it.out[2]);
+ I_g = avg->cav[2] + ic * (avg->lav - avg->cav[2]);
+ I_a = I_l + ia * (I_g - I_l);
+ it.out[2] /= (it.out[2] + powf(f * I_a, m));
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_TonemapOperation.h b/source/blender/compositor/operations/COM_TonemapOperation.h
index 7ecb179504d..56b57730ec1 100644
--- a/source/blender/compositor/operations/COM_TonemapOperation.h
+++ b/source/blender/compositor/operations/COM_TonemapOperation.h
@@ -18,7 +18,7 @@
#pragma once
-#include "COM_NodeOperation.h"
+#include "COM_MultiThreadedOperation.h"
#include "DNA_node_types.h"
namespace blender::compositor {
@@ -39,7 +39,7 @@ typedef struct AvgLogLum {
* \brief base class of tonemap, implementing the simple tonemap
* \ingroup operation
*/
-class TonemapOperation : public NodeOperation {
+class TonemapOperation : public MultiThreadedOperation {
protected:
/**
* \brief Cached reference to the reader
@@ -85,6 +85,14 @@ class TonemapOperation : public NodeOperation {
bool determineDependingAreaOfInterest(rcti *input,
ReadBufferOperation *readOperation,
rcti *output) override;
+
+ void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
+ void update_memory_buffer_started(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
+ virtual void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
/**
@@ -99,6 +107,10 @@ class PhotoreceptorTonemapOperation : public TonemapOperation {
* The inner loop of this operation.
*/
void executePixel(float output[4], int x, int y, void *data) override;
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_TrackPositionOperation.cc b/source/blender/compositor/operations/COM_TrackPositionOperation.cc
index 993410e3e84..0f4be16a620 100644
--- a/source/blender/compositor/operations/COM_TrackPositionOperation.cc
+++ b/source/blender/compositor/operations/COM_TrackPositionOperation.cc
@@ -42,14 +42,24 @@ TrackPositionOperation::TrackPositionOperation()
this->m_relativeFrame = 0;
this->m_speed_output = false;
flags.is_set_operation = true;
+ is_track_position_calculated_ = false;
}
void TrackPositionOperation::initExecution()
{
+ if (!is_track_position_calculated_) {
+ calc_track_position();
+ }
+}
+
+void TrackPositionOperation::calc_track_position()
+{
+ is_track_position_calculated_ = true;
MovieTracking *tracking = nullptr;
MovieClipUser user = {0};
MovieTrackingObject *object;
+ track_position_ = 0;
zero_v2(this->m_markerPos);
zero_v2(this->m_relativePos);
@@ -114,6 +124,14 @@ void TrackPositionOperation::initExecution()
}
}
}
+
+ track_position_ = this->m_markerPos[this->m_axis] - this->m_relativePos[this->m_axis];
+ if (this->m_axis == 0) {
+ track_position_ *= this->m_width;
+ }
+ else {
+ track_position_ *= this->m_height;
+ }
}
void TrackPositionOperation::executePixelSampled(float output[4],
@@ -131,6 +149,14 @@ void TrackPositionOperation::executePixelSampled(float output[4],
}
}
+const float *TrackPositionOperation::get_constant_elem()
+{
+ if (!is_track_position_calculated_) {
+ calc_track_position();
+ }
+ return &track_position_;
+}
+
void TrackPositionOperation::determineResolution(unsigned int resolution[2],
unsigned int preferredResolution[2])
{
diff --git a/source/blender/compositor/operations/COM_TrackPositionOperation.h b/source/blender/compositor/operations/COM_TrackPositionOperation.h
index b0b0a123bd6..f716bd97737 100644
--- a/source/blender/compositor/operations/COM_TrackPositionOperation.h
+++ b/source/blender/compositor/operations/COM_TrackPositionOperation.h
@@ -20,7 +20,7 @@
#include <string.h>
-#include "COM_NodeOperation.h"
+#include "COM_ConstantOperation.h"
#include "DNA_movieclip_types.h"
#include "DNA_tracking_types.h"
@@ -33,7 +33,7 @@ namespace blender::compositor {
/**
* Class with implementation of green screen gradient rasterization
*/
-class TrackPositionOperation : public NodeOperation {
+class TrackPositionOperation : public ConstantOperation {
protected:
MovieClip *m_movieClip;
int m_framenumber;
@@ -47,6 +47,8 @@ class TrackPositionOperation : public NodeOperation {
int m_width, m_height;
float m_markerPos[2];
float m_relativePos[2];
+ float track_position_;
+ bool is_track_position_calculated_;
/**
* Determine the output resolution. The resolution is retrieved from the Renderer
@@ -93,6 +95,11 @@ class TrackPositionOperation : public NodeOperation {
void initExecution() override;
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ const float *get_constant_elem() override;
+
+ private:
+ void calc_track_position();
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_VectorCurveOperation.cc b/source/blender/compositor/operations/COM_VectorCurveOperation.cc
index 9d53ed5d8ee..c2087fd071e 100644
--- a/source/blender/compositor/operations/COM_VectorCurveOperation.cc
+++ b/source/blender/compositor/operations/COM_VectorCurveOperation.cc
@@ -53,4 +53,14 @@ void VectorCurveOperation::deinitExecution()
this->m_inputProgram = nullptr;
}
+void VectorCurveOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ CurveMapping *curve_map = this->m_curveMapping;
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ BKE_curvemapping_evaluate_premulRGBF(curve_map, it.out, it.in(0));
+ }
+}
+
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_VectorCurveOperation.h b/source/blender/compositor/operations/COM_VectorCurveOperation.h
index 8cbb80e27c7..27b3ad69e17 100644
--- a/source/blender/compositor/operations/COM_VectorCurveOperation.h
+++ b/source/blender/compositor/operations/COM_VectorCurveOperation.h
@@ -47,6 +47,10 @@ class VectorCurveOperation : public CurveBaseOperation {
* Deinitialize the execution
*/
void deinitExecution() override;
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_WrapOperation.cc b/source/blender/compositor/operations/COM_WrapOperation.cc
index d0d2fcac3ac..888602114cc 100644
--- a/source/blender/compositor/operations/COM_WrapOperation.cc
+++ b/source/blender/compositor/operations/COM_WrapOperation.cc
@@ -57,20 +57,20 @@ void WrapOperation::executePixelSampled(float output[4], float x, float y, Pixel
MemoryBufferExtend extend_x = MemoryBufferExtend::Clip, extend_y = MemoryBufferExtend::Clip;
switch (m_wrappingType) {
case CMP_NODE_WRAP_NONE:
- // Intentionally empty, originalXPos and originalYPos have been set before
+ /* Intentionally empty, originalXPos and originalYPos have been set before. */
break;
case CMP_NODE_WRAP_X:
- // wrap only on the x-axis
+ /* Wrap only on the x-axis. */
nx = this->getWrappedOriginalXPos(x);
extend_x = MemoryBufferExtend::Repeat;
break;
case CMP_NODE_WRAP_Y:
- // wrap only on the y-axis
+ /* Wrap only on the y-axis. */
ny = this->getWrappedOriginalYPos(y);
extend_y = MemoryBufferExtend::Repeat;
break;
case CMP_NODE_WRAP_XY:
- // wrap on both
+ /* Wrap on both. */
nx = this->getWrappedOriginalXPos(x);
ny = this->getWrappedOriginalYPos(y);
extend_x = MemoryBufferExtend::Repeat;
@@ -92,7 +92,7 @@ bool WrapOperation::determineDependingAreaOfInterest(rcti *input,
newInput.ymax = input->ymax;
if (ELEM(m_wrappingType, CMP_NODE_WRAP_X, CMP_NODE_WRAP_XY)) {
- // wrap only on the x-axis if tile is wrapping
+ /* Wrap only on the x-axis if tile is wrapping. */
newInput.xmin = getWrappedOriginalXPos(input->xmin);
newInput.xmax = roundf(getWrappedOriginalXPos(input->xmax));
if (newInput.xmin >= newInput.xmax) {
@@ -101,7 +101,7 @@ bool WrapOperation::determineDependingAreaOfInterest(rcti *input,
}
}
if (ELEM(m_wrappingType, CMP_NODE_WRAP_Y, CMP_NODE_WRAP_XY)) {
- // wrap only on the y-axis if tile is wrapping
+ /* Wrap only on the y-axis if tile is wrapping. */
newInput.ymin = getWrappedOriginalYPos(input->ymin);
newInput.ymax = roundf(getWrappedOriginalYPos(input->ymax));
if (newInput.ymin >= newInput.ymax) {
diff --git a/source/blender/compositor/operations/COM_ZCombineOperation.cc b/source/blender/compositor/operations/COM_ZCombineOperation.cc
index 9d3ca7e736e..7050c3b2d83 100644
--- a/source/blender/compositor/operations/COM_ZCombineOperation.cc
+++ b/source/blender/compositor/operations/COM_ZCombineOperation.cc
@@ -33,6 +33,7 @@ ZCombineOperation::ZCombineOperation()
this->m_depth1Reader = nullptr;
this->m_image2Reader = nullptr;
this->m_depth2Reader = nullptr;
+ this->flags.can_be_constant = true;
}
void ZCombineOperation::initExecution()
@@ -60,6 +61,19 @@ void ZCombineOperation::executePixelSampled(float output[4],
this->m_image2Reader->readSampled(output, x, y, sampler);
}
}
+
+void ZCombineOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ const float depth1 = *it.in(1);
+ const float depth2 = *it.in(3);
+ const float *color = (depth1 < depth2) ? it.in(0) : it.in(2);
+ copy_v4_v4(it.out, color);
+ }
+}
+
void ZCombineAlphaOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -88,6 +102,32 @@ void ZCombineAlphaOperation::executePixelSampled(float output[4],
output[3] = MAX2(color1[3], color2[3]);
}
+void ZCombineAlphaOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ const float depth1 = *it.in(1);
+ const float depth2 = *it.in(3);
+ const float *color1;
+ const float *color2;
+ if (depth1 <= depth2) {
+ color1 = it.in(0);
+ color2 = it.in(2);
+ }
+ else {
+ color1 = it.in(2);
+ color2 = it.in(0);
+ }
+ const float fac = color1[3];
+ const float ifac = 1.0f - fac;
+ it.out[0] = fac * color1[0] + ifac * color2[0];
+ it.out[1] = fac * color1[1] + ifac * color2[1];
+ it.out[2] = fac * color1[2] + ifac * color2[2];
+ it.out[3] = MAX2(color1[3], color2[3]);
+ }
+}
+
void ZCombineOperation::deinitExecution()
{
this->m_image1Reader = nullptr;
@@ -132,6 +172,18 @@ void ZCombineMaskOperation::executePixelSampled(float output[4],
interp_v4_v4v4(output, color1, color2, 1.0f - mask[0]);
}
+void ZCombineMaskOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ const float mask = *it.in(0);
+ const float *color1 = it.in(1);
+ const float *color2 = it.in(2);
+ interp_v4_v4v4(it.out, color1, color2, 1.0f - mask);
+ }
+}
+
void ZCombineMaskAlphaOperation::executePixelSampled(float output[4],
float x,
float y,
@@ -154,6 +206,24 @@ void ZCombineMaskAlphaOperation::executePixelSampled(float output[4],
output[3] = MAX2(color1[3], color2[3]);
}
+void ZCombineMaskAlphaOperation::update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs)
+{
+ for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
+ const float mask = *it.in(0);
+ const float *color1 = it.in(1);
+ const float *color2 = it.in(2);
+ const float fac = (1.0f - mask) * (1.0f - color1[3]) + mask * color2[3];
+ const float mfac = 1.0f - fac;
+
+ it.out[0] = color1[0] * mfac + color2[0] * fac;
+ it.out[1] = color1[1] * mfac + color2[1] * fac;
+ it.out[2] = color1[2] * mfac + color2[2] * fac;
+ it.out[3] = MAX2(color1[3], color2[3]);
+ }
+}
+
void ZCombineMaskOperation::deinitExecution()
{
this->m_image1Reader = nullptr;
diff --git a/source/blender/compositor/operations/COM_ZCombineOperation.h b/source/blender/compositor/operations/COM_ZCombineOperation.h
index d0b1aee7310..acd60b6c866 100644
--- a/source/blender/compositor/operations/COM_ZCombineOperation.h
+++ b/source/blender/compositor/operations/COM_ZCombineOperation.h
@@ -26,7 +26,7 @@ namespace blender::compositor {
* this program converts an input color to an output value.
* it assumes we are in sRGB color space.
*/
-class ZCombineOperation : public NodeOperation {
+class ZCombineOperation : public MultiThreadedOperation {
protected:
SocketReader *m_image1Reader;
SocketReader *m_depth1Reader;
@@ -46,13 +46,21 @@ class ZCombineOperation : public NodeOperation {
* The inner loop of this operation.
*/
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
class ZCombineAlphaOperation : public ZCombineOperation {
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
-class ZCombineMaskOperation : public NodeOperation {
+class ZCombineMaskOperation : public MultiThreadedOperation {
protected:
SocketReader *m_maskReader;
SocketReader *m_image1Reader;
@@ -64,9 +72,17 @@ class ZCombineMaskOperation : public NodeOperation {
void initExecution() override;
void deinitExecution() override;
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
class ZCombineMaskAlphaOperation : public ZCombineMaskOperation {
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
+
+ void update_memory_buffer_partial(MemoryBuffer *output,
+ const rcti &area,
+ Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor
diff --git a/source/blender/datatoc/datatoc.c b/source/blender/datatoc/datatoc.c
index 62b4cee4af0..816353be9de 100644
--- a/source/blender/datatoc/datatoc.c
+++ b/source/blender/datatoc/datatoc.c
@@ -100,15 +100,14 @@ int main(int argc, char **argv)
fprintf(fpout, "const int datatoc_%s_size = %d;\n", argv[1], (int)size);
fprintf(fpout, "const char datatoc_%s[] = {\n", argv[1]);
while (size--) {
- /* if we want to open in an editor
- * this is nicer to avoid very long lines */
-#ifdef VERBOSE
+ /* Even though this file is generated and doesn't need new-lines,
+ * these files may be loaded by developers when looking up symbols.
+ * Avoid a very long single line that may lock-up some editors. */
if (size % 32 == 31) {
fprintf(fpout, "\n");
}
-#endif
- /* fprintf (fpout, "\\x%02x", getc(fpin)); */
+ // fprintf(fpout, "\\x%02x", getc(fpin));
fprintf(fpout, "%3d,", getc(fpin));
}
diff --git a/source/blender/depsgraph/DEG_depsgraph.h b/source/blender/depsgraph/DEG_depsgraph.h
index 749b1bba871..47dad17b482 100644
--- a/source/blender/depsgraph/DEG_depsgraph.h
+++ b/source/blender/depsgraph/DEG_depsgraph.h
@@ -85,7 +85,7 @@ extern "C" {
// Get main depsgraph instance from context!
/* Create new Depsgraph instance */
-// TODO: what args are needed here? What's the building-graph entry point?
+/* TODO: what args are needed here? What's the building-graph entry point? */
Depsgraph *DEG_graph_new(struct Main *bmain,
struct Scene *scene,
struct ViewLayer *view_layer,
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
index 8d1074d912f..22bce10937d 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
@@ -648,9 +648,9 @@ void DepsgraphNodeBuilder::build_idproperties(IDProperty *id_property)
void DepsgraphNodeBuilder::build_collection(LayerCollection *from_layer_collection,
Collection *collection)
{
- const int restrict_flag = (graph_->mode == DAG_EVAL_VIEWPORT) ? COLLECTION_RESTRICT_VIEWPORT :
- COLLECTION_RESTRICT_RENDER;
- const bool is_collection_restricted = (collection->flag & restrict_flag);
+ const int visibility_flag = (graph_->mode == DAG_EVAL_VIEWPORT) ? COLLECTION_HIDE_VIEWPORT :
+ COLLECTION_HIDE_RENDER;
+ const bool is_collection_restricted = (collection->flag & visibility_flag);
const bool is_collection_visible = !is_collection_restricted && is_parent_collection_visible_;
IDNode *id_node;
if (built_map_.checkIsBuiltAndTag(collection)) {
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc
index 29aa05b83db..b6e3f4fa935 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc
@@ -60,11 +60,11 @@ namespace blender::deg {
void DepsgraphNodeBuilder::build_layer_collections(ListBase *lb)
{
- const int restrict_flag = (graph_->mode == DAG_EVAL_VIEWPORT) ? COLLECTION_RESTRICT_VIEWPORT :
- COLLECTION_RESTRICT_RENDER;
+ const int visibility_flag = (graph_->mode == DAG_EVAL_VIEWPORT) ? COLLECTION_HIDE_VIEWPORT :
+ COLLECTION_HIDE_RENDER;
for (LayerCollection *lc = (LayerCollection *)lb->first; lc; lc = lc->next) {
- if (lc->collection->flag & restrict_flag) {
+ if (lc->collection->flag & visibility_flag) {
continue;
}
if ((lc->flag & LAYER_COLLECTION_EXCLUDE) == 0) {
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
index c7c6fafa512..d88e9bc9c04 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
@@ -118,6 +118,7 @@
#include "intern/node/deg_node_operation.h"
#include "intern/node/deg_node_time.h"
+#include "intern/depsgraph.h"
#include "intern/depsgraph_relation.h"
#include "intern/depsgraph_type.h"
@@ -2095,7 +2096,7 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object)
ctx.node = reinterpret_cast<::DepsNodeHandle *>(&handle);
mti->updateDepsgraph(md, &ctx);
}
- if (BKE_object_modifier_use_time(object, md)) {
+ if (BKE_object_modifier_use_time(scene_, object, md, graph_->mode)) {
TimeSourceKey time_src_key;
add_relation(time_src_key, obdata_ubereval_key, "Time Source");
}
@@ -2224,6 +2225,11 @@ void DepsgraphRelationBuilder::build_object_data_geometry_datablock(ID *obdata)
OperationKey obdata_geom_eval_key(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL);
OperationKey obdata_geom_done_key(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_DONE);
add_relation(obdata_geom_eval_key, obdata_geom_done_key, "ObData Geom Eval Done");
+
+ /* Link object data evaluation to parameter evaluation. */
+ ComponentKey parameters_key(obdata, NodeType::PARAMETERS);
+ add_relation(parameters_key, obdata_geom_eval_key, "ObData Geom Params");
+
/* Type-specific links. */
const ID_Type id_type = GS(obdata->name);
switch (id_type) {
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 24876049942..c37fb1b83a4 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
@@ -61,11 +61,11 @@ namespace blender::deg {
void DepsgraphRelationBuilder::build_layer_collections(ListBase *lb)
{
- const int restrict_flag = (graph_->mode == DAG_EVAL_VIEWPORT) ? COLLECTION_RESTRICT_VIEWPORT :
- COLLECTION_RESTRICT_RENDER;
+ const int visibility_flag = (graph_->mode == DAG_EVAL_VIEWPORT) ? COLLECTION_HIDE_VIEWPORT :
+ COLLECTION_HIDE_RENDER;
for (LayerCollection *lc = (LayerCollection *)lb->first; lc; lc = lc->next) {
- if ((lc->collection->flag & restrict_flag)) {
+ if (lc->collection->flag & visibility_flag) {
continue;
}
if ((lc->flag & LAYER_COLLECTION_EXCLUDE) == 0) {
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc
index bdabd67cc07..40e59875832 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc
@@ -400,8 +400,8 @@ RNANodeQueryIDData *RNANodeQuery::ensure_id_data(const ID *id)
bool rna_prop_affects_parameters_node(const PointerRNA *ptr, const PropertyRNA *prop)
{
return prop != nullptr && RNA_property_is_idprop(prop) &&
- /* ID properties in the geometry nodes modifier don't affect that parameters node. Instead
- they affect the modifier and therefore the geometry node directly. */
+ /* ID properties in the geometry nodes modifier don't affect that parameters node.
+ * Instead they affect the modifier and therefore the geometry node directly. */
!RNA_struct_is_a(ptr->type, &RNA_NodesModifier);
}
diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc
index ab93464d09a..dd96c5a3b2b 100644
--- a/source/blender/depsgraph/intern/depsgraph_tag.cc
+++ b/source/blender/depsgraph/intern/depsgraph_tag.cc
@@ -646,8 +646,8 @@ void graph_id_tag_update(
{
const int debug_flags = (graph != nullptr) ? DEG_debug_flags_get((::Depsgraph *)graph) : G.debug;
if (graph != nullptr && graph->is_evaluating) {
- if (debug_flags & G_DEBUG_DEPSGRAPH) {
- printf("ID tagged for update during dependency graph evaluation.");
+ if (debug_flags & G_DEBUG_DEPSGRAPH_TAG) {
+ printf("ID tagged for update during dependency graph evaluation.\n");
}
return;
}
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h
index 255ea840088..d0bb841caab 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h
@@ -68,11 +68,11 @@ ID *deg_expand_copy_on_write_datablock(const struct Depsgraph *depsgraph,
ID *deg_update_copy_on_write_datablock(const struct Depsgraph *depsgraph, const IDNode *id_node);
ID *deg_update_copy_on_write_datablock(const struct Depsgraph *depsgraph, struct ID *id_orig);
-/* Helper function which frees memory used by copy-on-written databnlock. */
+/* Helper function which frees memory used by copy-on-written data-block. */
void deg_free_copy_on_write_datablock(struct ID *id_cow);
/* Callback function for depsgraph operation node which ensures copy-on-write
- * datablock is ready for use by further evaluation routines.
+ * data-block is ready for use by further evaluation routines.
*/
void deg_evaluate_copy_on_write(struct ::Depsgraph *depsgraph, const struct IDNode *id_node);
@@ -80,16 +80,16 @@ void deg_evaluate_copy_on_write(struct ::Depsgraph *depsgraph, const struct IDNo
* copies inside. */
bool deg_validate_copy_on_write_datablock(ID *id_cow);
-/* Tag given ID block as being copy-on-wtritten. */
+/* Tag given ID block as being copy-on-written. */
void deg_tag_copy_on_write_id(struct ID *id_cow, const struct ID *id_orig);
-/* Check whether ID datablock is expanded.
+/* Check whether ID data-block is expanded.
*
* TODO(sergey): Make it an inline function or a macro.
*/
bool deg_copy_on_write_is_expanded(const struct ID *id_cow);
-/* Check whether copy-on-write datablock is needed for given ID.
+/* Check whether copy-on-write data-block is needed for given ID.
*
* There are some exceptions on data-blocks which are covered by dependency graph
* but which we don't want to start duplicating.
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt
index 930d82fa225..257eb80ae0b 100644
--- a/source/blender/draw/CMakeLists.txt
+++ b/source/blender/draw/CMakeLists.txt
@@ -483,6 +483,13 @@ data_to_c_simple(engines/image/shaders/engine_image_vert.glsl SRC)
list(APPEND INC
)
+if(WITH_DRAW_DEBUG)
+ list(APPEND SRC
+ engines/select/select_debug_engine.c
+ )
+ add_definitions(-DWITH_DRAW_DEBUG)
+endif()
+
if(WITH_MOD_FLUID)
list(APPEND INC
../../../intern/mantaflow/extern
diff --git a/source/blender/draw/engines/eevee/eevee_lookdev.c b/source/blender/draw/engines/eevee/eevee_lookdev.c
index f4dc553e982..879a7b08eba 100644
--- a/source/blender/draw/engines/eevee/eevee_lookdev.c
+++ b/source/blender/draw/engines/eevee/eevee_lookdev.c
@@ -246,7 +246,7 @@ void EEVEE_lookdev_cache_init(EEVEE_Data *vedata,
DRW_shgroup_uniform_float_copy(grp, "studioLightIntensity", shading->studiolight_intensity);
BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE);
DRW_shgroup_uniform_texture_ex(grp, "studioLight", sl->equirect_radiance_gputexture, state);
- /* Do not fadeout when doing probe rendering, only when drawing the background */
+ /* Do not fade-out when doing probe rendering, only when drawing the background. */
DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", 1.0f);
}
else {
diff --git a/source/blender/draw/engines/overlay/overlay_edit_mesh.c b/source/blender/draw/engines/overlay/overlay_edit_mesh.c
index a7ed6c777e8..3a2871249a2 100644
--- a/source/blender/draw/engines/overlay/overlay_edit_mesh.c
+++ b/source/blender/draw/engines/overlay/overlay_edit_mesh.c
@@ -132,6 +132,11 @@ void OVERLAY_edit_mesh_cache_init(OVERLAY_Data *vedata)
DRW_shgroup_uniform_float_copy(grp, "normalSize", v3d->overlay.normals_length);
DRW_shgroup_uniform_float_copy(grp, "alpha", backwire_opacity);
DRW_shgroup_uniform_texture_ref(grp, "depthTex", depth_tex);
+ DRW_shgroup_uniform_bool_copy(grp,
+ "isConstantScreenSizeNormals",
+ (flag & V3D_OVERLAY_EDIT_CONSTANT_SCREEN_SIZE_NORMALS) != 0);
+ DRW_shgroup_uniform_float_copy(
+ grp, "normalScreenSize", v3d->overlay.normals_constant_screen_size);
}
{
/* Mesh Analysis Pass */
diff --git a/source/blender/draw/engines/overlay/overlay_engine.c b/source/blender/draw/engines/overlay/overlay_engine.c
index 235104245cc..b07e86000fd 100644
--- a/source/blender/draw/engines/overlay/overlay_engine.c
+++ b/source/blender/draw/engines/overlay/overlay_engine.c
@@ -576,9 +576,20 @@ static void OVERLAY_draw_scene(void *vedata)
OVERLAY_extra_blend_draw(vedata);
OVERLAY_volume_draw(vedata);
- if (pd->ctx_mode == CTX_MODE_SCULPT) {
- /* Sculpt overlays are drawn here to avoid artifacts with wireframe opacity. */
- OVERLAY_sculpt_draw(vedata);
+ /* These overlays are drawn here to avoid artifacts with wireframe opacity. */
+ switch (pd->ctx_mode) {
+ case CTX_MODE_SCULPT:
+ OVERLAY_sculpt_draw(vedata);
+ break;
+ case CTX_MODE_EDIT_MESH:
+ case CTX_MODE_POSE:
+ case CTX_MODE_PAINT_WEIGHT:
+ case CTX_MODE_PAINT_VERTEX:
+ case CTX_MODE_PAINT_TEXTURE:
+ OVERLAY_paint_draw(vedata);
+ break;
+ default:
+ break;
}
if (DRW_state_is_fbo()) {
@@ -601,11 +612,6 @@ static void OVERLAY_draw_scene(void *vedata)
OVERLAY_xray_depth_infront_copy(vedata);
- if (pd->ctx_mode == CTX_MODE_PAINT_WEIGHT) {
- /* Fix weird case where weightpaint mode needs to draw before xray bones. */
- OVERLAY_paint_draw(vedata);
- }
-
if (DRW_state_is_fbo()) {
GPU_framebuffer_bind(fbl->overlay_in_front_fb);
}
@@ -640,7 +646,6 @@ static void OVERLAY_draw_scene(void *vedata)
switch (pd->ctx_mode) {
case CTX_MODE_EDIT_MESH:
- OVERLAY_paint_draw(vedata);
OVERLAY_edit_mesh_draw(vedata);
break;
case CTX_MODE_EDIT_SURFACE:
@@ -654,13 +659,8 @@ static void OVERLAY_draw_scene(void *vedata)
OVERLAY_edit_lattice_draw(vedata);
break;
case CTX_MODE_POSE:
- OVERLAY_paint_draw(vedata);
OVERLAY_pose_draw(vedata);
break;
- case CTX_MODE_PAINT_VERTEX:
- case CTX_MODE_PAINT_TEXTURE:
- OVERLAY_paint_draw(vedata);
- break;
case CTX_MODE_PARTICLE:
OVERLAY_edit_particle_draw(vedata);
break;
diff --git a/source/blender/draw/engines/overlay/overlay_extra.c b/source/blender/draw/engines/overlay/overlay_extra.c
index f5be9c846d1..2a9080eb217 100644
--- a/source/blender/draw/engines/overlay/overlay_extra.c
+++ b/source/blender/draw/engines/overlay/overlay_extra.c
@@ -1357,7 +1357,8 @@ static void OVERLAY_relationship_lines(OVERLAY_ExtraCallBuffers *cb,
}
}
}
- BKE_constraints_clear_evalob(cob);
+ /* NOTE: Don't use BKE_constraints_clear_evalob here as that will reset ob->constinv. */
+ MEM_freeN(cob);
}
}
diff --git a/source/blender/draw/engines/overlay/overlay_paint.c b/source/blender/draw/engines/overlay/overlay_paint.c
index d52640ed174..60a90616d29 100644
--- a/source/blender/draw/engines/overlay/overlay_paint.c
+++ b/source/blender/draw/engines/overlay/overlay_paint.c
@@ -92,18 +92,26 @@ void OVERLAY_paint_cache_init(OVERLAY_Data *vedata)
case CTX_MODE_PAINT_WEIGHT: {
opacity = is_edit_mode ? 1.0 : pd->overlay.weight_paint_mode_opacity;
if (opacity > 0.0f) {
- state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL;
- state |= pd->painting.alpha_blending ? DRW_STATE_BLEND_ALPHA : DRW_STATE_BLEND_MUL;
+ state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_BLEND_ALPHA;
DRW_PASS_CREATE(psl->paint_color_ps, state | pd->clipping_state);
- sh = OVERLAY_shader_paint_weight();
+ const bool do_shading = draw_ctx->v3d->shading.type != OB_WIRE;
+
+ sh = OVERLAY_shader_paint_weight(do_shading);
pd->paint_surf_grp = grp = DRW_shgroup_create(sh, psl->paint_color_ps);
DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo);
DRW_shgroup_uniform_bool_copy(grp, "drawContours", draw_contours);
- DRW_shgroup_uniform_bool_copy(grp, "useAlphaBlend", pd->painting.alpha_blending);
DRW_shgroup_uniform_float_copy(grp, "opacity", opacity);
DRW_shgroup_uniform_texture(grp, "colorramp", G_draw.weight_ramp);
+ /* Arbitrary light to give a hint of the geometry behind the weights. */
+ if (do_shading) {
+ float light_dir[3];
+ copy_v3_fl3(light_dir, 0.0f, 0.5f, 0.86602f);
+ normalize_v3(light_dir);
+ DRW_shgroup_uniform_vec3_copy(grp, "light_dir", light_dir);
+ }
+
if (pd->painting.alpha_blending) {
state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL;
DRW_PASS_CREATE(psl->paint_depth_ps, state | pd->clipping_state);
@@ -257,17 +265,10 @@ void OVERLAY_paint_draw(OVERLAY_Data *vedata)
OVERLAY_PassList *psl = vedata->psl;
OVERLAY_FramebufferList *fbl = vedata->fbl;
- DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
if (DRW_state_is_fbo()) {
- if (pd->painting.alpha_blending) {
- GPU_framebuffer_bind(pd->painting.in_front ? fbl->overlay_in_front_fb :
- fbl->overlay_default_fb);
- }
- else {
- /* Paint overlay needs final color because of multiply blend mode. */
- GPU_framebuffer_bind(pd->painting.in_front ? dfbl->in_front_fb : dfbl->default_fb);
- }
+ GPU_framebuffer_bind(pd->painting.in_front ? fbl->overlay_in_front_fb :
+ fbl->overlay_default_fb);
}
if (psl->paint_depth_ps) {
diff --git a/source/blender/draw/engines/overlay/overlay_private.h b/source/blender/draw/engines/overlay/overlay_private.h
index 03bfaf56f24..68f60bee779 100644
--- a/source/blender/draw/engines/overlay/overlay_private.h
+++ b/source/blender/draw/engines/overlay/overlay_private.h
@@ -736,7 +736,7 @@ GPUShader *OVERLAY_shader_paint_face(void);
GPUShader *OVERLAY_shader_paint_point(void);
GPUShader *OVERLAY_shader_paint_texture(void);
GPUShader *OVERLAY_shader_paint_vertcol(void);
-GPUShader *OVERLAY_shader_paint_weight(void);
+GPUShader *OVERLAY_shader_paint_weight(bool shading);
GPUShader *OVERLAY_shader_paint_wire(void);
GPUShader *OVERLAY_shader_particle_dot(void);
GPUShader *OVERLAY_shader_particle_shape(void);
diff --git a/source/blender/draw/engines/overlay/overlay_shader.c b/source/blender/draw/engines/overlay/overlay_shader.c
index 7a7ae9a921b..edf9148c8c0 100644
--- a/source/blender/draw/engines/overlay/overlay_shader.c
+++ b/source/blender/draw/engines/overlay/overlay_shader.c
@@ -211,7 +211,7 @@ typedef struct OVERLAY_Shaders {
GPUShader *paint_point;
GPUShader *paint_texture;
GPUShader *paint_vertcol;
- GPUShader *paint_weight;
+ GPUShader *paint_weight[2];
GPUShader *paint_wire;
GPUShader *particle_dot;
GPUShader *particle_shape;
@@ -1334,13 +1334,14 @@ GPUShader *OVERLAY_shader_paint_vertcol(void)
return sh_data->paint_vertcol;
}
-GPUShader *OVERLAY_shader_paint_weight(void)
+GPUShader *OVERLAY_shader_paint_weight(const bool shading)
{
+ int index = shading ? 1 : 0;
const DRWContextState *draw_ctx = DRW_context_state_get();
const GPUShaderConfigData *sh_cfg = &GPU_shader_cfg_data[draw_ctx->sh_cfg];
OVERLAY_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg];
- if (!sh_data->paint_weight) {
- sh_data->paint_weight = GPU_shader_create_from_arrays({
+ if (!sh_data->paint_weight[index]) {
+ sh_data->paint_weight[index] = GPU_shader_create_from_arrays({
.vert = (const char *[]){sh_cfg->lib,
datatoc_common_globals_lib_glsl,
datatoc_common_view_lib_glsl,
@@ -1349,10 +1350,10 @@ GPUShader *OVERLAY_shader_paint_weight(void)
.frag = (const char *[]){datatoc_common_globals_lib_glsl,
datatoc_paint_weight_frag_glsl,
NULL},
- .defs = (const char *[]){sh_cfg->def, NULL},
+ .defs = (const char *[]){sh_cfg->def, shading ? "#define FAKE_SHADING\n" : "", NULL},
});
}
- return sh_data->paint_weight;
+ return sh_data->paint_weight[index];
}
GPUShader *OVERLAY_shader_paint_wire(void)
diff --git a/source/blender/draw/engines/overlay/shaders/edit_mesh_normal_vert.glsl b/source/blender/draw/engines/overlay/shaders/edit_mesh_normal_vert.glsl
index 007495f84e0..d370943db03 100644
--- a/source/blender/draw/engines/overlay/shaders/edit_mesh_normal_vert.glsl
+++ b/source/blender/draw/engines/overlay/shaders/edit_mesh_normal_vert.glsl
@@ -1,5 +1,7 @@
uniform float normalSize;
+uniform float normalScreenSize;
+uniform bool isConstantScreenSizeNormals;
uniform sampler2D depthTex;
uniform float alpha = 1.0;
@@ -49,11 +51,24 @@ void main()
}
vec3 n = normalize(normal_object_to_world(nor));
-
vec3 world_pos = point_object_to_world(pos);
if (gl_VertexID == 0) {
- world_pos += n * normalSize;
+ if (isConstantScreenSizeNormals) {
+ bool is_persp = (ProjectionMatrix[3][3] == 0.0);
+ if (is_persp) {
+ float dist_fac = length(cameraPos - world_pos);
+ float cos_fac = dot(cameraForward, cameraVec(world_pos));
+ world_pos += n * normalScreenSize * dist_fac * cos_fac * pixelFac * sizePixel;
+ }
+ else {
+ float frustrum_fac = mul_project_m4_v3_zfac(n) * sizePixel;
+ world_pos += n * normalScreenSize * frustrum_fac;
+ }
+ }
+ else {
+ world_pos += n * normalSize;
+ }
}
gl_Position = point_world_to_ndc(world_pos);
diff --git a/source/blender/draw/engines/overlay/shaders/paint_weight_frag.glsl b/source/blender/draw/engines/overlay/shaders/paint_weight_frag.glsl
index 0020d76ed6a..8009713d655 100644
--- a/source/blender/draw/engines/overlay/shaders/paint_weight_frag.glsl
+++ b/source/blender/draw/engines/overlay/shaders/paint_weight_frag.glsl
@@ -1,12 +1,12 @@
in vec2 weight_interp; /* (weight, alert) */
+in float color_fac;
out vec4 fragColor;
uniform float opacity = 1.0;
uniform sampler1D colorramp;
-uniform bool useAlphaBlend = false;
uniform bool drawContours = false;
float contours(float value, float steps, float width_px, float max_rel_width, float gradient)
@@ -68,6 +68,13 @@ vec4 contour_grid(float weight, float weight_gradient)
return grid * clamp((weight_gradient - flt_eps) / flt_eps, 0.0, 1.0);
}
+vec4 apply_color_fac(vec4 color_in)
+{
+ vec4 color = color_in;
+ color.rgb = max(vec3(0.005), color_in.rgb) * color_fac;
+ return color;
+}
+
void main()
{
float alert = weight_interp.y;
@@ -75,12 +82,13 @@ void main()
/* Missing vertex group alert color. Uniform in practice. */
if (alert > 1.1) {
- color = colorVertexMissingData;
+ color = apply_color_fac(colorVertexMissingData);
}
/* Weights are available */
else {
float weight = weight_interp.x;
vec4 weight_color = texture(colorramp, weight, 0);
+ weight_color = apply_color_fac(weight_color);
/* Contour display */
if (drawContours) {
@@ -93,14 +101,9 @@ void main()
}
/* Zero weight alert color. Nonlinear blend to reduce impact. */
- color = mix(weight_color, colorVertexUnreferenced, alert * alert);
+ vec4 color_unreferenced = apply_color_fac(colorVertexUnreferenced);
+ color = mix(weight_color, color_unreferenced, alert * alert);
}
- if (useAlphaBlend) {
- fragColor = vec4(color.rgb, opacity);
- }
- else {
- /* mix with 1.0 -> is like opacity when using multiply blend mode */
- fragColor = vec4(mix(vec3(1.0), color.rgb, opacity), 1.0);
- }
+ fragColor = vec4(color.rgb, opacity);
}
diff --git a/source/blender/draw/engines/overlay/shaders/paint_weight_vert.glsl b/source/blender/draw/engines/overlay/shaders/paint_weight_vert.glsl
index b3baa8c7b07..31b6dc42cf4 100644
--- a/source/blender/draw/engines/overlay/shaders/paint_weight_vert.glsl
+++ b/source/blender/draw/engines/overlay/shaders/paint_weight_vert.glsl
@@ -1,8 +1,13 @@
+#ifdef FAKE_SHADING
+uniform vec3 light_dir;
+#endif
in float weight;
in vec3 pos;
+in vec3 nor;
out vec2 weight_interp; /* (weight, alert) */
+out float color_fac;
void main()
{
@@ -14,6 +19,16 @@ void main()
/* Separate actual weight and alerts for independent interpolation */
weight_interp = max(vec2(weight, -weight), 0.0);
+ /* Saturate the weight to give a hint of the geometry behind the weights. */
+#ifdef FAKE_SHADING
+ vec3 view_normal = normalize(normal_object_to_view(nor));
+ color_fac = abs(dot(view_normal, light_dir));
+ color_fac = color_fac * 0.9 + 0.1;
+
+#else
+ color_fac = 1.0;
+#endif
+
#ifdef USE_WORLD_CLIP_PLANES
world_clip_planes_calc_clip_distance(world_pos);
#endif
diff --git a/source/blender/draw/engines/select/select_debug_engine.c b/source/blender/draw/engines/select/select_debug_engine.c
new file mode 100644
index 00000000000..e9437c5ab92
--- /dev/null
+++ b/source/blender/draw/engines/select/select_debug_engine.c
@@ -0,0 +1,135 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2019, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup draw_engine
+ *
+ * Engine for debugging the selection map drawing.
+ */
+
+#include "DNA_ID.h"
+#include "DNA_vec_types.h"
+
+#include "DRW_engine.h"
+#include "DRW_select_buffer.h"
+
+#include "draw_cache.h"
+#include "draw_manager.h"
+
+#include "select_engine.h"
+
+#define SELECT_DEBUG_ENGINE "SELECT_DEBUG_ENGINE"
+
+/* -------------------------------------------------------------------- */
+/** \name Structs and static variables
+ * \{ */
+
+typedef struct SELECTIDDEBUG_PassList {
+ struct DRWPass *debug_pass;
+} SELECTIDDEBUG_PassList;
+
+typedef struct SELECTIDDEBUG_Data {
+ void *engine_type;
+ DRWViewportEmptyList *fbl;
+ DRWViewportEmptyList *txl;
+ SELECTIDDEBUG_PassList *psl;
+ DRWViewportEmptyList *stl;
+} SELECTIDDEBUG_Data;
+
+static struct {
+ struct GPUShader *select_debug_sh;
+} e_data = {{NULL}}; /* Engine data */
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Engine Functions
+ * \{ */
+
+static void select_debug_engine_init(void *vedata)
+{
+ SELECTIDDEBUG_PassList *psl = ((SELECTIDDEBUG_Data *)vedata)->psl;
+
+ if (!e_data.select_debug_sh) {
+ e_data.select_debug_sh = DRW_shader_create_fullscreen(
+ "uniform usampler2D image;"
+ "in vec4 uvcoordsvar;"
+ "out vec4 fragColor;"
+ "void main() {"
+ " uint px = texture(image, uvcoordsvar.xy).r;"
+ " fragColor = vec4(1.0, 1.0, 1.0, 0.0);"
+ " if (px != 0u) {"
+ " fragColor.a = 1.0;"
+ " px &= 0x3Fu;"
+ " fragColor.r = ((px >> 0) & 0x3u) / float(0x3u);"
+ " fragColor.g = ((px >> 2) & 0x3u) / float(0x3u);"
+ " fragColor.b = ((px >> 4) & 0x3u) / float(0x3u);"
+ " }"
+ "}\n",
+ NULL);
+ }
+
+ psl->debug_pass = DRW_pass_create("Debug Pass", DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA);
+ GPUTexture *texture_u32 = DRW_engine_select_texture_get();
+ if (texture_u32) {
+ DRWShadingGroup *shgrp = DRW_shgroup_create(e_data.select_debug_sh, psl->debug_pass);
+ DRW_shgroup_uniform_texture(shgrp, "image", texture_u32);
+ DRW_shgroup_call_procedural_triangles(shgrp, NULL, 1);
+ }
+}
+
+static void select_debug_draw_scene(void *vedata)
+{
+ SELECTIDDEBUG_PassList *psl = ((SELECTIDDEBUG_Data *)vedata)->psl;
+ DRW_draw_pass(psl->debug_pass);
+}
+
+static void select_debug_engine_free(void)
+{
+ DRW_SHADER_FREE_SAFE(e_data.select_debug_sh);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Engine Type
+ * \{ */
+
+static const DrawEngineDataSize select_debug_data_size = DRW_VIEWPORT_DATA_SIZE(
+ SELECTIDDEBUG_Data);
+
+DrawEngineType draw_engine_debug_select_type = {
+ NULL,
+ NULL,
+ N_("Select ID Debug"),
+ &select_debug_data_size,
+ &select_debug_engine_init,
+ &select_debug_engine_free,
+ NULL,
+ NULL,
+ NULL,
+ &select_debug_draw_scene,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+/** \} */
+
+#undef SELECT_DEBUG_ENGINE
diff --git a/source/blender/draw/engines/select/select_engine.c b/source/blender/draw/engines/select/select_engine.c
index 86b4a0ac727..96ab8a28e09 100644
--- a/source/blender/draw/engines/select/select_engine.c
+++ b/source/blender/draw/engines/select/select_engine.c
@@ -193,7 +193,7 @@ static void select_cache_init(void *vedata)
if (e_data.context.select_mode & SCE_SELECT_VERTEX) {
DRW_PASS_CREATE(psl->select_id_vert_pass, state);
pd->shgrp_vert = DRW_shgroup_create(sh->select_id_flat, psl->select_id_vert_pass);
- DRW_shgroup_uniform_float_copy(pd->shgrp_vert, "sizeVertex", G_draw.block.sizeVertex);
+ DRW_shgroup_uniform_float_copy(pd->shgrp_vert, "sizeVertex", 2 * G_draw.block.sizeVertex);
}
}
diff --git a/source/blender/draw/engines/select/select_engine.h b/source/blender/draw/engines/select/select_engine.h
index 2b35cf6bee5..d6192103178 100644
--- a/source/blender/draw/engines/select/select_engine.h
+++ b/source/blender/draw/engines/select/select_engine.h
@@ -22,9 +22,15 @@
#pragma once
+/* select_engine.c */
extern DrawEngineType draw_engine_select_type;
extern RenderEngineType DRW_engine_viewport_select_type;
+#ifdef WITH_DRAW_DEBUG
+/* select_debug_engine.c */
+extern DrawEngineType draw_engine_debug_select_type;
+#endif
+
struct SELECTID_Context *DRW_select_engine_context_get(void);
struct GPUFrameBuffer *DRW_engine_select_framebuffer_get(void);
diff --git a/source/blender/draw/engines/workbench/workbench_private.h b/source/blender/draw/engines/workbench/workbench_private.h
index 453ef9c7f8e..e17bd7d9956 100644
--- a/source/blender/draw/engines/workbench/workbench_private.h
+++ b/source/blender/draw/engines/workbench/workbench_private.h
@@ -399,7 +399,7 @@ typedef struct WORKBENCH_ViewLayerData {
/* inline helper functions */
BLI_INLINE bool workbench_is_specular_highlight_enabled(WORKBENCH_PrivateData *wpd)
{
- if ((wpd->shading.flag & V3D_SHADING_SPECULAR_HIGHLIGHT)) {
+ if (wpd->shading.flag & V3D_SHADING_SPECULAR_HIGHLIGHT) {
if (STUDIOLIGHT_ENABLED(wpd) || MATCAP_ENABLED(wpd)) {
return (wpd->studio_light->flag & STUDIOLIGHT_SPECULAR_HIGHLIGHT_PASS) != 0;
}
diff --git a/source/blender/draw/intern/draw_cache_impl_gpencil.c b/source/blender/draw/intern/draw_cache_impl_gpencil.c
index 336ccd40d5c..6ed4148a7fe 100644
--- a/source/blender/draw/intern/draw_cache_impl_gpencil.c
+++ b/source/blender/draw/intern/draw_cache_impl_gpencil.c
@@ -552,6 +552,9 @@ bGPDstroke *DRW_cache_gpencil_sbuffer_stroke_data_get(Object *ob)
gps->caps[0] = gps->caps[1] = GP_STROKE_CAP_ROUND;
gps->runtime.stroke_start = 1; /* Add one for the adjacency index. */
copy_v4_v4(gps->vert_color_fill, gpd->runtime.vert_color_fill);
+ /* Caps. */
+ gps->caps[0] = gps->caps[1] = (short)brush->gpencil_settings->caps_type;
+
gpd->runtime.sbuffer_gps = gps;
}
return gpd->runtime.sbuffer_gps;
diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c
index 359788545e4..52b76733b78 100644
--- a/source/blender/draw/intern/draw_cache_impl_mesh.c
+++ b/source/blender/draw/intern/draw_cache_impl_mesh.c
@@ -95,7 +95,7 @@
#define MDEPS_CREATE(buff_name, ...) [_BUFFER_INDEX(buff_name)] = VA_NARGS_CALL_OVERLOAD(_MDEPS_CREATE, __VA_ARGS__)
-#define _MDEPS_CREATE_MAP1(a) g_buffer_desps[_BUFFER_INDEX(a)]
+#define _MDEPS_CREATE_MAP1(a) g_buffer_deps[_BUFFER_INDEX(a)]
#define _MDEPS_CREATE_MAP2(a, b) _MDEPS_CREATE_MAP1(a) | _MDEPS_CREATE_MAP1(b)
#define _MDEPS_CREATE_MAP3(a, b, c) _MDEPS_CREATE_MAP2(a, b) | _MDEPS_CREATE_MAP1(c)
#define _MDEPS_CREATE_MAP4(a, b, c, d) _MDEPS_CREATE_MAP3(a, b, c) | _MDEPS_CREATE_MAP1(d)
@@ -110,8 +110,8 @@
#ifndef NDEBUG
# define _MDEPS_ASSERT2(b, name) \
- g_buffer_desps_d[_BUFFER_INDEX(name)] |= _MDEPS_CREATE1(b); \
- BLI_assert(g_buffer_desps[_BUFFER_INDEX(name)] & _MDEPS_CREATE1(b))
+ g_buffer_deps_d[_BUFFER_INDEX(name)] |= _MDEPS_CREATE1(b); \
+ BLI_assert(g_buffer_deps[_BUFFER_INDEX(name)] & _MDEPS_CREATE1(b))
# define _MDEPS_ASSERT3(b, n1, n2) _MDEPS_ASSERT2(b, n1); _MDEPS_ASSERT2(b, n2)
# define _MDEPS_ASSERT4(b, n1, n2, n3) _MDEPS_ASSERT3(b, n1, n2); _MDEPS_ASSERT2(b, n3)
# define _MDEPS_ASSERT5(b, n1, n2, n3, n4) _MDEPS_ASSERT4(b, n1, n2, n3); _MDEPS_ASSERT2(b, n4)
@@ -120,7 +120,7 @@
# define _MDEPS_ASSERT8(b, n1, n2, n3, n4, n5, n6, n7) _MDEPS_ASSERT7(b, n1, n2, n3, n4, n5, n6); _MDEPS_ASSERT2(b, n7)
# define MDEPS_ASSERT(...) VA_NARGS_CALL_OVERLOAD(_MDEPS_ASSERT, __VA_ARGS__)
-# define MDEPS_ASSERT_MAP(name) BLI_assert(g_buffer_desps_d[_BUFFER_INDEX(name)] == g_buffer_desps[_BUFFER_INDEX(name)])
+# define MDEPS_ASSERT_MAP(name) BLI_assert(g_buffer_deps_d[_BUFFER_INDEX(name)] == g_buffer_deps[_BUFFER_INDEX(name)])
#else
# define MDEPS_ASSERT(...)
# define MDEPS_ASSERT_MAP(name)
@@ -128,7 +128,7 @@
/* clang-format on */
-static const DRWBatchFlag g_buffer_desps[] = {
+static const DRWBatchFlag g_buffer_deps[] = {
MDEPS_CREATE(vbo.pos_nor,
batch.surface,
batch.surface_weights,
@@ -215,7 +215,7 @@ static const DRWBatchFlag g_buffer_desps[] = {
};
#ifndef NDEBUG
-static DRWBatchFlag g_buffer_desps_d[sizeof(g_buffer_desps)] = {0};
+static DRWBatchFlag g_buffer_deps_d[ARRAY_SIZE(g_buffer_deps)] = {0};
#endif
static void mesh_batch_cache_discard_surface_batches(MeshBatchCache *cache);
diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c
index 35072518b66..027ab8ce32b 100644
--- a/source/blender/draw/intern/draw_manager.c
+++ b/source/blender/draw/intern/draw_manager.c
@@ -512,7 +512,7 @@ static void drw_context_state_init(void)
if (DST.draw_ctx.object_mode & OB_MODE_POSE) {
DST.draw_ctx.object_pose = DST.draw_ctx.obact;
}
- else if ((DST.draw_ctx.object_mode & OB_MODE_ALL_WEIGHT_PAINT)) {
+ else if (DST.draw_ctx.object_mode & OB_MODE_ALL_WEIGHT_PAINT) {
DST.draw_ctx.object_pose = BKE_object_pose_armature_get(DST.draw_ctx.obact);
}
else {
@@ -1281,6 +1281,12 @@ static void drw_engines_enable(ViewLayer *UNUSED(view_layer),
use_drw_engine(&draw_engine_gpencil_type);
}
drw_engines_enable_overlays();
+
+#ifdef WITH_DRAW_DEBUG
+ if (G.debug_value == 31) {
+ use_drw_engine(&draw_engine_debug_select_type);
+ }
+#endif
}
static void drw_engines_disable(void)
@@ -2682,6 +2688,7 @@ void DRW_draw_select_id(Depsgraph *depsgraph, ARegion *region, View3D *v3d, cons
drw_viewport_var_init();
/* Update UBO's */
+ UI_SetTheme(SPACE_VIEW3D, RGN_TYPE_WINDOW);
DRW_globals_update();
/* Init Select Engine */
@@ -2939,6 +2946,9 @@ void DRW_engines_register(void)
DRW_engine_register(&draw_engine_overlay_type);
DRW_engine_register(&draw_engine_select_type);
DRW_engine_register(&draw_engine_basic_type);
+#ifdef WITH_DRAW_DEBUG
+ DRW_engine_register(&draw_engine_debug_select_type);
+#endif
DRW_engine_register(&draw_engine_image_type);
DRW_engine_register(DRW_engine_viewport_external_type.draw_engine);
diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c
index 2126385a352..af331c86a8b 100644
--- a/source/blender/draw/intern/draw_manager_data.c
+++ b/source/blender/draw/intern/draw_manager_data.c
@@ -1533,13 +1533,6 @@ static void draw_frustum_boundbox_calc(const float (*viewinv)[4],
projmat_dimensions(projmat, &left, &right, &bottom, &top, &near, &far);
- if (is_persp) {
- left *= near;
- right *= near;
- bottom *= near;
- top *= near;
- }
-
r_bbox->vec[0][2] = r_bbox->vec[3][2] = r_bbox->vec[7][2] = r_bbox->vec[4][2] = -near;
r_bbox->vec[0][0] = r_bbox->vec[3][0] = left;
r_bbox->vec[4][0] = r_bbox->vec[7][0] = right;
diff --git a/source/blender/draw/intern/draw_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c
index 7f7696d485c..22356a3c57b 100644
--- a/source/blender/draw/intern/draw_manager_exec.c
+++ b/source/blender/draw/intern/draw_manager_exec.c
@@ -283,11 +283,11 @@ void DRW_state_reset_ex(DRWState state)
static void drw_state_validate(void)
{
/* Cannot write to stencil buffer without stencil test. */
- if ((DST.state & DRW_STATE_WRITE_STENCIL_ENABLED)) {
+ if (DST.state & DRW_STATE_WRITE_STENCIL_ENABLED) {
BLI_assert(DST.state & DRW_STATE_STENCIL_TEST_ENABLED);
}
/* Cannot write to depth buffer without depth test. */
- if ((DST.state & DRW_STATE_WRITE_DEPTH)) {
+ if (DST.state & DRW_STATE_WRITE_DEPTH) {
BLI_assert(DST.state & DRW_STATE_DEPTH_TEST_ENABLED);
}
}
diff --git a/source/blender/draw/intern/draw_manager_texture.c b/source/blender/draw/intern/draw_manager_texture.c
index 73afdd6e1e3..99e8ba968a2 100644
--- a/source/blender/draw/intern/draw_manager_texture.c
+++ b/source/blender/draw/intern/draw_manager_texture.c
@@ -83,8 +83,8 @@ GPUTexture *DRW_texture_create_1d(int w,
DRWTextureFlag flags,
const float *fpixels)
{
- int mips = (flags & DRW_TEX_MIPMAP) ? 9999 : 1;
- GPUTexture *tex = GPU_texture_create_1d(__func__, w, mips, format, fpixels);
+ int mip_len = (flags & DRW_TEX_MIPMAP) ? 9999 : 1;
+ GPUTexture *tex = GPU_texture_create_1d(__func__, w, mip_len, format, fpixels);
drw_texture_set_parameters(tex, flags);
return tex;
@@ -93,8 +93,8 @@ GPUTexture *DRW_texture_create_1d(int w,
GPUTexture *DRW_texture_create_2d(
int w, int h, eGPUTextureFormat format, DRWTextureFlag flags, const float *fpixels)
{
- int mips = (flags & DRW_TEX_MIPMAP) ? 9999 : 1;
- GPUTexture *tex = GPU_texture_create_2d(__func__, w, h, mips, format, fpixels);
+ int mip_len = (flags & DRW_TEX_MIPMAP) ? 9999 : 1;
+ GPUTexture *tex = GPU_texture_create_2d(__func__, w, h, mip_len, format, fpixels);
drw_texture_set_parameters(tex, flags);
return tex;
@@ -103,8 +103,8 @@ GPUTexture *DRW_texture_create_2d(
GPUTexture *DRW_texture_create_2d_array(
int w, int h, int d, eGPUTextureFormat format, DRWTextureFlag flags, const float *fpixels)
{
- int mips = (flags & DRW_TEX_MIPMAP) ? 9999 : 1;
- GPUTexture *tex = GPU_texture_create_2d_array(__func__, w, h, d, mips, format, fpixels);
+ int mip_len = (flags & DRW_TEX_MIPMAP) ? 9999 : 1;
+ GPUTexture *tex = GPU_texture_create_2d_array(__func__, w, h, d, mip_len, format, fpixels);
drw_texture_set_parameters(tex, flags);
return tex;
@@ -113,9 +113,9 @@ GPUTexture *DRW_texture_create_2d_array(
GPUTexture *DRW_texture_create_3d(
int w, int h, int d, eGPUTextureFormat format, DRWTextureFlag flags, const float *fpixels)
{
- int mips = (flags & DRW_TEX_MIPMAP) ? 9999 : 1;
+ int mip_len = (flags & DRW_TEX_MIPMAP) ? 9999 : 1;
GPUTexture *tex = GPU_texture_create_3d(
- __func__, w, h, d, mips, format, GPU_DATA_FLOAT, fpixels);
+ __func__, w, h, d, mip_len, format, GPU_DATA_FLOAT, fpixels);
drw_texture_set_parameters(tex, flags);
return tex;
@@ -126,8 +126,8 @@ GPUTexture *DRW_texture_create_cube(int w,
DRWTextureFlag flags,
const float *fpixels)
{
- int mips = (flags & DRW_TEX_MIPMAP) ? 9999 : 1;
- GPUTexture *tex = GPU_texture_create_cube(__func__, w, mips, format, fpixels);
+ int mip_len = (flags & DRW_TEX_MIPMAP) ? 9999 : 1;
+ GPUTexture *tex = GPU_texture_create_cube(__func__, w, mip_len, format, fpixels);
drw_texture_set_parameters(tex, flags);
return tex;
}
@@ -135,8 +135,8 @@ GPUTexture *DRW_texture_create_cube(int w,
GPUTexture *DRW_texture_create_cube_array(
int w, int d, eGPUTextureFormat format, DRWTextureFlag flags, const float *fpixels)
{
- int mips = (flags & DRW_TEX_MIPMAP) ? 9999 : 1;
- GPUTexture *tex = GPU_texture_create_cube_array(__func__, w, d, mips, format, fpixels);
+ int mip_len = (flags & DRW_TEX_MIPMAP) ? 9999 : 1;
+ GPUTexture *tex = GPU_texture_create_cube_array(__func__, w, d, mip_len, format, fpixels);
drw_texture_set_parameters(tex, flags);
return tex;
}
diff --git a/source/blender/draw/intern/draw_view.c b/source/blender/draw/intern/draw_view.c
index ae2c66881ff..90bb3762473 100644
--- a/source/blender/draw/intern/draw_view.c
+++ b/source/blender/draw/intern/draw_view.c
@@ -211,8 +211,17 @@ static bool is_cursor_visible_2d(const DRWContextState *draw_ctx)
return false;
}
SpaceImage *sima = (SpaceImage *)space_data;
- if (sima->mode != SI_MODE_UV) {
- return false;
+ switch (sima->mode) {
+ case SI_MODE_VIEW:
+ return false;
+ break;
+ case SI_MODE_PAINT:
+ return false;
+ break;
+ case SI_MODE_MASK:
+ break;
+ case SI_MODE_UV:
+ break;
}
return (sima->overlay.flag & SI_OVERLAY_SHOW_OVERLAYS) != 0;
}
diff --git a/source/blender/draw/tests/shaders_test.cc b/source/blender/draw/tests/shaders_test.cc
index 0c7cbd4dac8..a9810b4cc77 100644
--- a/source/blender/draw/tests/shaders_test.cc
+++ b/source/blender/draw/tests/shaders_test.cc
@@ -267,7 +267,8 @@ static void test_overlay_glsl_shaders()
EXPECT_NE(OVERLAY_shader_paint_point(), nullptr);
EXPECT_NE(OVERLAY_shader_paint_texture(), nullptr);
EXPECT_NE(OVERLAY_shader_paint_vertcol(), nullptr);
- EXPECT_NE(OVERLAY_shader_paint_weight(), nullptr);
+ EXPECT_NE(OVERLAY_shader_paint_weight(false), nullptr);
+ EXPECT_NE(OVERLAY_shader_paint_weight(true), nullptr);
EXPECT_NE(OVERLAY_shader_paint_wire(), nullptr);
EXPECT_NE(OVERLAY_shader_particle_dot(), nullptr);
EXPECT_NE(OVERLAY_shader_particle_shape(), nullptr);
diff --git a/source/blender/editors/animation/CMakeLists.txt b/source/blender/editors/animation/CMakeLists.txt
index 7a53b54b5a4..d9f52d90766 100644
--- a/source/blender/editors/animation/CMakeLists.txt
+++ b/source/blender/editors/animation/CMakeLists.txt
@@ -47,7 +47,7 @@ set(SRC
keyframes_draw.c
keyframes_edit.c
keyframes_general.c
- keyframes_keylist.c
+ keyframes_keylist.cc
keyframing.c
keyingsets.c
time_scrub_ui.c
diff --git a/source/blender/editors/animation/anim_draw.c b/source/blender/editors/animation/anim_draw.c
index baf8adf28d0..6d272bfc180 100644
--- a/source/blender/editors/animation/anim_draw.c
+++ b/source/blender/editors/animation/anim_draw.c
@@ -494,17 +494,14 @@ static bool find_prev_next_keyframes(struct bContext *C, int *r_nextfra, int *r_
Object *ob = CTX_data_active_object(C);
Mask *mask = CTX_data_edit_mask(C);
bDopeSheet ads = {NULL};
- DLRBT_Tree keys;
- ActKeyColumn *aknext, *akprev;
+ struct AnimKeylist *keylist = ED_keylist_create();
+ const ActKeyColumn *aknext, *akprev;
float cfranext, cfraprev;
bool donenext = false, doneprev = false;
int nextcount = 0, prevcount = 0;
cfranext = cfraprev = (float)(CFRA);
- /* init binarytree-list for getting keyframes */
- BLI_dlrbTree_init(&keys);
-
/* seed up dummy dopesheet context with flags to perform necessary filtering */
if ((scene->flag & SCE_KEYS_NO_SELONLY) == 0) {
/* only selected channels are included */
@@ -512,22 +509,23 @@ static bool find_prev_next_keyframes(struct bContext *C, int *r_nextfra, int *r_
}
/* populate tree with keyframe nodes */
- scene_to_keylist(&ads, scene, &keys, 0);
- gpencil_to_keylist(&ads, scene->gpd, &keys, false);
+ scene_to_keylist(&ads, scene, keylist, 0);
+ gpencil_to_keylist(&ads, scene->gpd, keylist, false);
if (ob) {
- ob_to_keylist(&ads, ob, &keys, 0);
- gpencil_to_keylist(&ads, ob->data, &keys, false);
+ ob_to_keylist(&ads, ob, keylist, 0);
+ gpencil_to_keylist(&ads, ob->data, keylist, false);
}
if (mask) {
MaskLayer *masklay = BKE_mask_layer_active(mask);
- mask_to_keylist(&ads, masklay, &keys);
+ mask_to_keylist(&ads, masklay, keylist);
}
+ /* TODO(jbakker): Keylists are ordered, no need to do any searching at all. */
/* find matching keyframe in the right direction */
do {
- aknext = (ActKeyColumn *)BLI_dlrbTree_search_next(&keys, compare_ak_cfraPtr, &cfranext);
+ aknext = ED_keylist_find_next(keylist, cfranext);
if (aknext) {
if (CFRA == (int)aknext->cfra) {
@@ -545,7 +543,7 @@ static bool find_prev_next_keyframes(struct bContext *C, int *r_nextfra, int *r_
} while ((aknext != NULL) && (donenext == false));
do {
- akprev = (ActKeyColumn *)BLI_dlrbTree_search_prev(&keys, compare_ak_cfraPtr, &cfraprev);
+ akprev = ED_keylist_find_prev(keylist, cfraprev);
if (akprev) {
if (CFRA == (int)akprev->cfra) {
@@ -562,7 +560,7 @@ static bool find_prev_next_keyframes(struct bContext *C, int *r_nextfra, int *r_
} while ((akprev != NULL) && (doneprev == false));
/* free temp stuff */
- BLI_dlrbTree_free(&keys);
+ ED_keylist_free(keylist);
/* any success? */
if (doneprev || donenext) {
diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c
index b2d387ea898..020518b5813 100644
--- a/source/blender/editors/animation/anim_filter.c
+++ b/source/blender/editors/animation/anim_filter.c
@@ -1892,7 +1892,7 @@ static size_t animdata_filter_gpencil(bAnimContext *ac,
}
/* outliner restrict-flag */
- if (ob->restrictflag & OB_RESTRICT_VIEWPORT) {
+ if (ob->visibility_flag & OB_HIDE_VIEWPORT) {
continue;
}
}
@@ -3098,7 +3098,7 @@ static bool animdata_filter_base_is_ok(bDopeSheet *ads, Base *base, int filter_m
}
/* outliner restrict-flag */
- if (ob->restrictflag & OB_RESTRICT_VIEWPORT) {
+ if (ob->visibility_flag & OB_HIDE_VIEWPORT) {
return false;
}
}
diff --git a/source/blender/editors/animation/anim_ipo_utils.c b/source/blender/editors/animation/anim_ipo_utils.c
index 5992545bdbe..eda87cf1897 100644
--- a/source/blender/editors/animation/anim_ipo_utils.c
+++ b/source/blender/editors/animation/anim_ipo_utils.c
@@ -134,6 +134,28 @@ int getname_anim_fcurve(char *name, ID *id, FCurve *fcu)
else {
structname = RNA_struct_ui_name(ptr.type);
}
+
+ /* For the VSE, a strip's 'Transform' or 'Crop' is a nested (under Sequence) struct, but
+ * displaying the struct name alone is no meaningful information (and also cannot be
+ * filtered well), same for modifiers. So display strip name alongside as well. */
+ if (GS(ptr.owner_id->name) == ID_SCE) {
+ if (BLI_str_startswith(fcu->rna_path, "sequence_editor.sequences_all[\"")) {
+ if (strstr(fcu->rna_path, ".transform.") || strstr(fcu->rna_path, ".crop.") ||
+ strstr(fcu->rna_path, ".modifiers[")) {
+ char *stripname = BLI_str_quoted_substrN(fcu->rna_path, "sequences_all[");
+ const char *structname_all = BLI_sprintfN(
+ "%s : %s", stripname ? stripname : "", structname);
+ if (free_structname) {
+ MEM_freeN((void *)structname);
+ }
+ if (stripname) {
+ MEM_freeN(stripname);
+ }
+ structname = structname_all;
+ free_structname = 1;
+ }
+ }
+ }
}
/* Property Name is straightforward */
diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c
index cfcea950955..0de3f429bc7 100644
--- a/source/blender/editors/animation/anim_markers.c
+++ b/source/blender/editors/animation/anim_markers.c
@@ -445,7 +445,7 @@ static void draw_marker_name(const uchar *text_color,
if (marker->camera) {
Object *camera = marker->camera;
name = camera->id.name + 2;
- if (camera->restrictflag & OB_RESTRICT_RENDER) {
+ if (camera->visibility_flag & OB_HIDE_RENDER) {
final_text_color[3] = 100;
}
}
diff --git a/source/blender/editors/animation/anim_motion_paths.c b/source/blender/editors/animation/anim_motion_paths.c
index bddd5dbff55..d976f5f72ad 100644
--- a/source/blender/editors/animation/anim_motion_paths.c
+++ b/source/blender/editors/animation/anim_motion_paths.c
@@ -55,7 +55,7 @@ typedef struct MPathTarget {
bMotionPath *mpath; /* motion path in question */
- DLRBT_Tree keys; /* temp, to know where the keyframes are */
+ struct AnimKeylist *keylist; /* temp, to know where the keyframes are */
/* Original (Source Objects) */
Object *ob; /* source object */
@@ -187,7 +187,7 @@ static void motionpaths_calc_bake_targets(ListBase *targets, int cframe)
float mframe = (float)(cframe);
/* Tag if it's a keyframe */
- if (BLI_dlrbTree_search_exact(&mpt->keys, compare_ak_cfraPtr, &mframe)) {
+ if (ED_keylist_find_exact(mpt->keylist, mframe)) {
mpv->flag |= MOTIONPATH_VERT_KEY;
}
else {
@@ -234,52 +234,55 @@ static void motionpath_get_global_framerange(ListBase *targets, int *r_sfra, int
}
}
-static int motionpath_get_prev_keyframe(MPathTarget *mpt, DLRBT_Tree *fcu_keys, int current_frame)
+/* TODO(jbakker): Remove complexity, keylists are ordered. */
+static int motionpath_get_prev_keyframe(MPathTarget *mpt,
+ struct AnimKeylist *keylist,
+ int current_frame)
{
if (current_frame <= mpt->mpath->start_frame) {
return mpt->mpath->start_frame;
}
float current_frame_float = current_frame;
- DLRBT_Node *node = BLI_dlrbTree_search_prev(fcu_keys, compare_ak_cfraPtr, &current_frame_float);
- if (node == NULL) {
+ const ActKeyColumn *ak = ED_keylist_find_prev(keylist, current_frame_float);
+ if (ak == NULL) {
return mpt->mpath->start_frame;
}
- ActKeyColumn *key_data = (ActKeyColumn *)node;
- return key_data->cfra;
+ return ak->cfra;
}
static int motionpath_get_prev_prev_keyframe(MPathTarget *mpt,
- DLRBT_Tree *fcu_keys,
+ struct AnimKeylist *keylist,
int current_frame)
{
- int frame = motionpath_get_prev_keyframe(mpt, fcu_keys, current_frame);
- return motionpath_get_prev_keyframe(mpt, fcu_keys, frame);
+ int frame = motionpath_get_prev_keyframe(mpt, keylist, current_frame);
+ return motionpath_get_prev_keyframe(mpt, keylist, frame);
}
-static int motionpath_get_next_keyframe(MPathTarget *mpt, DLRBT_Tree *fcu_keys, int current_frame)
+static int motionpath_get_next_keyframe(MPathTarget *mpt,
+ struct AnimKeylist *keylist,
+ int current_frame)
{
if (current_frame >= mpt->mpath->end_frame) {
return mpt->mpath->end_frame;
}
float current_frame_float = current_frame;
- DLRBT_Node *node = BLI_dlrbTree_search_next(fcu_keys, compare_ak_cfraPtr, &current_frame_float);
- if (node == NULL) {
+ const ActKeyColumn *ak = ED_keylist_find_next(keylist, current_frame_float);
+ if (ak == NULL) {
return mpt->mpath->end_frame;
}
- ActKeyColumn *key_data = (ActKeyColumn *)node;
- return key_data->cfra;
+ return ak->cfra;
}
static int motionpath_get_next_next_keyframe(MPathTarget *mpt,
- DLRBT_Tree *fcu_keys,
+ struct AnimKeylist *keylist,
int current_frame)
{
- int frame = motionpath_get_next_keyframe(mpt, fcu_keys, current_frame);
- return motionpath_get_next_keyframe(mpt, fcu_keys, frame);
+ int frame = motionpath_get_next_keyframe(mpt, keylist, current_frame);
+ return motionpath_get_next_keyframe(mpt, keylist, frame);
}
static bool motionpath_check_can_use_keyframe_range(MPathTarget *UNUSED(mpt),
@@ -324,17 +327,16 @@ static void motionpath_calculate_update_range(MPathTarget *mpt,
* Could be optimized further by storing some flags about which channels has been modified so
* we ignore all others (which can potentially make an update range unnecessary wide). */
for (FCurve *fcu = fcurve_list->first; fcu != NULL; fcu = fcu->next) {
- DLRBT_Tree fcu_keys;
- BLI_dlrbTree_init(&fcu_keys);
- fcurve_to_keylist(adt, fcu, &fcu_keys, 0);
+ struct AnimKeylist *keylist = ED_keylist_create();
+ fcurve_to_keylist(adt, fcu, keylist, 0);
- int fcu_sfra = motionpath_get_prev_prev_keyframe(mpt, &fcu_keys, current_frame);
- int fcu_efra = motionpath_get_next_next_keyframe(mpt, &fcu_keys, current_frame);
+ int fcu_sfra = motionpath_get_prev_prev_keyframe(mpt, keylist, current_frame);
+ int fcu_efra = motionpath_get_next_next_keyframe(mpt, keylist, current_frame);
/* Extend range further, since acceleration compensation propagates even further away. */
if (fcu->auto_smoothing != FCURVE_SMOOTH_NONE) {
- fcu_sfra = motionpath_get_prev_prev_keyframe(mpt, &fcu_keys, fcu_sfra);
- fcu_efra = motionpath_get_next_next_keyframe(mpt, &fcu_keys, fcu_efra);
+ fcu_sfra = motionpath_get_prev_prev_keyframe(mpt, keylist, fcu_sfra);
+ fcu_efra = motionpath_get_next_next_keyframe(mpt, keylist, fcu_efra);
}
if (fcu_sfra <= fcu_efra) {
@@ -342,14 +344,14 @@ static void motionpath_calculate_update_range(MPathTarget *mpt,
*r_efra = max_ii(*r_efra, fcu_efra);
}
- BLI_dlrbTree_free(&fcu_keys);
+ ED_keylist_free(keylist);
}
}
static void motionpath_free_free_tree_data(ListBase *targets)
{
LISTBASE_FOREACH (MPathTarget *, mpt, targets) {
- BLI_dlrbTree_free(&mpt->keys);
+ ED_keylist_free(mpt->keylist);
}
}
@@ -418,7 +420,7 @@ void animviz_calc_motionpaths(Depsgraph *depsgraph,
AnimData *adt = BKE_animdata_from_id(&mpt->ob_eval->id);
/* build list of all keyframes in active action for object or pchan */
- BLI_dlrbTree_init(&mpt->keys);
+ mpt->keylist = ED_keylist_create();
ListBase *fcurve_list = NULL;
if (adt) {
@@ -433,12 +435,12 @@ void animviz_calc_motionpaths(Depsgraph *depsgraph,
if (agrp) {
fcurve_list = &agrp->channels;
- agroup_to_keylist(adt, agrp, &mpt->keys, 0);
+ agroup_to_keylist(adt, agrp, mpt->keylist, 0);
}
}
else {
fcurve_list = &adt->action->curves;
- action_to_keylist(adt, adt->action, &mpt->keys, 0);
+ action_to_keylist(adt, adt->action, mpt->keylist, 0);
}
}
@@ -502,7 +504,7 @@ void animviz_calc_motionpaths(Depsgraph *depsgraph,
avs->recalc &= ~ANIMVIZ_RECALC_PATHS;
/* Clean temp data */
- BLI_dlrbTree_free(&mpt->keys);
+ ED_keylist_free(mpt->keylist);
/* Free previous batches to force update. */
GPU_VERTBUF_DISCARD_SAFE(mpath->points_vbo);
diff --git a/source/blender/editors/animation/fmodifier_ui.c b/source/blender/editors/animation/fmodifier_ui.c
index 31552330071..40871fba2be 100644
--- a/source/blender/editors/animation/fmodifier_ui.c
+++ b/source/blender/editors/animation/fmodifier_ui.c
@@ -672,10 +672,7 @@ static void fmod_envelope_deletepoint_cb(bContext *UNUSED(C), void *fcm_dv, void
}
else {
/* just free array, since the only vert was deleted */
- if (env->data) {
- MEM_freeN(env->data);
- env->data = NULL;
- }
+ MEM_SAFE_FREE(env->data);
env->totvert = 0;
}
}
diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c
index 4f512c9d7ca..61918871b90 100644
--- a/source/blender/editors/animation/keyframes_draw.c
+++ b/source/blender/editors/animation/keyframes_draw.c
@@ -25,6 +25,8 @@
#include <float.h>
+#include "MEM_guardedalloc.h"
+
#include "BLI_dlrbTree.h"
#include "BLI_listbase.h"
#include "BLI_rect.h"
@@ -55,11 +57,7 @@ void draw_keyframe_shape(float x,
short key_type,
short mode,
float alpha,
- uint pos_id,
- uint size_id,
- uint color_id,
- uint outline_color_id,
- uint flags_id,
+ const KeyframeShaderBindings *sh_bindings,
short handle_type,
short extreme_type)
{
@@ -176,354 +174,563 @@ void draw_keyframe_shape(float x,
}
}
- immAttr1f(size_id, size);
- immAttr4ubv(color_id, fill_col);
- immAttr4ubv(outline_color_id, outline_col);
- immAttr1u(flags_id, flags);
- immVertex2f(pos_id, x, y);
+ immAttr1f(sh_bindings->size_id, size);
+ immAttr4ubv(sh_bindings->color_id, fill_col);
+ immAttr4ubv(sh_bindings->outline_color_id, outline_col);
+ immAttr1u(sh_bindings->flags_id, flags);
+ immVertex2f(sh_bindings->pos_id, x, y);
}
-static void draw_keylist(View2D *v2d,
- DLRBT_Tree *keys,
- float ypos,
- float yscale_fac,
- bool channelLocked,
- int saction_flag)
-{
- if (keys == NULL) {
- return;
- }
-
- const float icon_sz = U.widget_unit * 0.5f * yscale_fac;
- const float half_icon_sz = 0.5f * icon_sz;
- const float smaller_sz = 0.35f * icon_sz;
- const float ipo_sz = 0.1f * icon_sz;
- const float gpencil_sz = smaller_sz * 0.8f;
- const float screenspace_margin = (0.35f * (float)UI_UNIT_X) / UI_view2d_scale_get_x(v2d);
+/* Common attributes shared between the draw calls. */
+typedef struct DrawKeylistUIData {
+ float alpha;
+ float icon_sz;
+ float half_icon_sz;
+ float smaller_sz;
+ float ipo_sz;
+ float gpencil_sz;
+ float screenspace_margin;
+ float sel_color[4];
+ float unsel_color[4];
+ float sel_mhcol[4];
+ float unsel_mhcol[4];
+ float ipo_color[4];
+ float ipo_color_mix[4];
+ /* Show interpolation and handle type? */
+ bool show_ipo;
+} DrawKeylistUIData;
+
+static void draw_keylist_ui_data_init(DrawKeylistUIData *ctx,
+ View2D *v2d,
+ float yscale_fac,
+ bool channel_locked,
+ eSAction_Flag saction_flag)
+{
/* locked channels are less strongly shown, as feedback for locked channels in DopeSheet */
/* TODO: allow this opacity factor to be themed? */
- float alpha = channelLocked ? 0.25f : 1.0f;
+ ctx->alpha = channel_locked ? 0.25f : 1.0f;
+
+ ctx->icon_sz = U.widget_unit * 0.5f * yscale_fac;
+ ctx->half_icon_sz = 0.5f * ctx->icon_sz;
+ ctx->smaller_sz = 0.35f * ctx->icon_sz;
+ ctx->ipo_sz = 0.1f * ctx->icon_sz;
+ ctx->gpencil_sz = ctx->smaller_sz * 0.8f;
+ ctx->screenspace_margin = (0.35f * (float)UI_UNIT_X) / UI_view2d_scale_get_x(v2d);
+
+ ctx->show_ipo = (saction_flag & SACTION_SHOW_INTERPOLATION) != 0;
+
+ UI_GetThemeColor4fv(TH_STRIP_SELECT, ctx->sel_color);
+ UI_GetThemeColor4fv(TH_STRIP, ctx->unsel_color);
+ UI_GetThemeColor4fv(TH_DOPESHEET_IPOLINE, ctx->ipo_color);
+
+ ctx->sel_color[3] *= ctx->alpha;
+ ctx->unsel_color[3] *= ctx->alpha;
+ ctx->ipo_color[3] *= ctx->alpha;
+
+ copy_v4_v4(ctx->sel_mhcol, ctx->sel_color);
+ ctx->sel_mhcol[3] *= 0.8f;
+ copy_v4_v4(ctx->unsel_mhcol, ctx->unsel_color);
+ ctx->unsel_mhcol[3] *= 0.8f;
+ copy_v4_v4(ctx->ipo_color_mix, ctx->ipo_color);
+ ctx->ipo_color_mix[3] *= 0.5f;
+}
- /* Show interpolation and handle type? */
- bool show_ipo = (saction_flag & SACTION_SHOW_INTERPOLATION) != 0;
- /* draw keyblocks */
- float sel_color[4], unsel_color[4];
- float sel_mhcol[4], unsel_mhcol[4];
- float ipo_color[4], ipo_color_mix[4];
-
- /* cache colors first */
- UI_GetThemeColor4fv(TH_STRIP_SELECT, sel_color);
- UI_GetThemeColor4fv(TH_STRIP, unsel_color);
- UI_GetThemeColor4fv(TH_DOPESHEET_IPOLINE, ipo_color);
-
- sel_color[3] *= alpha;
- unsel_color[3] *= alpha;
- ipo_color[3] *= alpha;
-
- copy_v4_v4(sel_mhcol, sel_color);
- sel_mhcol[3] *= 0.8f;
- copy_v4_v4(unsel_mhcol, unsel_color);
- unsel_mhcol[3] *= 0.8f;
- copy_v4_v4(ipo_color_mix, ipo_color);
- ipo_color_mix[3] *= 0.5f;
-
- LISTBASE_FOREACH (ActKeyColumn *, ab, keys) {
- /* Draw grease pencil bars between keyframes. */
- if ((ab->next != NULL) && (ab->block.flag & ACTKEYBLOCK_FLAG_GPENCIL)) {
- UI_draw_roundbox_corner_set(UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT);
- float size = 1.0f;
- switch (ab->next->key_type) {
- case BEZT_KEYTYPE_BREAKDOWN:
- case BEZT_KEYTYPE_MOVEHOLD:
- case BEZT_KEYTYPE_JITTER:
- size *= 0.5f;
- break;
- case BEZT_KEYTYPE_KEYFRAME:
- size *= 0.8f;
- break;
- default:
- break;
- }
- UI_draw_roundbox_4fv(
- &(const rctf){
- .xmin = ab->cfra,
- .xmax = min_ff(ab->next->cfra - (screenspace_margin * size), ab->next->cfra),
- .ymin = ypos - gpencil_sz,
- .ymax = ypos + gpencil_sz,
- },
- true,
- 0.25f * (float)UI_UNIT_X,
- (ab->block.sel) ? sel_mhcol : unsel_mhcol);
- }
- else {
- /* Draw other types. */
- UI_draw_roundbox_corner_set(UI_CNR_NONE);
-
- int valid_hold = actkeyblock_get_valid_hold(ab);
- if (valid_hold != 0) {
- if ((valid_hold & ACTKEYBLOCK_FLAG_STATIC_HOLD) == 0) {
- /* draw "moving hold" long-keyframe block - slightly smaller */
- UI_draw_roundbox_4fv(
- &(const rctf){
- .xmin = ab->cfra,
- .xmax = ab->next->cfra,
- .ymin = ypos - smaller_sz,
- .ymax = ypos + smaller_sz,
- },
- true,
- 3.0f,
- (ab->block.sel) ? sel_mhcol : unsel_mhcol);
- }
- else {
- /* draw standard long-keyframe block */
- UI_draw_roundbox_4fv(
- &(const rctf){
- .xmin = ab->cfra,
- .xmax = ab->next->cfra,
- .ymin = ypos - half_icon_sz,
- .ymax = ypos + half_icon_sz,
- },
- true,
- 3.0f,
- (ab->block.sel) ? sel_color : unsel_color);
- }
- }
- if (show_ipo && actkeyblock_is_valid(ab) && (ab->block.flag & ACTKEYBLOCK_FLAG_NON_BEZIER)) {
- /* draw an interpolation line */
- UI_draw_roundbox_4fv(
- &(const rctf){
- .xmin = ab->cfra,
- .xmax = ab->next->cfra,
- .ymin = ypos - ipo_sz,
- .ymax = ypos + ipo_sz,
- },
- true,
- 3.0f,
- (ab->block.conflict & ACTKEYBLOCK_FLAG_NON_BEZIER) ? ipo_color_mix : ipo_color);
- }
- }
+static void draw_keylist_block_gpencil(const DrawKeylistUIData *ctx,
+ const ActKeyColumn *ab,
+ float ypos)
+{
+ UI_draw_roundbox_corner_set(UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT);
+ float size = 1.0f;
+ switch (ab->next->key_type) {
+ case BEZT_KEYTYPE_BREAKDOWN:
+ case BEZT_KEYTYPE_MOVEHOLD:
+ case BEZT_KEYTYPE_JITTER:
+ size *= 0.5f;
+ break;
+ case BEZT_KEYTYPE_KEYFRAME:
+ size *= 0.8f;
+ break;
+ default:
+ break;
}
+ UI_draw_roundbox_4fv(
+ &(const rctf){
+ .xmin = ab->cfra,
+ .xmax = min_ff(ab->next->cfra - (ctx->screenspace_margin * size), ab->next->cfra),
+ .ymin = ypos - ctx->gpencil_sz,
+ .ymax = ypos + ctx->gpencil_sz,
+ },
+ true,
+ 0.25f * (float)UI_UNIT_X,
+ (ab->block.sel) ? ctx->sel_mhcol : ctx->unsel_mhcol);
+}
- GPU_blend(GPU_BLEND_ALPHA);
+static void draw_keylist_block_moving_hold(const DrawKeylistUIData *ctx,
+ const ActKeyColumn *ab,
+ float ypos)
+{
- /* count keys */
- uint key_len = 0;
- LISTBASE_FOREACH (ActKeyColumn *, ak, keys) {
- /* Optimization: if keyframe doesn't appear within 5 units (screenspace)
- * in visible area, don't draw.
- * This might give some improvements,
- * since we current have to flip between view/region matrices.
- */
- if (IN_RANGE_INCL(ak->cfra, v2d->cur.xmin, v2d->cur.xmax)) {
- key_len++;
- }
- }
+ UI_draw_roundbox_4fv(
+ &(const rctf){
+ .xmin = ab->cfra,
+ .xmax = ab->next->cfra,
+ .ymin = ypos - ctx->smaller_sz,
+ .ymax = ypos + ctx->smaller_sz,
+ },
+ true,
+ 3.0f,
+ (ab->block.sel) ? ctx->sel_mhcol : ctx->unsel_mhcol);
+}
+
+static void draw_keylist_block_standard(const DrawKeylistUIData *ctx,
+ const ActKeyColumn *ab,
+ float ypos)
+{
+ UI_draw_roundbox_4fv(
+ &(const rctf){
+ .xmin = ab->cfra,
+ .xmax = ab->next->cfra,
+ .ymin = ypos - ctx->half_icon_sz,
+ .ymax = ypos + ctx->half_icon_sz,
+ },
+ true,
+ 3.0f,
+ (ab->block.sel) ? ctx->sel_color : ctx->unsel_color);
+}
+
+static void draw_keylist_block_interpolation_line(const DrawKeylistUIData *ctx,
+ const ActKeyColumn *ab,
+ float ypos)
+{
+ UI_draw_roundbox_4fv(
+ &(const rctf){
+ .xmin = ab->cfra,
+ .xmax = ab->next->cfra,
+ .ymin = ypos - ctx->ipo_sz,
+ .ymax = ypos + ctx->ipo_sz,
+ },
+ true,
+ 3.0f,
+ (ab->block.conflict & ACTKEYBLOCK_FLAG_NON_BEZIER) ? ctx->ipo_color_mix : ctx->ipo_color);
+}
+
+static void draw_keylist_block(const DrawKeylistUIData *ctx, const ActKeyColumn *ab, float ypos)
+{
- if (key_len > 0) {
- /* draw keys */
- GPUVertFormat *format = immVertexFormat();
- uint pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- uint size_id = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
- uint color_id = GPU_vertformat_attr_add(
- format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
- uint outline_color_id = GPU_vertformat_attr_add(
- format, "outlineColor", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
- uint flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT);
-
- GPU_program_point_size(true);
- immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND);
- immUniform1f("outline_scale", 1.0f);
- immUniform2f("ViewportSize", BLI_rcti_size_x(&v2d->mask) + 1, BLI_rcti_size_y(&v2d->mask) + 1);
- immBegin(GPU_PRIM_POINTS, key_len);
-
- short handle_type = KEYFRAME_HANDLE_NONE, extreme_type = KEYFRAME_EXTREME_NONE;
-
- LISTBASE_FOREACH (ActKeyColumn *, ak, keys) {
- if (IN_RANGE_INCL(ak->cfra, v2d->cur.xmin, v2d->cur.xmax)) {
- if (show_ipo) {
- handle_type = ak->handle_type;
- }
- if (saction_flag & SACTION_SHOW_EXTREMES) {
- extreme_type = ak->extreme_type;
- }
-
- draw_keyframe_shape(ak->cfra,
- ypos,
- icon_sz,
- (ak->sel & SELECT),
- ak->key_type,
- KEYFRAME_SHAPE_BOTH,
- alpha,
- pos_id,
- size_id,
- color_id,
- outline_color_id,
- flags_id,
- handle_type,
- extreme_type);
+ /* Draw grease pencil bars between keyframes. */
+ if ((ab->next != NULL) && (ab->block.flag & ACTKEYBLOCK_FLAG_GPENCIL)) {
+ draw_keylist_block_gpencil(ctx, ab, ypos);
+ }
+ else {
+ /* Draw other types. */
+ UI_draw_roundbox_corner_set(UI_CNR_NONE);
+
+ int valid_hold = actkeyblock_get_valid_hold(ab);
+ if (valid_hold != 0) {
+ if ((valid_hold & ACTKEYBLOCK_FLAG_STATIC_HOLD) == 0) {
+ /* draw "moving hold" long-keyframe block - slightly smaller */
+ draw_keylist_block_moving_hold(ctx, ab, ypos);
+ }
+ else {
+ /* draw standard long-keyframe block */
+ draw_keylist_block_standard(ctx, ab, ypos);
}
}
-
- immEnd();
- GPU_program_point_size(false);
- immUnbindProgram();
+ if (ctx->show_ipo && actkeyblock_is_valid(ab) &&
+ (ab->block.flag & ACTKEYBLOCK_FLAG_NON_BEZIER)) {
+ /* draw an interpolation line */
+ draw_keylist_block_interpolation_line(ctx, ab, ypos);
+ }
}
-
- GPU_blend(GPU_BLEND_NONE);
}
-/* *************************** Channel Drawing Funcs *************************** */
-
-void draw_summary_channel(
- View2D *v2d, bAnimContext *ac, float ypos, float yscale_fac, int saction_flag)
+static void draw_keylist_blocks(const DrawKeylistUIData *ctx,
+ const ListBase * /*ActKeyColumn*/ columns,
+ float ypos)
{
- DLRBT_Tree keys;
-
- saction_flag &= ~SACTION_SHOW_EXTREMES;
+ LISTBASE_FOREACH (ActKeyColumn *, ab, columns) {
+ draw_keylist_block(ctx, ab, ypos);
+ }
+}
- BLI_dlrbTree_init(&keys);
+static bool draw_keylist_is_visible_key(const View2D *v2d, const ActKeyColumn *ak)
+{
+ return IN_RANGE_INCL(ak->cfra, v2d->cur.xmin, v2d->cur.xmax);
+}
- summary_to_keylist(ac, &keys, saction_flag);
+static void draw_keylist_keys(const DrawKeylistUIData *ctx,
+ View2D *v2d,
+ const KeyframeShaderBindings *sh_bindings,
+ const ListBase * /*ActKeyColumn*/ keys,
+ float ypos,
+ eSAction_Flag saction_flag)
+{
+ short handle_type = KEYFRAME_HANDLE_NONE, extreme_type = KEYFRAME_EXTREME_NONE;
- draw_keylist(v2d, &keys, ypos, yscale_fac, false, saction_flag);
+ LISTBASE_FOREACH (ActKeyColumn *, ak, keys) {
+ if (draw_keylist_is_visible_key(v2d, ak)) {
+ if (ctx->show_ipo) {
+ handle_type = ak->handle_type;
+ }
+ if (saction_flag & SACTION_SHOW_EXTREMES) {
+ extreme_type = ak->extreme_type;
+ }
- BLI_dlrbTree_free(&keys);
+ draw_keyframe_shape(ak->cfra,
+ ypos,
+ ctx->icon_sz,
+ (ak->sel & SELECT),
+ ak->key_type,
+ KEYFRAME_SHAPE_BOTH,
+ ctx->alpha,
+ sh_bindings,
+ handle_type,
+ extreme_type);
+ }
+ }
}
-void draw_scene_channel(
- View2D *v2d, bDopeSheet *ads, Scene *sce, float ypos, float yscale_fac, int saction_flag)
+/* *************************** Drawing Stack *************************** */
+typedef enum eAnimKeylistDrawListElemType {
+ ANIM_KEYLIST_SUMMARY,
+ ANIM_KEYLIST_SCENE,
+ ANIM_KEYLIST_OBJECT,
+ ANIM_KEYLIST_FCURVE,
+ ANIM_KEYLIST_ACTION,
+ ANIM_KEYLIST_AGROUP,
+ ANIM_KEYLIST_GP_LAYER,
+ ANIM_KEYLIST_MASK_LAYER,
+} eAnimKeylistDrawListElemType;
+
+typedef struct AnimKeylistDrawListElem {
+ struct AnimKeylistDrawListElem *next, *prev;
+ struct AnimKeylist *keylist;
+ eAnimKeylistDrawListElemType type;
+
+ float yscale_fac;
+ float ypos;
+ eSAction_Flag saction_flag;
+ bool channel_locked;
+
+ bAnimContext *ac;
+ bDopeSheet *ads;
+ Scene *sce;
+ Object *ob;
+ AnimData *adt;
+ FCurve *fcu;
+ bAction *act;
+ bActionGroup *agrp;
+ bGPDlayer *gpl;
+ MaskLayer *masklay;
+
+} AnimKeylistDrawListElem;
+
+static void ED_keylist_draw_list_elem_build_keylist(AnimKeylistDrawListElem *elem)
{
- DLRBT_Tree keys;
+ switch (elem->type) {
+ case ANIM_KEYLIST_SUMMARY: {
+ summary_to_keylist(elem->ac, elem->keylist, elem->saction_flag);
+ break;
+ }
+ case ANIM_KEYLIST_SCENE: {
+ scene_to_keylist(elem->ads, elem->sce, elem->keylist, elem->saction_flag);
+ break;
+ }
+ case ANIM_KEYLIST_OBJECT: {
+ ob_to_keylist(elem->ads, elem->ob, elem->keylist, elem->saction_flag);
+ break;
+ }
+ case ANIM_KEYLIST_FCURVE: {
+ fcurve_to_keylist(elem->adt, elem->fcu, elem->keylist, elem->saction_flag);
+ break;
+ }
+ case ANIM_KEYLIST_ACTION: {
+ action_to_keylist(elem->adt, elem->act, elem->keylist, elem->saction_flag);
+ break;
+ }
+ case ANIM_KEYLIST_AGROUP: {
+ agroup_to_keylist(elem->adt, elem->agrp, elem->keylist, elem->saction_flag);
+ break;
+ }
+ case ANIM_KEYLIST_GP_LAYER: {
+ gpl_to_keylist(elem->ads, elem->gpl, elem->keylist);
+ break;
+ }
+ case ANIM_KEYLIST_MASK_LAYER: {
+ mask_to_keylist(elem->ads, elem->masklay, elem->keylist);
+ break;
+ }
+ }
+}
- saction_flag &= ~SACTION_SHOW_EXTREMES;
+static void ED_keylist_draw_list_elem_draw_blocks(AnimKeylistDrawListElem *elem, View2D *v2d)
+{
+ DrawKeylistUIData ctx;
+ draw_keylist_ui_data_init(&ctx, v2d, elem->yscale_fac, elem->channel_locked, elem->saction_flag);
- BLI_dlrbTree_init(&keys);
+ const ListBase *keys = ED_keylist_listbase(elem->keylist);
+ draw_keylist_blocks(&ctx, keys, elem->ypos);
+}
- scene_to_keylist(ads, sce, &keys, saction_flag);
+static void ED_keylist_draw_list_elem_draw_keys(AnimKeylistDrawListElem *elem,
+ View2D *v2d,
+ const KeyframeShaderBindings *sh_bindings)
+{
+ DrawKeylistUIData ctx;
+ draw_keylist_ui_data_init(&ctx, v2d, elem->yscale_fac, elem->channel_locked, elem->saction_flag);
+ const ListBase *keys = ED_keylist_listbase(elem->keylist);
+ draw_keylist_keys(&ctx, v2d, sh_bindings, keys, elem->ypos, elem->saction_flag);
+}
- draw_keylist(v2d, &keys, ypos, yscale_fac, false, saction_flag);
+typedef struct AnimKeylistDrawList {
+ ListBase /* AnimKeylistDrawListElem*/ channels;
+} AnimKeylistDrawList;
- BLI_dlrbTree_free(&keys);
+AnimKeylistDrawList *ED_keylist_draw_list_create(void)
+{
+ return MEM_callocN(sizeof(AnimKeylistDrawList), __func__);
}
-void draw_object_channel(
- View2D *v2d, bDopeSheet *ads, Object *ob, float ypos, float yscale_fac, int saction_flag)
+static void ED_keylist_draw_list_build_keylists(AnimKeylistDrawList *draw_list)
{
- DLRBT_Tree keys;
-
- saction_flag &= ~SACTION_SHOW_EXTREMES;
-
- BLI_dlrbTree_init(&keys);
+ LISTBASE_FOREACH (AnimKeylistDrawListElem *, elem, &draw_list->channels) {
+ ED_keylist_draw_list_elem_build_keylist(elem);
+ }
+}
- ob_to_keylist(ads, ob, &keys, saction_flag);
+static void ED_keylist_draw_list_draw_blocks(AnimKeylistDrawList *draw_list, View2D *v2d)
+{
+ LISTBASE_FOREACH (AnimKeylistDrawListElem *, elem, &draw_list->channels) {
+ ED_keylist_draw_list_elem_draw_blocks(elem, v2d);
+ }
+}
- draw_keylist(v2d, &keys, ypos, yscale_fac, false, saction_flag);
+static int ED_keylist_draw_keylist_visible_key_len(const View2D *v2d,
+ const ListBase * /*ActKeyColumn*/ keys)
+{
+ /* count keys */
+ uint len = 0;
- BLI_dlrbTree_free(&keys);
+ LISTBASE_FOREACH (ActKeyColumn *, ak, keys) {
+ /* Optimization: if keyframe doesn't appear within 5 units (screenspace)
+ * in visible area, don't draw.
+ * This might give some improvements,
+ * since we current have to flip between view/region matrices.
+ */
+ if (draw_keylist_is_visible_key(v2d, ak)) {
+ len++;
+ }
+ }
+ return len;
}
-void draw_fcurve_channel(
- View2D *v2d, AnimData *adt, FCurve *fcu, float ypos, float yscale_fac, int saction_flag)
+static int ED_keylist_draw_list_visible_key_len(const AnimKeylistDrawList *draw_list,
+ const View2D *v2d)
{
- DLRBT_Tree keys;
+ uint len = 0;
+ LISTBASE_FOREACH (AnimKeylistDrawListElem *, elem, &draw_list->channels) {
+ const ListBase *keys = ED_keylist_listbase(elem->keylist);
+ len += ED_keylist_draw_keylist_visible_key_len(v2d, keys);
+ }
+ return len;
+}
- bool locked = (fcu->flag & FCURVE_PROTECTED) ||
- ((fcu->grp) && (fcu->grp->flag & AGRP_PROTECTED)) ||
- ((adt && adt->action) && ID_IS_LINKED(adt->action));
+static void ED_keylist_draw_list_draw_keys(AnimKeylistDrawList *draw_list, View2D *v2d)
+{
+ const int visible_key_len = ED_keylist_draw_list_visible_key_len(draw_list, v2d);
+ if (visible_key_len == 0) {
+ return;
+ }
- BLI_dlrbTree_init(&keys);
+ GPU_blend(GPU_BLEND_ALPHA);
- fcurve_to_keylist(adt, fcu, &keys, saction_flag);
+ GPUVertFormat *format = immVertexFormat();
+ KeyframeShaderBindings sh_bindings;
+
+ sh_bindings.pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ sh_bindings.size_id = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+ sh_bindings.color_id = GPU_vertformat_attr_add(
+ format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ sh_bindings.outline_color_id = GPU_vertformat_attr_add(
+ format, "outlineColor", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ sh_bindings.flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT);
+
+ GPU_program_point_size(true);
+ immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND);
+ immUniform1f("outline_scale", 1.0f);
+ immUniform2f("ViewportSize", BLI_rcti_size_x(&v2d->mask) + 1, BLI_rcti_size_y(&v2d->mask) + 1);
+ immBegin(GPU_PRIM_POINTS, visible_key_len);
+
+ LISTBASE_FOREACH (AnimKeylistDrawListElem *, elem, &draw_list->channels) {
+ ED_keylist_draw_list_elem_draw_keys(elem, v2d, &sh_bindings);
+ }
- draw_keylist(v2d, &keys, ypos, yscale_fac, locked, saction_flag);
+ immEnd();
+ GPU_program_point_size(false);
+ immUnbindProgram();
- BLI_dlrbTree_free(&keys);
+ GPU_blend(GPU_BLEND_NONE);
}
-void draw_agroup_channel(
- View2D *v2d, AnimData *adt, bActionGroup *agrp, float ypos, float yscale_fac, int saction_flag)
+static void ED_keylist_draw_list_draw(AnimKeylistDrawList *draw_list, View2D *v2d)
{
- DLRBT_Tree keys;
+ ED_keylist_draw_list_draw_blocks(draw_list, v2d);
+ ED_keylist_draw_list_draw_keys(draw_list, v2d);
+}
- bool locked = (agrp->flag & AGRP_PROTECTED) ||
- ((adt && adt->action) && ID_IS_LINKED(adt->action));
+void ED_keylist_draw_list_flush(AnimKeylistDrawList *draw_list, View2D *v2d)
+{
+ ED_keylist_draw_list_build_keylists(draw_list);
+ ED_keylist_draw_list_draw(draw_list, v2d);
+}
- BLI_dlrbTree_init(&keys);
+void ED_keylist_draw_list_free(AnimKeylistDrawList *draw_list)
+{
+ LISTBASE_FOREACH (AnimKeylistDrawListElem *, elem, &draw_list->channels) {
+ ED_keylist_free(elem->keylist);
+ }
+ BLI_freelistN(&draw_list->channels);
+ MEM_freeN(draw_list);
+}
- agroup_to_keylist(adt, agrp, &keys, saction_flag);
+static AnimKeylistDrawListElem *ed_keylist_draw_list_add_elem(
+ AnimKeylistDrawList *draw_list,
+ eAnimKeylistDrawListElemType elem_type,
+ float ypos,
+ float yscale_fac,
+ eSAction_Flag saction_flag)
+{
+ AnimKeylistDrawListElem *draw_elem = MEM_callocN(sizeof(AnimKeylistDrawListElem), __func__);
+ BLI_addtail(&draw_list->channels, draw_elem);
+ draw_elem->type = elem_type;
+ draw_elem->keylist = ED_keylist_create();
+ draw_elem->ypos = ypos;
+ draw_elem->yscale_fac = yscale_fac;
+ draw_elem->saction_flag = saction_flag;
+ return draw_elem;
+}
- draw_keylist(v2d, &keys, ypos, yscale_fac, locked, saction_flag);
+/* *************************** Channel Drawing Funcs *************************** */
- BLI_dlrbTree_free(&keys);
+void draw_summary_channel(struct AnimKeylistDrawList *draw_list,
+ bAnimContext *ac,
+ float ypos,
+ float yscale_fac,
+ int saction_flag)
+{
+ saction_flag &= ~SACTION_SHOW_EXTREMES;
+ AnimKeylistDrawListElem *draw_elem = ed_keylist_draw_list_add_elem(
+ draw_list, ANIM_KEYLIST_SUMMARY, ypos, yscale_fac, saction_flag);
+ draw_elem->ac = ac;
}
-void draw_action_channel(
- View2D *v2d, AnimData *adt, bAction *act, float ypos, float yscale_fac, int saction_flag)
+void draw_scene_channel(AnimKeylistDrawList *draw_list,
+ bDopeSheet *ads,
+ Scene *sce,
+ float ypos,
+ float yscale_fac,
+ int saction_flag)
{
- DLRBT_Tree keys;
-
- bool locked = (act && ID_IS_LINKED(act));
-
saction_flag &= ~SACTION_SHOW_EXTREMES;
+ AnimKeylistDrawListElem *draw_elem = ed_keylist_draw_list_add_elem(
+ draw_list, ANIM_KEYLIST_SCENE, ypos, yscale_fac, saction_flag);
+ draw_elem->ads = ads;
+ draw_elem->sce = sce;
+}
- BLI_dlrbTree_init(&keys);
+void draw_object_channel(AnimKeylistDrawList *draw_list,
+ bDopeSheet *ads,
+ Object *ob,
+ float ypos,
+ float yscale_fac,
+ int saction_flag)
+{
+ saction_flag &= ~SACTION_SHOW_EXTREMES;
+ AnimKeylistDrawListElem *draw_elem = ed_keylist_draw_list_add_elem(
+ draw_list, ANIM_KEYLIST_OBJECT, ypos, yscale_fac, saction_flag);
+ draw_elem->ads = ads;
+ draw_elem->ob = ob;
+}
- action_to_keylist(adt, act, &keys, saction_flag);
+void draw_fcurve_channel(AnimKeylistDrawList *draw_list,
+ AnimData *adt,
+ FCurve *fcu,
+ float ypos,
+ float yscale_fac,
+ int saction_flag)
+{
+ const bool locked = (fcu->flag & FCURVE_PROTECTED) ||
+ ((fcu->grp) && (fcu->grp->flag & AGRP_PROTECTED)) ||
+ ((adt && adt->action) && ID_IS_LINKED(adt->action));
+
+ AnimKeylistDrawListElem *draw_elem = ed_keylist_draw_list_add_elem(
+ draw_list, ANIM_KEYLIST_FCURVE, ypos, yscale_fac, saction_flag);
+ draw_elem->adt = adt;
+ draw_elem->fcu = fcu;
+ draw_elem->channel_locked = locked;
+}
- draw_keylist(v2d, &keys, ypos, yscale_fac, locked, saction_flag);
+void draw_agroup_channel(AnimKeylistDrawList *draw_list,
+ AnimData *adt,
+ bActionGroup *agrp,
+ float ypos,
+ float yscale_fac,
+ int saction_flag)
+{
+ bool locked = (agrp->flag & AGRP_PROTECTED) ||
+ ((adt && adt->action) && ID_IS_LINKED(adt->action));
- BLI_dlrbTree_free(&keys);
+ AnimKeylistDrawListElem *draw_elem = ed_keylist_draw_list_add_elem(
+ draw_list, ANIM_KEYLIST_AGROUP, ypos, yscale_fac, saction_flag);
+ draw_elem->adt = adt;
+ draw_elem->agrp = agrp;
+ draw_elem->channel_locked = locked;
}
-void draw_gpencil_channel(
- View2D *v2d, bDopeSheet *ads, bGPdata *gpd, float ypos, float yscale_fac, int saction_flag)
+void draw_action_channel(AnimKeylistDrawList *draw_list,
+ AnimData *adt,
+ bAction *act,
+ float ypos,
+ float yscale_fac,
+ int saction_flag)
{
- DLRBT_Tree keys;
-
+ const bool locked = (act && ID_IS_LINKED(act));
saction_flag &= ~SACTION_SHOW_EXTREMES;
- BLI_dlrbTree_init(&keys);
-
- gpencil_to_keylist(ads, gpd, &keys, false);
-
- draw_keylist(v2d, &keys, ypos, yscale_fac, false, saction_flag);
-
- BLI_dlrbTree_free(&keys);
+ AnimKeylistDrawListElem *draw_elem = ed_keylist_draw_list_add_elem(
+ draw_list, ANIM_KEYLIST_ACTION, ypos, yscale_fac, saction_flag);
+ draw_elem->adt = adt;
+ draw_elem->act = act;
+ draw_elem->channel_locked = locked;
}
-void draw_gpl_channel(
- View2D *v2d, bDopeSheet *ads, bGPDlayer *gpl, float ypos, float yscale_fac, int saction_flag)
+void draw_gpl_channel(AnimKeylistDrawList *draw_list,
+ bDopeSheet *ads,
+ bGPDlayer *gpl,
+ float ypos,
+ float yscale_fac,
+ int saction_flag)
{
- DLRBT_Tree keys;
-
bool locked = (gpl->flag & GP_LAYER_LOCKED) != 0;
-
- BLI_dlrbTree_init(&keys);
-
- gpl_to_keylist(ads, gpl, &keys);
-
- draw_keylist(v2d, &keys, ypos, yscale_fac, locked, saction_flag);
-
- BLI_dlrbTree_free(&keys);
+ AnimKeylistDrawListElem *draw_elem = ed_keylist_draw_list_add_elem(
+ draw_list, ANIM_KEYLIST_GP_LAYER, ypos, yscale_fac, saction_flag);
+ draw_elem->ads = ads;
+ draw_elem->gpl = gpl;
+ draw_elem->channel_locked = locked;
}
-void draw_masklay_channel(View2D *v2d,
+void draw_masklay_channel(AnimKeylistDrawList *draw_list,
bDopeSheet *ads,
MaskLayer *masklay,
float ypos,
float yscale_fac,
int saction_flag)
{
- DLRBT_Tree keys;
-
bool locked = (masklay->flag & MASK_LAYERFLAG_LOCKED) != 0;
-
- BLI_dlrbTree_init(&keys);
-
- mask_to_keylist(ads, masklay, &keys);
-
- draw_keylist(v2d, &keys, ypos, yscale_fac, locked, saction_flag);
-
- BLI_dlrbTree_free(&keys);
+ AnimKeylistDrawListElem *draw_elem = ed_keylist_draw_list_add_elem(
+ draw_list, ANIM_KEYLIST_MASK_LAYER, ypos, yscale_fac, saction_flag);
+ draw_elem->ads = ads;
+ draw_elem->masklay = masklay;
+ draw_elem->channel_locked = locked;
}
diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c
index eb91afa5c84..75ce62d5e27 100644
--- a/source/blender/editors/animation/keyframes_general.c
+++ b/source/blender/editors/animation/keyframes_general.c
@@ -92,10 +92,7 @@ void delete_fcurve_key(FCurve *fcu, int index, bool do_recalc)
fcu->totvert--;
if (fcu->totvert == 0) {
- if (fcu->bezt) {
- MEM_freeN(fcu->bezt);
- }
- fcu->bezt = NULL;
+ MEM_SAFE_FREE(fcu->bezt);
}
/* recalc handles - only if it won't cause problems */
@@ -136,10 +133,7 @@ bool delete_fcurve_keys(FCurve *fcu)
void clear_fcurve_keys(FCurve *fcu)
{
- if (fcu->bezt) {
- MEM_freeN(fcu->bezt);
- }
- fcu->bezt = NULL;
+ MEM_SAFE_FREE(fcu->bezt);
fcu->totvert = 0;
}
diff --git a/source/blender/editors/animation/keyframes_keylist.c b/source/blender/editors/animation/keyframes_keylist.cc
index 47ed2b56300..f6ade11a517 100644
--- a/source/blender/editors/animation/keyframes_keylist.c
+++ b/source/blender/editors/animation/keyframes_keylist.cc
@@ -23,15 +23,16 @@
/* System includes ----------------------------------------------------- */
-#include <float.h>
-#include <math.h>
-#include <stdlib.h>
-#include <string.h>
+#include <cfloat>
+#include <cmath>
+#include <cstdlib>
+#include <cstring>
#include "MEM_guardedalloc.h"
#include "BLI_dlrbTree.h"
#include "BLI_listbase.h"
+#include "BLI_range.h"
#include "BLI_utildefines.h"
#include "DNA_anim_types.h"
@@ -46,16 +47,97 @@
#include "ED_anim_api.h"
#include "ED_keyframes_keylist.h"
+extern "C" {
/* *************************** Keyframe Processing *************************** */
+struct AnimKeylist {
+ DLRBT_Tree keys;
+};
+
+static void ED_keylist_init(AnimKeylist *keylist)
+{
+ BLI_dlrbTree_init(&keylist->keys);
+}
+
+AnimKeylist *ED_keylist_create(void)
+{
+ AnimKeylist *keylist = static_cast<AnimKeylist *>(MEM_callocN(sizeof(AnimKeylist), __func__));
+ ED_keylist_init(keylist);
+ return keylist;
+}
+
+void ED_keylist_free(AnimKeylist *keylist)
+{
+ BLI_assert(keylist);
+ BLI_dlrbTree_free(&keylist->keys);
+ MEM_freeN(keylist);
+}
+
+const ActKeyColumn *ED_keylist_find_exact(const AnimKeylist *keylist, float cfra)
+{
+ return (const ActKeyColumn *)BLI_dlrbTree_search_exact(
+ &keylist->keys, compare_ak_cfraPtr, &cfra);
+}
+
+const ActKeyColumn *ED_keylist_find_next(const AnimKeylist *keylist, float cfra)
+{
+ return (const ActKeyColumn *)BLI_dlrbTree_search_next(&keylist->keys, compare_ak_cfraPtr, &cfra);
+}
+
+const ActKeyColumn *ED_keylist_find_prev(const AnimKeylist *keylist, float cfra)
+{
+ return (const ActKeyColumn *)BLI_dlrbTree_search_prev(&keylist->keys, compare_ak_cfraPtr, &cfra);
+}
+
+/* TODO(jbakker): Should we change this to use `ED_keylist_find_next(keys, min_fra)` and only check
+ * boundary of `max_fra`. */
+const ActKeyColumn *ED_keylist_find_any_between(const AnimKeylist *keylist,
+ const Range2f frame_range)
+{
+ for (const ActKeyColumn *ak = static_cast<const ActKeyColumn *>(keylist->keys.root); ak;
+ ak = static_cast<const ActKeyColumn *>((ak->cfra < frame_range.min) ? ak->right :
+ ak->left)) {
+ if (range2f_in_range(&frame_range, ak->cfra)) {
+ return ak;
+ }
+ }
+ return nullptr;
+}
+
+bool ED_keylist_is_empty(const struct AnimKeylist *keylist)
+{
+ return keylist->keys.root == nullptr;
+}
+
+const struct ListBase *ED_keylist_listbase(const AnimKeylist *keylist)
+{
+ return (ListBase *)&keylist->keys;
+}
+
+bool ED_keylist_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_range)
+{
+ BLI_assert(r_frame_range);
+
+ if (ED_keylist_is_empty(keylist)) {
+ return false;
+ }
+
+ const ActKeyColumn *first_column = (const ActKeyColumn *)keylist->keys.first;
+ r_frame_range->min = first_column->cfra;
+
+ const ActKeyColumn *last_column = (const ActKeyColumn *)keylist->keys.last;
+ r_frame_range->max = last_column->cfra;
+
+ return true;
+}
/* ActKeyColumns (Keyframe Columns) ------------------------------------------ */
-BLI_INLINE bool is_cfra_eq(float a, float b)
+BLI_INLINE bool is_cfra_eq(const float a, const float b)
{
return IS_EQT(a, b, BEZT_BINARYSEARCH_THRESH);
}
-BLI_INLINE bool is_cfra_lt(float a, float b)
+BLI_INLINE bool is_cfra_lt(const float a, const float b)
{
return (b - a) > BEZT_BINARYSEARCH_THRESH;
}
@@ -65,8 +147,8 @@ BLI_INLINE bool is_cfra_lt(float a, float b)
short compare_ak_cfraPtr(void *node, void *data)
{
ActKeyColumn *ak = (ActKeyColumn *)node;
- const float *cframe = data;
- float val = *cframe;
+ const float *cframe = static_cast<const float *>(data);
+ const float val = *cframe;
if (is_cfra_eq(val, ak->cfra)) {
return 0;
@@ -81,16 +163,16 @@ short compare_ak_cfraPtr(void *node, void *data)
/* --------------- */
/* Set of references to three logically adjacent keys. */
-typedef struct BezTripleChain {
+struct BezTripleChain {
/* Current keyframe. */
BezTriple *cur;
- /* Logical neighbors. May be NULL. */
+ /* Logical neighbors. May be nullptr. */
BezTriple *prev, *next;
-} BezTripleChain;
+};
/* Categorize the interpolation & handle type of the keyframe. */
-static eKeyframeHandleDrawOpts bezt_handle_type(BezTriple *bezt)
+static eKeyframeHandleDrawOpts bezt_handle_type(const BezTriple *bezt)
{
if (bezt->h1 == HD_AUTO_ANIM && bezt->h2 == HD_AUTO_ANIM) {
return KEYFRAME_HANDLE_AUTO_CLAMP;
@@ -110,14 +192,14 @@ static eKeyframeHandleDrawOpts bezt_handle_type(BezTriple *bezt)
/* Determine if the keyframe is an extreme by comparing with neighbors.
* Ends of fixed-value sections and of the whole curve are also marked.
*/
-static eKeyframeExtremeDrawOpts bezt_extreme_type(BezTripleChain *chain)
+static eKeyframeExtremeDrawOpts bezt_extreme_type(const BezTripleChain *chain)
{
- if (chain->prev == NULL && chain->next == NULL) {
+ if (chain->prev == nullptr && chain->next == nullptr) {
return KEYFRAME_EXTREME_NONE;
}
/* Keyframe values for the current one and neighbors. */
- float cur_y = chain->cur->vec[1][1];
+ const float cur_y = chain->cur->vec[1][1];
float prev_y = cur_y, next_y = cur_y;
if (chain->prev && !IS_EQF(cur_y, chain->prev->vec[1][1])) {
@@ -138,22 +220,24 @@ static eKeyframeExtremeDrawOpts bezt_extreme_type(BezTripleChain *chain)
}
/* Bezier handle values for the overshoot check. */
- bool l_bezier = chain->prev && chain->prev->ipo == BEZT_IPO_BEZ;
- bool r_bezier = chain->next && chain->cur->ipo == BEZT_IPO_BEZ;
- float handle_l = l_bezier ? chain->cur->vec[0][1] : cur_y;
- float handle_r = r_bezier ? chain->cur->vec[2][1] : cur_y;
+ const bool l_bezier = chain->prev && chain->prev->ipo == BEZT_IPO_BEZ;
+ const bool r_bezier = chain->next && chain->cur->ipo == BEZT_IPO_BEZ;
+ const float handle_l = l_bezier ? chain->cur->vec[0][1] : cur_y;
+ const float handle_r = r_bezier ? chain->cur->vec[2][1] : cur_y;
/* Detect extremes. One of the neighbors is allowed to be equal to current. */
if (prev_y < cur_y || next_y < cur_y) {
- bool is_overshoot = (handle_l > cur_y || handle_r > cur_y);
+ const bool is_overshoot = (handle_l > cur_y || handle_r > cur_y);
- return KEYFRAME_EXTREME_MAX | (is_overshoot ? KEYFRAME_EXTREME_MIXED : 0);
+ return static_cast<eKeyframeExtremeDrawOpts>(KEYFRAME_EXTREME_MAX |
+ (is_overshoot ? KEYFRAME_EXTREME_MIXED : 0));
}
if (prev_y > cur_y || next_y > cur_y) {
- bool is_overshoot = (handle_l < cur_y || handle_r < cur_y);
+ const bool is_overshoot = (handle_l < cur_y || handle_r < cur_y);
- return KEYFRAME_EXTREME_MIN | (is_overshoot ? KEYFRAME_EXTREME_MIXED : 0);
+ return static_cast<eKeyframeExtremeDrawOpts>(KEYFRAME_EXTREME_MIN |
+ (is_overshoot ? KEYFRAME_EXTREME_MIXED : 0));
}
return KEYFRAME_EXTREME_NONE;
@@ -162,7 +246,7 @@ static eKeyframeExtremeDrawOpts bezt_extreme_type(BezTripleChain *chain)
/* Comparator callback used for ActKeyColumns and BezTripleChain */
static short compare_ak_bezt(void *node, void *data)
{
- BezTripleChain *chain = data;
+ BezTripleChain *chain = static_cast<BezTripleChain *>(data);
return compare_ak_cfraPtr(node, &chain->cur->vec[1][0]);
}
@@ -170,9 +254,10 @@ static short compare_ak_bezt(void *node, void *data)
/* New node callback used for building ActKeyColumns from BezTripleChain */
static DLRBT_Node *nalloc_ak_bezt(void *data)
{
- ActKeyColumn *ak = MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumn");
- BezTripleChain *chain = data;
- BezTriple *bezt = chain->cur;
+ ActKeyColumn *ak = static_cast<ActKeyColumn *>(
+ MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumn"));
+ const BezTripleChain *chain = static_cast<const BezTripleChain *>(data);
+ const BezTriple *bezt = chain->cur;
/* store settings based on state of BezTriple */
ak->cfra = bezt->vec[1][0];
@@ -190,9 +275,9 @@ static DLRBT_Node *nalloc_ak_bezt(void *data)
/* Node updater callback used for building ActKeyColumns from BezTripleChain */
static void nupdate_ak_bezt(void *node, void *data)
{
- ActKeyColumn *ak = node;
- BezTripleChain *chain = data;
- BezTriple *bezt = chain->cur;
+ ActKeyColumn *ak = static_cast<ActKeyColumn *>(node);
+ const BezTripleChain *chain = static_cast<const BezTripleChain *>(data);
+ const BezTriple *bezt = chain->cur;
/* set selection status and 'touched' status */
if (BEZT_ISSEL_ANY(bezt)) {
@@ -209,10 +294,10 @@ static void nupdate_ak_bezt(void *node, void *data)
}
/* For interpolation type, select the highest value (enum is sorted). */
- ak->handle_type = MAX2(ak->handle_type, bezt_handle_type(bezt));
+ ak->handle_type = MAX2((eKeyframeHandleDrawOpts)ak->handle_type, bezt_handle_type(bezt));
/* For extremes, detect when combining different states. */
- char new_extreme = bezt_extreme_type(chain);
+ const char new_extreme = bezt_extreme_type(chain);
if (new_extreme != ak->extreme_type) {
/* Replace the flat status without adding mixed. */
@@ -230,7 +315,7 @@ static void nupdate_ak_bezt(void *node, void *data)
/* Comparator callback used for ActKeyColumns and GPencil frame */
static short compare_ak_gpframe(void *node, void *data)
{
- bGPDframe *gpf = (bGPDframe *)data;
+ const bGPDframe *gpf = (bGPDframe *)data;
float frame = gpf->framenum;
return compare_ak_cfraPtr(node, &frame);
@@ -239,8 +324,9 @@ static short compare_ak_gpframe(void *node, void *data)
/* New node callback used for building ActKeyColumns from GPencil frames */
static DLRBT_Node *nalloc_ak_gpframe(void *data)
{
- ActKeyColumn *ak = MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumnGPF");
- bGPDframe *gpf = (bGPDframe *)data;
+ ActKeyColumn *ak = static_cast<ActKeyColumn *>(
+ MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumnGPF"));
+ const bGPDframe *gpf = (bGPDframe *)data;
/* store settings based on state of BezTriple */
ak->cfra = gpf->framenum;
@@ -261,7 +347,7 @@ static DLRBT_Node *nalloc_ak_gpframe(void *data)
static void nupdate_ak_gpframe(void *node, void *data)
{
ActKeyColumn *ak = (ActKeyColumn *)node;
- bGPDframe *gpf = (bGPDframe *)data;
+ const bGPDframe *gpf = (bGPDframe *)data;
/* set selection status and 'touched' status */
if (gpf->flag & GP_FRAME_SELECT) {
@@ -283,7 +369,7 @@ static void nupdate_ak_gpframe(void *node, void *data)
/* Comparator callback used for ActKeyColumns and GPencil frame */
static short compare_ak_masklayshape(void *node, void *data)
{
- MaskLayerShape *masklay_shape = (MaskLayerShape *)data;
+ const MaskLayerShape *masklay_shape = (const MaskLayerShape *)data;
float frame = masklay_shape->frame;
return compare_ak_cfraPtr(node, &frame);
@@ -292,8 +378,9 @@ static short compare_ak_masklayshape(void *node, void *data)
/* New node callback used for building ActKeyColumns from GPencil frames */
static DLRBT_Node *nalloc_ak_masklayshape(void *data)
{
- ActKeyColumn *ak = MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumnGPF");
- MaskLayerShape *masklay_shape = (MaskLayerShape *)data;
+ ActKeyColumn *ak = static_cast<ActKeyColumn *>(
+ MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumnGPF"));
+ const MaskLayerShape *masklay_shape = (const MaskLayerShape *)data;
/* store settings based on state of BezTriple */
ak->cfra = masklay_shape->frame;
@@ -309,7 +396,7 @@ static DLRBT_Node *nalloc_ak_masklayshape(void *data)
static void nupdate_ak_masklayshape(void *node, void *data)
{
ActKeyColumn *ak = (ActKeyColumn *)node;
- MaskLayerShape *masklay_shape = (MaskLayerShape *)data;
+ const MaskLayerShape *masklay_shape = (const MaskLayerShape *)data;
/* set selection status and 'touched' status */
if (masklay_shape->flag & MASK_SHAPE_SELECT) {
@@ -323,33 +410,33 @@ static void nupdate_ak_masklayshape(void *node, void *data)
/* --------------- */
/* Add the given BezTriple to the given 'list' of Keyframes */
-static void add_bezt_to_keycolumns_list(DLRBT_Tree *keys, BezTripleChain *bezt)
+static void add_bezt_to_keycolumns_list(AnimKeylist *keylist, BezTripleChain *bezt)
{
- if (ELEM(NULL, keys, bezt)) {
+ if (ELEM(nullptr, keylist, bezt)) {
return;
}
- BLI_dlrbTree_add(keys, compare_ak_bezt, nalloc_ak_bezt, nupdate_ak_bezt, bezt);
+ BLI_dlrbTree_add(&keylist->keys, compare_ak_bezt, nalloc_ak_bezt, nupdate_ak_bezt, bezt);
}
/* Add the given GPencil Frame to the given 'list' of Keyframes */
-static void add_gpframe_to_keycolumns_list(DLRBT_Tree *keys, bGPDframe *gpf)
+static void add_gpframe_to_keycolumns_list(AnimKeylist *keylist, bGPDframe *gpf)
{
- if (ELEM(NULL, keys, gpf)) {
+ if (ELEM(nullptr, keylist, gpf)) {
return;
}
- BLI_dlrbTree_add(keys, compare_ak_gpframe, nalloc_ak_gpframe, nupdate_ak_gpframe, gpf);
+ BLI_dlrbTree_add(&keylist->keys, compare_ak_gpframe, nalloc_ak_gpframe, nupdate_ak_gpframe, gpf);
}
/* Add the given MaskLayerShape Frame to the given 'list' of Keyframes */
-static void add_masklay_to_keycolumns_list(DLRBT_Tree *keys, MaskLayerShape *masklay_shape)
+static void add_masklay_to_keycolumns_list(AnimKeylist *keylist, MaskLayerShape *masklay_shape)
{
- if (ELEM(NULL, keys, masklay_shape)) {
+ if (ELEM(nullptr, keylist, masklay_shape)) {
return;
}
- BLI_dlrbTree_add(keys,
+ BLI_dlrbTree_add(&keylist->keys,
compare_ak_masklayshape,
nalloc_ak_masklayshape,
nupdate_ak_masklayshape,
@@ -360,7 +447,9 @@ static void add_masklay_to_keycolumns_list(DLRBT_Tree *keys, MaskLayerShape *mas
static const ActKeyBlockInfo dummy_keyblock = {0};
-static void compute_keyblock_data(ActKeyBlockInfo *info, BezTriple *prev, BezTriple *beztn)
+static void compute_keyblock_data(ActKeyBlockInfo *info,
+ const BezTriple *prev,
+ const BezTriple *beztn)
{
memset(info, 0, sizeof(ActKeyBlockInfo));
@@ -423,34 +512,34 @@ static void add_keyblock_info(ActKeyColumn *col, const ActKeyBlockInfo *block)
}
}
-static void add_bezt_to_keyblocks_list(DLRBT_Tree *keys, BezTriple *bezt, int bezt_len)
+static void add_bezt_to_keyblocks_list(AnimKeylist *keylist, BezTriple *bezt, const int bezt_len)
{
- ActKeyColumn *col = keys->first;
+ ActKeyColumn *col = static_cast<ActKeyColumn *>(keylist->keys.first);
if (bezt && bezt_len >= 2) {
ActKeyBlockInfo block;
/* Find the first key column while inserting dummy blocks. */
- for (; col != NULL && is_cfra_lt(col->cfra, bezt[0].vec[1][0]); col = col->next) {
+ for (; col != nullptr && is_cfra_lt(col->cfra, bezt[0].vec[1][0]); col = col->next) {
add_keyblock_info(col, &dummy_keyblock);
}
- BLI_assert(col != NULL);
+ BLI_assert(col != nullptr);
/* Insert real blocks. */
- for (int v = 1; col != NULL && v < bezt_len; v++, bezt++) {
+ for (int v = 1; col != nullptr && v < bezt_len; v++, bezt++) {
/* Wrong order of bezier keys: resync position. */
if (is_cfra_lt(bezt[1].vec[1][0], bezt[0].vec[1][0])) {
/* Backtrack to find the right location. */
if (is_cfra_lt(bezt[1].vec[1][0], col->cfra)) {
ActKeyColumn *newcol = (ActKeyColumn *)BLI_dlrbTree_search_exact(
- keys, compare_ak_cfraPtr, &bezt[1].vec[1][0]);
+ &keylist->keys, compare_ak_cfraPtr, &bezt[1].vec[1][0]);
- if (newcol != NULL) {
+ if (newcol != nullptr) {
col = newcol;
/* The previous keyblock is garbage too. */
- if (col->prev != NULL) {
+ if (col->prev != nullptr) {
add_keyblock_info(col->prev, &dummy_keyblock);
}
}
@@ -467,16 +556,16 @@ static void add_bezt_to_keyblocks_list(DLRBT_Tree *keys, BezTriple *bezt, int be
compute_keyblock_data(&block, bezt, bezt + 1);
- for (; col != NULL && is_cfra_lt(col->cfra, bezt[1].vec[1][0]); col = col->next) {
+ for (; col != nullptr && is_cfra_lt(col->cfra, bezt[1].vec[1][0]); col = col->next) {
add_keyblock_info(col, &block);
}
- BLI_assert(col != NULL);
+ BLI_assert(col != nullptr);
}
}
/* Insert dummy blocks at the end. */
- for (; col != NULL; col = col->next) {
+ for (; col != nullptr; col = col->next) {
add_keyblock_info(col, &dummy_keyblock);
}
}
@@ -486,28 +575,28 @@ static void add_bezt_to_keyblocks_list(DLRBT_Tree *keys, BezTriple *bezt, int be
* This must be called even by animation sources that don't generate
* keyblocks to keep the data structure consistent after adding columns.
*/
-static void update_keyblocks(DLRBT_Tree *keys, BezTriple *bezt, int bezt_len)
+static void update_keyblocks(AnimKeylist *keylist, BezTriple *bezt, const int bezt_len)
{
/* Recompute the prev/next linked list. */
- BLI_dlrbTree_linkedlist_sync(keys);
+ BLI_dlrbTree_linkedlist_sync(&keylist->keys);
/* Find the curve count */
int max_curve = 0;
- LISTBASE_FOREACH (ActKeyColumn *, col, keys) {
+ LISTBASE_FOREACH (ActKeyColumn *, col, &keylist->keys) {
max_curve = MAX2(max_curve, col->totcurve);
}
/* Propagate blocks to inserted keys */
- ActKeyColumn *prev_ready = NULL;
+ ActKeyColumn *prev_ready = nullptr;
- LISTBASE_FOREACH (ActKeyColumn *, col, keys) {
+ LISTBASE_FOREACH (ActKeyColumn *, col, &keylist->keys) {
/* Pre-existing column. */
if (col->totcurve > 0) {
prev_ready = col;
}
/* Newly inserted column, so copy block data from previous. */
- else if (prev_ready != NULL) {
+ else if (prev_ready != nullptr) {
col->totblock = prev_ready->totblock;
memcpy(&col->block, &prev_ready->block, sizeof(ActKeyBlockInfo));
}
@@ -516,18 +605,18 @@ static void update_keyblocks(DLRBT_Tree *keys, BezTriple *bezt, int bezt_len)
}
/* Add blocks on top */
- add_bezt_to_keyblocks_list(keys, bezt, bezt_len);
+ add_bezt_to_keyblocks_list(keylist, bezt, bezt_len);
}
/* --------- */
-bool actkeyblock_is_valid(ActKeyColumn *ac)
+bool actkeyblock_is_valid(const ActKeyColumn *ac)
{
- return ac != NULL && ac->next != NULL && ac->totblock > 0;
+ return ac != nullptr && ac->next != nullptr && ac->totblock > 0;
}
/* Checks if ActKeyBlock should exist... */
-int actkeyblock_get_valid_hold(ActKeyColumn *ac)
+int actkeyblock_get_valid_hold(const ActKeyColumn *ac)
{
/* check that block is valid */
if (!actkeyblock_is_valid(ac)) {
@@ -540,34 +629,32 @@ int actkeyblock_get_valid_hold(ActKeyColumn *ac)
/* *************************** Keyframe List Conversions *************************** */
-void summary_to_keylist(bAnimContext *ac, DLRBT_Tree *keys, int saction_flag)
+void summary_to_keylist(bAnimContext *ac, AnimKeylist *keylist, const int saction_flag)
{
if (ac) {
- ListBase anim_data = {NULL, NULL};
- bAnimListElem *ale;
- int filter;
+ ListBase anim_data = {nullptr, nullptr};
/* get F-Curves to take keyframes from */
- filter = ANIMFILTER_DATA_VISIBLE;
- ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
+ const eAnimFilter_Flags filter = ANIMFILTER_DATA_VISIBLE;
+ ANIM_animdata_filter(
+ ac, &anim_data, filter, ac->data, static_cast<eAnimCont_Types>(ac->datatype));
/* loop through each F-Curve, grabbing the keyframes */
- for (ale = anim_data.first; ale; ale = ale->next) {
+ LISTBASE_FOREACH (const bAnimListElem *, ale, &anim_data) {
/* Why not use all #eAnim_KeyType here?
* All of the other key types are actually "summaries" themselves,
* and will just end up duplicating stuff that comes up through
* standard filtering of just F-Curves. Given the way that these work,
* there isn't really any benefit at all from including them. - Aligorith */
-
switch (ale->datatype) {
case ALE_FCURVE:
- fcurve_to_keylist(ale->adt, ale->data, keys, saction_flag);
+ fcurve_to_keylist(ale->adt, static_cast<FCurve *>(ale->data), keylist, saction_flag);
break;
case ALE_MASKLAY:
- mask_to_keylist(ac->ads, ale->data, keys);
+ mask_to_keylist(ac->ads, static_cast<MaskLayer *>(ale->data), keylist);
break;
case ALE_GPFRAME:
- gpl_to_keylist(ac->ads, ale->data, keys);
+ gpl_to_keylist(ac->ads, static_cast<bGPDlayer *>(ale->data), keylist);
break;
default:
// printf("%s: datatype %d unhandled\n", __func__, ale->datatype);
@@ -579,16 +666,14 @@ void summary_to_keylist(bAnimContext *ac, DLRBT_Tree *keys, int saction_flag)
}
}
-void scene_to_keylist(bDopeSheet *ads, Scene *sce, DLRBT_Tree *keys, int saction_flag)
+void scene_to_keylist(bDopeSheet *ads, Scene *sce, AnimKeylist *keylist, const int saction_flag)
{
- bAnimContext ac = {NULL};
- ListBase anim_data = {NULL, NULL};
- bAnimListElem *ale;
- int filter;
+ bAnimContext ac = {nullptr};
+ ListBase anim_data = {nullptr, nullptr};
- bAnimListElem dummychan = {NULL};
+ bAnimListElem dummychan = {nullptr};
- if (sce == NULL) {
+ if (sce == nullptr) {
return;
}
@@ -603,28 +688,27 @@ void scene_to_keylist(bDopeSheet *ads, Scene *sce, DLRBT_Tree *keys, int saction
ac.datatype = ANIMCONT_CHANNEL;
/* get F-Curves to take keyframes from */
- filter = ANIMFILTER_DATA_VISIBLE; /* curves only */
- ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
+ const eAnimFilter_Flags filter = ANIMFILTER_DATA_VISIBLE; /* curves only */
+ ANIM_animdata_filter(
+ &ac, &anim_data, filter, ac.data, static_cast<eAnimCont_Types>(ac.datatype));
/* loop through each F-Curve, grabbing the keyframes */
- for (ale = anim_data.first; ale; ale = ale->next) {
- fcurve_to_keylist(ale->adt, ale->data, keys, saction_flag);
+ LISTBASE_FOREACH (const bAnimListElem *, ale, &anim_data) {
+ fcurve_to_keylist(ale->adt, static_cast<FCurve *>(ale->data), keylist, saction_flag);
}
ANIM_animdata_freelist(&anim_data);
}
-void ob_to_keylist(bDopeSheet *ads, Object *ob, DLRBT_Tree *keys, int saction_flag)
+void ob_to_keylist(bDopeSheet *ads, Object *ob, AnimKeylist *keylist, const int saction_flag)
{
- bAnimContext ac = {NULL};
- ListBase anim_data = {NULL, NULL};
- bAnimListElem *ale;
- int filter;
+ bAnimContext ac = {nullptr};
+ ListBase anim_data = {nullptr, nullptr};
- bAnimListElem dummychan = {NULL};
- Base dummybase = {NULL};
+ bAnimListElem dummychan = {nullptr};
+ Base dummybase = {nullptr};
- if (ob == NULL) {
+ if (ob == nullptr) {
return;
}
@@ -641,12 +725,13 @@ void ob_to_keylist(bDopeSheet *ads, Object *ob, DLRBT_Tree *keys, int saction_fl
ac.datatype = ANIMCONT_CHANNEL;
/* get F-Curves to take keyframes from */
- filter = ANIMFILTER_DATA_VISIBLE; /* curves only */
- ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
+ const eAnimFilter_Flags filter = ANIMFILTER_DATA_VISIBLE; /* curves only */
+ ANIM_animdata_filter(
+ &ac, &anim_data, filter, ac.data, static_cast<eAnimCont_Types>(ac.datatype));
/* loop through each F-Curve, grabbing the keyframes */
- for (ale = anim_data.first; ale; ale = ale->next) {
- fcurve_to_keylist(ale->adt, ale->data, keys, saction_flag);
+ LISTBASE_FOREACH (const bAnimListElem *, ale, &anim_data) {
+ fcurve_to_keylist(ale->adt, static_cast<FCurve *>(ale->data), keylist, saction_flag);
}
ANIM_animdata_freelist(&anim_data);
@@ -654,44 +739,45 @@ void ob_to_keylist(bDopeSheet *ads, Object *ob, DLRBT_Tree *keys, int saction_fl
void cachefile_to_keylist(bDopeSheet *ads,
CacheFile *cache_file,
- DLRBT_Tree *keys,
- int saction_flag)
+ AnimKeylist *keylist,
+ const int saction_flag)
{
- if (cache_file == NULL) {
+ if (cache_file == nullptr) {
return;
}
/* create a dummy wrapper data to work with */
- bAnimListElem dummychan = {NULL};
+ bAnimListElem dummychan = {nullptr};
dummychan.type = ANIMTYPE_DSCACHEFILE;
dummychan.data = cache_file;
dummychan.id = &cache_file->id;
dummychan.adt = cache_file->adt;
- bAnimContext ac = {NULL};
+ bAnimContext ac = {nullptr};
ac.ads = ads;
ac.data = &dummychan;
ac.datatype = ANIMCONT_CHANNEL;
/* get F-Curves to take keyframes from */
- ListBase anim_data = {NULL, NULL};
- int filter = ANIMFILTER_DATA_VISIBLE; /* curves only */
- ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
+ ListBase anim_data = {nullptr, nullptr};
+ const eAnimFilter_Flags filter = ANIMFILTER_DATA_VISIBLE; /* curves only */
+ ANIM_animdata_filter(
+ &ac, &anim_data, filter, ac.data, static_cast<eAnimCont_Types>(ac.datatype));
/* loop through each F-Curve, grabbing the keyframes */
- LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
- fcurve_to_keylist(ale->adt, ale->data, keys, saction_flag);
+ LISTBASE_FOREACH (const bAnimListElem *, ale, &anim_data) {
+ fcurve_to_keylist(ale->adt, static_cast<FCurve *>(ale->data), keylist, saction_flag);
}
ANIM_animdata_freelist(&anim_data);
}
-void fcurve_to_keylist(AnimData *adt, FCurve *fcu, DLRBT_Tree *keys, int saction_flag)
+void fcurve_to_keylist(AnimData *adt, FCurve *fcu, AnimKeylist *keylist, const int saction_flag)
{
if (fcu && fcu->totvert && fcu->bezt) {
/* apply NLA-mapping (if applicable) */
if (adt) {
- ANIM_nla_mapping_apply_fcurve(adt, fcu, 0, 0);
+ ANIM_nla_mapping_apply_fcurve(adt, fcu, false, false);
}
/* Check if the curve is cyclic. */
@@ -699,95 +785,95 @@ void fcurve_to_keylist(AnimData *adt, FCurve *fcu, DLRBT_Tree *keys, int saction
bool do_extremes = (saction_flag & SACTION_SHOW_EXTREMES) != 0;
/* loop through beztriples, making ActKeysColumns */
- BezTripleChain chain = {0};
+ BezTripleChain chain = {nullptr};
for (int v = 0; v < fcu->totvert; v++) {
chain.cur = &fcu->bezt[v];
/* Neighbor keys, accounting for being cyclic. */
if (do_extremes) {
- chain.prev = (v > 0) ? &fcu->bezt[v - 1] : is_cyclic ? &fcu->bezt[fcu->totvert - 2] : NULL;
- chain.next = (v + 1 < fcu->totvert) ? &fcu->bezt[v + 1] : is_cyclic ? &fcu->bezt[1] : NULL;
+ chain.prev = (v > 0) ? &fcu->bezt[v - 1] :
+ is_cyclic ? &fcu->bezt[fcu->totvert - 2] :
+ nullptr;
+ chain.next = (v + 1 < fcu->totvert) ? &fcu->bezt[v + 1] :
+ is_cyclic ? &fcu->bezt[1] :
+ nullptr;
}
- add_bezt_to_keycolumns_list(keys, &chain);
+ add_bezt_to_keycolumns_list(keylist, &chain);
}
/* Update keyblocks. */
- update_keyblocks(keys, fcu->bezt, fcu->totvert);
+ update_keyblocks(keylist, fcu->bezt, fcu->totvert);
/* unapply NLA-mapping if applicable */
if (adt) {
- ANIM_nla_mapping_apply_fcurve(adt, fcu, 1, 0);
+ ANIM_nla_mapping_apply_fcurve(adt, fcu, true, false);
}
}
}
-void agroup_to_keylist(AnimData *adt, bActionGroup *agrp, DLRBT_Tree *keys, int saction_flag)
+void agroup_to_keylist(AnimData *adt,
+ bActionGroup *agrp,
+ AnimKeylist *keylist,
+ const int saction_flag)
{
- FCurve *fcu;
-
if (agrp) {
/* loop through F-Curves */
- for (fcu = agrp->channels.first; fcu && fcu->grp == agrp; fcu = fcu->next) {
- fcurve_to_keylist(adt, fcu, keys, saction_flag);
+ LISTBASE_FOREACH (FCurve *, fcu, &agrp->channels) {
+ if (fcu->grp != agrp) {
+ break;
+ }
+ fcurve_to_keylist(adt, fcu, keylist, saction_flag);
}
}
}
-void action_to_keylist(AnimData *adt, bAction *act, DLRBT_Tree *keys, int saction_flag)
+void action_to_keylist(AnimData *adt, bAction *act, AnimKeylist *keylist, const int saction_flag)
{
- FCurve *fcu;
-
if (act) {
/* loop through F-Curves */
- for (fcu = act->curves.first; fcu; fcu = fcu->next) {
- fcurve_to_keylist(adt, fcu, keys, saction_flag);
+ LISTBASE_FOREACH (FCurve *, fcu, &act->curves) {
+ fcurve_to_keylist(adt, fcu, keylist, saction_flag);
}
}
}
-void gpencil_to_keylist(bDopeSheet *ads, bGPdata *gpd, DLRBT_Tree *keys, const bool active)
+void gpencil_to_keylist(bDopeSheet *ads, bGPdata *gpd, AnimKeylist *keylist, const bool active)
{
- bGPDlayer *gpl;
-
- if (gpd && keys) {
+ if (gpd && keylist) {
/* for now, just aggregate out all the frames, but only for visible layers */
- for (gpl = gpd->layers.last; gpl; gpl = gpl->prev) {
+ LISTBASE_FOREACH_BACKWARD (bGPDlayer *, gpl, &gpd->layers) {
if ((gpl->flag & GP_LAYER_HIDE) == 0) {
if ((!active) || ((active) && (gpl->flag & GP_LAYER_SELECT))) {
- gpl_to_keylist(ads, gpl, keys);
+ gpl_to_keylist(ads, gpl, keylist);
}
}
}
}
}
-void gpl_to_keylist(bDopeSheet *UNUSED(ads), bGPDlayer *gpl, DLRBT_Tree *keys)
+void gpl_to_keylist(bDopeSheet *UNUSED(ads), bGPDlayer *gpl, AnimKeylist *keylist)
{
- bGPDframe *gpf;
-
- if (gpl && keys) {
+ if (gpl && keylist) {
/* Although the frames should already be in an ordered list,
* they are not suitable for displaying yet. */
- for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
- add_gpframe_to_keycolumns_list(keys, gpf);
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ add_gpframe_to_keycolumns_list(keylist, gpf);
}
- update_keyblocks(keys, NULL, 0);
+ update_keyblocks(keylist, nullptr, 0);
}
}
-void mask_to_keylist(bDopeSheet *UNUSED(ads), MaskLayer *masklay, DLRBT_Tree *keys)
+void mask_to_keylist(bDopeSheet *UNUSED(ads), MaskLayer *masklay, AnimKeylist *keylist)
{
- MaskLayerShape *masklay_shape;
-
- if (masklay && keys) {
- for (masklay_shape = masklay->splines_shapes.first; masklay_shape;
- masklay_shape = masklay_shape->next) {
- add_masklay_to_keycolumns_list(keys, masklay_shape);
+ if (masklay && keylist) {
+ LISTBASE_FOREACH (MaskLayerShape *, masklay_shape, &masklay->splines_shapes) {
+ add_masklay_to_keycolumns_list(keylist, masklay_shape);
}
- update_keyblocks(keys, NULL, 0);
+ update_keyblocks(keylist, nullptr, 0);
}
}
+}
diff --git a/source/blender/editors/animation/time_scrub_ui.c b/source/blender/editors/animation/time_scrub_ui.c
index 6af033f3cf2..8aeb6a57124 100644
--- a/source/blender/editors/animation/time_scrub_ui.c
+++ b/source/blender/editors/animation/time_scrub_ui.c
@@ -244,6 +244,10 @@ void ED_time_scrub_channel_search_draw(const bContext *C, ARegion *region, bDope
UI_block_align_end(block);
UI_block_layout_resolve(block, NULL, NULL);
+ /* Make sure the events are consumed from the search and don't reach other UI blocks since this
+ * is drawn on top of animation-channels. */
+ UI_block_flag_enable(block, UI_BLOCK_CLIP_EVENTS);
+ UI_block_bounds_set_normal(block, 0);
UI_block_end(C, block);
UI_block_draw(C, block);
diff --git a/source/blender/editors/armature/meshlaplacian.c b/source/blender/editors/armature/meshlaplacian.c
index e362a2e2f40..990e7589d9d 100644
--- a/source/blender/editors/armature/meshlaplacian.c
+++ b/source/blender/editors/armature/meshlaplacian.c
@@ -307,10 +307,7 @@ static void laplacian_system_construct_end(LaplacianSystem *sys)
MEM_freeN(sys->faces);
sys->faces = NULL;
- if (sys->varea) {
- MEM_freeN(sys->varea);
- sys->varea = NULL;
- }
+ MEM_SAFE_FREE(sys->varea);
BLI_edgehash_free(sys->edgehash, NULL);
sys->edgehash = NULL;
diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c
index cb70b2810d1..646356e7a45 100644
--- a/source/blender/editors/armature/pose_lib.c
+++ b/source/blender/editors/armature/pose_lib.c
@@ -304,8 +304,6 @@ static int poselib_sanitize_exec(bContext *C, wmOperator *op)
{
Object *ob = get_poselib_object(C);
bAction *act = (ob) ? ob->poselib : NULL;
- DLRBT_Tree keys;
- ActKeyColumn *ak;
TimeMarker *marker, *markern;
/* validate action */
@@ -315,11 +313,11 @@ static int poselib_sanitize_exec(bContext *C, wmOperator *op)
}
/* determine which frames have keys */
- BLI_dlrbTree_init(&keys);
- action_to_keylist(NULL, act, &keys, 0);
+ struct AnimKeylist *keylist = ED_keylist_create();
+ action_to_keylist(NULL, act, keylist, 0);
/* for each key, make sure there is a corresponding pose */
- for (ak = keys.first; ak; ak = ak->next) {
+ LISTBASE_FOREACH (const ActKeyColumn *, ak, ED_keylist_listbase(keylist)) {
/* check if any pose matches this */
/* TODO: don't go looking through the list like this every time... */
for (marker = act->markers.first; marker; marker = marker->next) {
@@ -356,7 +354,7 @@ static int poselib_sanitize_exec(bContext *C, wmOperator *op)
}
/* free temp memory */
- BLI_dlrbTree_free(&keys);
+ ED_keylist_free(keylist);
/* send notifiers for this - using keyframe editing notifiers, since action
* may be being shown in anim editors as active action
diff --git a/source/blender/editors/armature/pose_lib_2.c b/source/blender/editors/armature/pose_lib_2.c
index 32440f941ba..91a5dc67a21 100644
--- a/source/blender/editors/armature/pose_lib_2.c
+++ b/source/blender/editors/armature/pose_lib_2.c
@@ -299,16 +299,16 @@ static void poselib_tempload_exit(PoseBlendData *pbd)
static bAction *poselib_blend_init_get_action(bContext *C, wmOperator *op)
{
bool asset_handle_valid;
- const AssetLibraryReference *asset_library = CTX_wm_asset_library(C);
+ const AssetLibraryReference *asset_library_ref = CTX_wm_asset_library_ref(C);
const AssetHandle asset_handle = CTX_wm_asset_handle(C, &asset_handle_valid);
/* Poll callback should check. */
- BLI_assert((asset_library != NULL) && asset_handle_valid);
+ BLI_assert((asset_library_ref != NULL) && asset_handle_valid);
PoseBlendData *pbd = op->customdata;
pbd->temp_id_consumer = ED_asset_temp_id_consumer_create(&asset_handle);
return (bAction *)ED_asset_temp_id_consumer_ensure_local_id(
- pbd->temp_id_consumer, C, asset_library, ID_AC, CTX_data_main(C), op->reports);
+ pbd->temp_id_consumer, C, asset_library_ref, ID_AC, CTX_data_main(C), op->reports);
}
static bAction *flip_pose(bContext *C, Object *ob, bAction *action)
@@ -423,7 +423,7 @@ static void poselib_blend_cleanup(bContext *C, wmOperator *op)
case POSE_BLEND_ORIGINAL:
/* Cleanup should not be called directly from these states. */
BLI_assert_msg(0, "poselib_blend_cleanup: unexpected pose blend state");
- BKE_report(op->reports, RPT_ERROR, "Internal pose library error, cancelling operator");
+ BKE_report(op->reports, RPT_ERROR, "Internal pose library error, canceling operator");
ATTR_FALLTHROUGH;
case POSE_BLEND_CANCEL:
ED_pose_backup_restore(pbd->pose_backup);
@@ -540,10 +540,10 @@ static bool poselib_asset_in_context(bContext *C)
{
bool asset_handle_valid;
/* Check whether the context provides the asset data needed to add a pose. */
- const AssetLibraryReference *asset_library = CTX_wm_asset_library(C);
+ const AssetLibraryReference *asset_library_ref = CTX_wm_asset_library_ref(C);
AssetHandle asset_handle = CTX_wm_asset_handle(C, &asset_handle_valid);
- return (asset_library != NULL) && asset_handle_valid &&
+ return (asset_library_ref != NULL) && asset_handle_valid &&
(ED_asset_handle_get_id_type(&asset_handle) == ID_AC);
}
diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c
index 38f562ebf25..bc5cbd92deb 100644
--- a/source/blender/editors/armature/pose_slide.c
+++ b/source/blender/editors/armature/pose_slide.c
@@ -146,7 +146,7 @@ typedef struct tPoseSlideOp {
/** links between posechannels and f-curves for all the pose objects. */
ListBase pfLinks;
/** binary tree for quicker searching for keyframes (when applicable) */
- DLRBT_Tree keys;
+ struct AnimKeylist *keylist;
/** current frame number - global time */
int cframe;
@@ -277,7 +277,7 @@ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode)
/* Do basic initialize of RB-BST used for finding keyframes, but leave the filling of it up
* to the caller of this (usually only invoke() will do it, to make things more efficient). */
- BLI_dlrbTree_init(&pso->keys);
+ pso->keylist = ED_keylist_create();
/* Initialize numeric input. */
initNumInput(&pso->num);
@@ -306,7 +306,7 @@ static void pose_slide_exit(bContext *C, wmOperator *op)
poseAnim_mapping_free(&pso->pfLinks);
/* Free RB-BST for keyframes (if it contained data). */
- BLI_dlrbTree_free(&pso->keys);
+ ED_keylist_free(pso->keylist);
if (pso->ob_data_array != NULL) {
MEM_freeN(pso->ob_data_array);
@@ -971,60 +971,56 @@ static int pose_slide_invoke_common(bContext *C, wmOperator *op, const wmEvent *
/* Do this for each F-Curve. */
for (ld = pfl->fcurves.first; ld; ld = ld->next) {
FCurve *fcu = (FCurve *)ld->data;
- fcurve_to_keylist(pfl->ob->adt, fcu, &pso->keys, 0);
+ fcurve_to_keylist(pfl->ob->adt, fcu, pso->keylist, 0);
}
}
/* Cancel if no keyframes found. */
- if (pso->keys.root) {
- ActKeyColumn *ak;
- float cframe = (float)pso->cframe;
-
- /* Firstly, check if the current frame is a keyframe. */
- ak = (ActKeyColumn *)BLI_dlrbTree_search_exact(&pso->keys, compare_ak_cfraPtr, &cframe);
-
- if (ak == NULL) {
- /* Current frame is not a keyframe, so search. */
- ActKeyColumn *pk = (ActKeyColumn *)BLI_dlrbTree_search_prev(
- &pso->keys, compare_ak_cfraPtr, &cframe);
- ActKeyColumn *nk = (ActKeyColumn *)BLI_dlrbTree_search_next(
- &pso->keys, compare_ak_cfraPtr, &cframe);
-
- /* New set the frames. */
- /* Prev frame. */
- pso->prevFrame = (pk) ? (pk->cfra) : (pso->cframe - 1);
- RNA_int_set(op->ptr, "prev_frame", pso->prevFrame);
- /* Next frame. */
- pso->nextFrame = (nk) ? (nk->cfra) : (pso->cframe + 1);
- RNA_int_set(op->ptr, "next_frame", pso->nextFrame);
- }
- else {
- /* Current frame itself is a keyframe, so just take keyframes on either side. */
- /* Prev frame. */
- pso->prevFrame = (ak->prev) ? (ak->prev->cfra) : (pso->cframe - 1);
- RNA_int_set(op->ptr, "prev_frame", pso->prevFrame);
- /* Next frame. */
- pso->nextFrame = (ak->next) ? (ak->next->cfra) : (pso->cframe + 1);
- RNA_int_set(op->ptr, "next_frame", pso->nextFrame);
- }
-
- /* Apply NLA mapping corrections so the frame look-ups work. */
- for (uint ob_index = 0; ob_index < pso->objects_len; ob_index++) {
- tPoseSlideObject *ob_data = &pso->ob_data_array[ob_index];
- if (ob_data->valid) {
- ob_data->prevFrameF = BKE_nla_tweakedit_remap(
- ob_data->ob->adt, pso->prevFrame, NLATIME_CONVERT_UNMAP);
- ob_data->nextFrameF = BKE_nla_tweakedit_remap(
- ob_data->ob->adt, pso->nextFrame, NLATIME_CONVERT_UNMAP);
- }
- }
- }
- else {
+ if (ED_keylist_is_empty(pso->keylist)) {
BKE_report(op->reports, RPT_ERROR, "No keyframes to slide between");
pose_slide_exit(C, op);
return OPERATOR_CANCELLED;
}
+ float cframe = (float)pso->cframe;
+
+ /* Firstly, check if the current frame is a keyframe. */
+ const ActKeyColumn *ak = ED_keylist_find_exact(pso->keylist, cframe);
+
+ if (ak == NULL) {
+ /* Current frame is not a keyframe, so search. */
+ const ActKeyColumn *pk = ED_keylist_find_prev(pso->keylist, cframe);
+ const ActKeyColumn *nk = ED_keylist_find_next(pso->keylist, cframe);
+
+ /* New set the frames. */
+ /* Prev frame. */
+ pso->prevFrame = (pk) ? (pk->cfra) : (pso->cframe - 1);
+ RNA_int_set(op->ptr, "prev_frame", pso->prevFrame);
+ /* Next frame. */
+ pso->nextFrame = (nk) ? (nk->cfra) : (pso->cframe + 1);
+ RNA_int_set(op->ptr, "next_frame", pso->nextFrame);
+ }
+ else {
+ /* Current frame itself is a keyframe, so just take keyframes on either side. */
+ /* Prev frame. */
+ pso->prevFrame = (ak->prev) ? (ak->prev->cfra) : (pso->cframe - 1);
+ RNA_int_set(op->ptr, "prev_frame", pso->prevFrame);
+ /* Next frame. */
+ pso->nextFrame = (ak->next) ? (ak->next->cfra) : (pso->cframe + 1);
+ RNA_int_set(op->ptr, "next_frame", pso->nextFrame);
+ }
+
+ /* Apply NLA mapping corrections so the frame look-ups work. */
+ for (uint ob_index = 0; ob_index < pso->objects_len; ob_index++) {
+ tPoseSlideObject *ob_data = &pso->ob_data_array[ob_index];
+ if (ob_data->valid) {
+ ob_data->prevFrameF = BKE_nla_tweakedit_remap(
+ ob_data->ob->adt, pso->prevFrame, NLATIME_CONVERT_UNMAP);
+ ob_data->nextFrameF = BKE_nla_tweakedit_remap(
+ ob_data->ob->adt, pso->nextFrame, NLATIME_CONVERT_UNMAP);
+ }
+ }
+
/* Initial apply for operator. */
/* TODO: need to calculate factor for initial round too. */
if (!ELEM(pso->mode, POSESLIDE_PUSH_REST, POSESLIDE_RELAX_REST)) {
@@ -1705,26 +1701,22 @@ typedef union tPosePropagate_ModeData {
*/
static float pose_propagate_get_boneHoldEndFrame(tPChanFCurveLink *pfl, float startFrame)
{
- DLRBT_Tree keys;
+ struct AnimKeylist *keylist = ED_keylist_create();
Object *ob = pfl->ob;
AnimData *adt = ob->adt;
LinkData *ld;
float endFrame = startFrame;
- /* Set up optimized data-structures for searching for relevant keyframes + holds. */
- BLI_dlrbTree_init(&keys);
-
for (ld = pfl->fcurves.first; ld; ld = ld->next) {
FCurve *fcu = (FCurve *)ld->data;
- fcurve_to_keylist(adt, fcu, &keys, 0);
+ fcurve_to_keylist(adt, fcu, keylist, 0);
}
/* Find the long keyframe (i.e. hold), and hence obtain the endFrame value
* - the best case would be one that starts on the frame itself
*/
- ActKeyColumn *ab = (ActKeyColumn *)BLI_dlrbTree_search_exact(
- &keys, compare_ak_cfraPtr, &startFrame);
+ const ActKeyColumn *ab = ED_keylist_find_exact(keylist, startFrame);
/* There are only two cases for no-exact match:
* 1) the current frame is just before another key but not on a key itself
@@ -1735,11 +1727,11 @@ static float pose_propagate_get_boneHoldEndFrame(tPChanFCurveLink *pfl, float st
*/
if (ab == NULL) {
/* We've got case 1, so try the one after. */
- ab = (ActKeyColumn *)BLI_dlrbTree_search_next(&keys, compare_ak_cfraPtr, &startFrame);
+ ab = ED_keylist_find_next(keylist, startFrame);
if ((actkeyblock_get_valid_hold(ab) & ACTKEYBLOCK_FLAG_STATIC_HOLD) == 0) {
/* Try the block before this frame then as last resort. */
- ab = (ActKeyColumn *)BLI_dlrbTree_search_prev(&keys, compare_ak_cfraPtr, &startFrame);
+ ab = ED_keylist_find_prev(keylist, startFrame);
}
}
@@ -1754,7 +1746,7 @@ static float pose_propagate_get_boneHoldEndFrame(tPChanFCurveLink *pfl, float st
if (ab) {
/* Go to next if it is also valid and meets "extension" criteria. */
while (ab->next) {
- ActKeyColumn *abn = ab->next;
+ const ActKeyColumn *abn = ab->next;
/* Must be valid. */
if ((actkeyblock_get_valid_hold(abn) & ACTKEYBLOCK_FLAG_STATIC_HOLD) == 0) {
@@ -1774,7 +1766,7 @@ static float pose_propagate_get_boneHoldEndFrame(tPChanFCurveLink *pfl, float st
}
/* Free temp memory. */
- BLI_dlrbTree_free(&keys);
+ ED_keylist_free(keylist);
/* Return the end frame we've found. */
return endFrame;
diff --git a/source/blender/editors/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c
index 1118e84ef4f..3798ca308ed 100644
--- a/source/blender/editors/armature/pose_transform.c
+++ b/source/blender/editors/armature/pose_transform.c
@@ -908,7 +908,7 @@ static int pose_paste_exec(bContext *C, wmOperator *op)
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
/* Recalculate paths if any of the bones have paths... */
- if ((ob->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS)) {
+ if (ob->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS) {
ED_pose_recalculate_paths(C, scene, ob, POSE_PATH_CALC_RANGE_FULL);
}
@@ -1219,7 +1219,7 @@ static int pose_clear_transform_generic_exec(bContext *C,
ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
/* now recalculate paths */
- if ((ob_iter->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS)) {
+ if (ob_iter->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS) {
ED_pose_recalculate_paths(C, scene, ob_iter, POSE_PATH_CALC_RANGE_FULL);
}
diff --git a/source/blender/editors/asset/ED_asset_handle.h b/source/blender/editors/asset/ED_asset_handle.h
index c51ce422c25..efb99410d3d 100644
--- a/source/blender/editors/asset/ED_asset_handle.h
+++ b/source/blender/editors/asset/ED_asset_handle.h
@@ -36,7 +36,7 @@ struct ID *ED_asset_handle_get_local_id(const struct AssetHandle *asset);
ID_Type ED_asset_handle_get_id_type(const struct AssetHandle *asset);
int ED_asset_handle_get_preview_icon_id(const struct AssetHandle *asset);
void ED_asset_handle_get_full_library_path(const struct bContext *C,
- const struct AssetLibraryReference *asset_library,
+ const struct AssetLibraryReference *asset_library_ref,
const struct AssetHandle *asset,
char r_full_lib_path[]);
diff --git a/source/blender/editors/asset/ED_asset_temp_id_consumer.h b/source/blender/editors/asset/ED_asset_temp_id_consumer.h
index 9af08c5c52b..7c10d88262e 100644
--- a/source/blender/editors/asset/ED_asset_temp_id_consumer.h
+++ b/source/blender/editors/asset/ED_asset_temp_id_consumer.h
@@ -39,7 +39,7 @@ void ED_asset_temp_id_consumer_free(AssetTempIDConsumer **consumer);
struct ID *ED_asset_temp_id_consumer_ensure_local_id(
AssetTempIDConsumer *consumer,
const struct bContext *C,
- const struct AssetLibraryReference *asset_library,
+ const struct AssetLibraryReference *asset_library_ref,
ID_Type id_type,
struct Main *bmain,
struct ReportList *reports);
diff --git a/source/blender/editors/asset/intern/asset_handle.cc b/source/blender/editors/asset/intern/asset_handle.cc
index aae85e61372..5c8d0b1349c 100644
--- a/source/blender/editors/asset/intern/asset_handle.cc
+++ b/source/blender/editors/asset/intern/asset_handle.cc
@@ -60,13 +60,13 @@ int ED_asset_handle_get_preview_icon_id(const AssetHandle *asset)
}
void ED_asset_handle_get_full_library_path(const bContext *C,
- const AssetLibraryReference *asset_library,
+ const AssetLibraryReference *asset_library_ref,
const AssetHandle *asset,
char r_full_lib_path[FILE_MAX_LIBEXTRA])
{
*r_full_lib_path = '\0';
- std::string asset_path = ED_assetlist_asset_filepath_get(C, *asset_library, *asset);
+ std::string asset_path = ED_assetlist_asset_filepath_get(C, *asset_library_ref, *asset);
if (asset_path.empty()) {
return;
}
diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc
index f18cf9712db..d69a2cae94d 100644
--- a/source/blender/editors/asset/intern/asset_ops.cc
+++ b/source/blender/editors/asset/intern/asset_ops.cc
@@ -36,7 +36,7 @@ using PointerRNAVec = blender::Vector<PointerRNA>;
static bool asset_operation_poll(bContext * /*C*/)
{
- return U.experimental.use_asset_browser;
+ return U.experimental.use_extended_asset_browser;
}
/**
@@ -110,7 +110,7 @@ void AssetMarkHelper::reportResults(ReportList &reports) const
{
/* User feedback on failure. */
if (!wasSuccessful()) {
- if ((stats.tot_already_asset > 0)) {
+ if (stats.tot_already_asset > 0) {
BKE_report(&reports,
RPT_ERROR,
"Selected data-blocks are already assets (or do not support use as assets)");
@@ -266,7 +266,7 @@ static void ASSET_OT_clear(wmOperatorType *ot)
static bool asset_list_refresh_poll(bContext *C)
{
- const AssetLibraryReference *library = CTX_wm_asset_library(C);
+ const AssetLibraryReference *library = CTX_wm_asset_library_ref(C);
if (!library) {
return false;
}
@@ -276,7 +276,7 @@ static bool asset_list_refresh_poll(bContext *C)
static int asset_list_refresh_exec(bContext *C, wmOperator *UNUSED(unused))
{
- const AssetLibraryReference *library = CTX_wm_asset_library(C);
+ const AssetLibraryReference *library = CTX_wm_asset_library_ref(C);
ED_assetlist_clear(library, C);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/asset/intern/asset_temp_id_consumer.cc b/source/blender/editors/asset/intern/asset_temp_id_consumer.cc
index bed35fdeeb5..f664eab5cbb 100644
--- a/source/blender/editors/asset/intern/asset_temp_id_consumer.cc
+++ b/source/blender/editors/asset/intern/asset_temp_id_consumer.cc
@@ -60,14 +60,14 @@ class AssetTemporaryIDConsumer : NonCopyable, NonMovable {
}
ID *import_id(const bContext *C,
- const AssetLibraryReference &asset_library,
+ const AssetLibraryReference &asset_library_ref,
ID_Type id_type,
Main &bmain,
ReportList &reports)
{
const char *asset_name = ED_asset_handle_get_name(&handle_);
char blend_file_path[FILE_MAX_LIBEXTRA];
- ED_asset_handle_get_full_library_path(C, &asset_library, &handle_, blend_file_path);
+ ED_asset_handle_get_full_library_path(C, &asset_library_ref, &handle_, blend_file_path);
temp_lib_context_ = BLO_library_temp_load_id(
&bmain, blend_file_path, id_type, asset_name, &reports);
@@ -99,12 +99,12 @@ void ED_asset_temp_id_consumer_free(AssetTempIDConsumer **consumer)
ID *ED_asset_temp_id_consumer_ensure_local_id(AssetTempIDConsumer *consumer_,
const bContext *C,
- const AssetLibraryReference *asset_library,
+ const AssetLibraryReference *asset_library_ref,
ID_Type id_type,
Main *bmain,
ReportList *reports)
{
- if (!(consumer_ && asset_library && bmain && reports)) {
+ if (!(consumer_ && asset_library_ref && bmain && reports)) {
return nullptr;
}
AssetTemporaryIDConsumer *consumer = reinterpret_cast<AssetTemporaryIDConsumer *>(consumer_);
@@ -112,5 +112,5 @@ ID *ED_asset_temp_id_consumer_ensure_local_id(AssetTempIDConsumer *consumer_,
if (ID *local_id = consumer->get_local_id()) {
return local_id;
}
- return consumer->import_id(C, *asset_library, id_type, *bmain, *reports);
+ return consumer->import_id(C, *asset_library_ref, id_type, *bmain, *reports);
}
diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c
index e7d97ce343c..c399abfa52d 100644
--- a/source/blender/editors/curve/editcurve.c
+++ b/source/blender/editors/curve/editcurve.c
@@ -1848,10 +1848,7 @@ static void ed_surf_delete_selected(Object *obedit)
nu->pntsv = 1;
SWAP(short, nu->orderu, nu->orderv);
BKE_nurb_order_clamp_u(nu);
- if (nu->knotsv) {
- MEM_freeN(nu->knotsv);
- }
- nu->knotsv = NULL;
+ MEM_SAFE_FREE(nu->knotsv);
}
else {
nu->pntsu = newu;
@@ -4650,10 +4647,7 @@ static int make_segment_exec(bContext *C, wmOperator *op)
/* now join the knots */
if (nu1->type == CU_NURBS) {
- if (nu1->knotsu != NULL) {
- MEM_freeN(nu1->knotsu);
- nu1->knotsu = NULL;
- }
+ MEM_SAFE_FREE(nu1->knotsu);
BKE_nurb_knot_calc_u(nu1);
}
diff --git a/source/blender/editors/curve/editcurve_add.c b/source/blender/editors/curve/editcurve_add.c
index 2be55accd3a..d1fe162fc4a 100644
--- a/source/blender/editors/curve/editcurve_add.c
+++ b/source/blender/editors/curve/editcurve_add.c
@@ -431,7 +431,7 @@ Nurb *ED_curve_add_nurbs_primitive(
if (newob && (U.flag & USER_ADD_VIEWALIGNED) == 0) {
ed_editnurb_spin(umat, NULL, obedit, tmp_vec, tmp_cent);
}
- else if ((U.flag & USER_ADD_VIEWALIGNED)) {
+ else if (U.flag & USER_ADD_VIEWALIGNED) {
ed_editnurb_spin(viewmat, NULL, obedit, zvec, mat[3]);
}
else {
@@ -466,7 +466,7 @@ Nurb *ED_curve_add_nurbs_primitive(
if (newob && (U.flag & USER_ADD_VIEWALIGNED) == 0) {
ed_editnurb_spin(umat, NULL, obedit, tmp_vec, tmp_cent);
}
- else if ((U.flag & USER_ADD_VIEWALIGNED)) {
+ else if (U.flag & USER_ADD_VIEWALIGNED) {
ed_editnurb_spin(viewmat, NULL, obedit, zvec, mat[3]);
}
else {
diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c
index e43e4194c51..39fb2882e4b 100644
--- a/source/blender/editors/curve/editfont.c
+++ b/source/blender/editors/curve/editfont.c
@@ -439,37 +439,27 @@ static void text_update_edited(bContext *C, Object *obedit, int mode)
WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
}
-static int kill_selection(Object *obedit, int ins) /* 1 == new character */
+static int kill_selection(Object *obedit, int ins) /* ins == new character len */
{
Curve *cu = obedit->data;
EditFont *ef = cu->editfont;
int selend, selstart, direction;
- int offset = 0;
int getfrom;
direction = BKE_vfont_select_get(obedit, &selstart, &selend);
if (direction) {
int size;
- if (ins) {
- offset = 1;
- }
if (ef->pos >= selstart) {
- ef->pos = selstart + offset;
+ ef->pos = selstart + ins;
}
if ((direction == -1) && ins) {
- selstart++;
- selend++;
- }
- getfrom = selend + offset;
- if (ins == 0) {
- getfrom++;
- }
- size = (ef->len * sizeof(*ef->textbuf)) - (selstart * sizeof(*ef->textbuf)) +
- (offset * sizeof(*ef->textbuf));
- memmove(ef->textbuf + selstart, ef->textbuf + getfrom, size);
- memmove(ef->textbufinfo + selstart,
- ef->textbufinfo + getfrom,
- ((ef->len - selstart) + offset) * sizeof(CharInfo));
+ selstart += ins;
+ selend += ins;
+ }
+ getfrom = selend + 1;
+ size = ef->len - selend; /* This is equivalent to: `(ef->len - getfrom) + 1(null)`. */
+ memmove(ef->textbuf + selstart, ef->textbuf + getfrom, sizeof(*ef->textbuf) * size);
+ memmove(ef->textbufinfo + selstart, ef->textbufinfo + getfrom, sizeof(CharInfo) * size);
ef->len -= ((selend - selstart) + 1);
ef->selstart = ef->selend = 0;
}
@@ -1650,7 +1640,7 @@ static int insert_text_exec(bContext *C, wmOperator *op)
MEM_freeN(inserted_text);
MEM_freeN(inserted_utf8);
- kill_selection(obedit, 1);
+ kill_selection(obedit, len);
text_update_edited(C, obedit, FO_EDIT);
return OPERATOR_FINISHED;
diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt
index beb22d43930..c4916b9182f 100644
--- a/source/blender/editors/datafiles/CMakeLists.txt
+++ b/source/blender/editors/datafiles/CMakeLists.txt
@@ -583,6 +583,9 @@ set(ICON_NAMES
uv_facesel
uv_islandsel
uv_sync_select
+ gp_caps_flat
+ gp_caps_round
+ fixed_size
transform_origins
gizmo
orientation_cursor
diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c
index 9bf44370c80..bf47704746b 100644
--- a/source/blender/editors/gpencil/annotate_paint.c
+++ b/source/blender/editors/gpencil/annotate_paint.c
@@ -836,10 +836,6 @@ static void annotation_stroke_newfrombuffer(tGPsdata *p)
/* exit with error if no valid points from this stroke */
if (totelem == 0) {
- if (G.debug & G_DEBUG) {
- printf("Error: No valid points in stroke buffer to convert (tot=%d)\n",
- gpd->runtime.sbuffer_used);
- }
return;
}
@@ -1263,9 +1259,6 @@ static bool annotation_session_initdata(bContext *C, tGPsdata *p)
/* make sure the active view (at the starting time) is a 3d-view */
if (curarea == NULL) {
p->status = GP_STATUS_ERROR;
- if (G.debug & G_DEBUG) {
- printf("Error: No active view for painting\n");
- }
return 0;
}
@@ -1294,11 +1287,6 @@ static bool annotation_session_initdata(bContext *C, tGPsdata *p)
if (region->regiondata == NULL) {
p->status = GP_STATUS_ERROR;
- if (G.debug & G_DEBUG) {
- printf(
- "Error: 3D-View active region doesn't have any region data, so cannot be "
- "drawable\n");
- }
return 0;
}
break;
@@ -1325,9 +1313,6 @@ static bool annotation_session_initdata(bContext *C, tGPsdata *p)
/* check that gpencil data is allowed to be drawn */
if (sseq->mainb == SEQ_DRAW_SEQUENCE) {
p->status = GP_STATUS_ERROR;
- if (G.debug & G_DEBUG) {
- printf("Error: In active view (sequencer), active mode doesn't support Grease Pencil\n");
- }
return 0;
}
break;
@@ -1387,9 +1372,6 @@ static bool annotation_session_initdata(bContext *C, tGPsdata *p)
/* unsupported views */
default: {
p->status = GP_STATUS_ERROR;
- if (G.debug & G_DEBUG) {
- printf("Error: Annotations are not supported in this editor\n");
- }
return 0;
}
}
@@ -1398,9 +1380,6 @@ static bool annotation_session_initdata(bContext *C, tGPsdata *p)
gpd_ptr = ED_annotation_data_get_pointers(C, &p->ownerPtr);
if ((gpd_ptr == NULL) || !ED_gpencil_data_owner_is_annotation(&p->ownerPtr)) {
p->status = GP_STATUS_ERROR;
- if (G.debug & G_DEBUG) {
- printf("Error: Current context doesn't allow for any Annotation data\n");
- }
return 0;
}
@@ -1507,7 +1486,6 @@ static void annotation_session_cleanup(tGPsdata *p)
/* free stroke buffer */
if (gpd->runtime.sbuffer) {
- /* printf("\t\tGP - free sbuffer\n"); */
MEM_freeN(gpd->runtime.sbuffer);
gpd->runtime.sbuffer = NULL;
}
@@ -1545,9 +1523,6 @@ static void annotation_paint_initstroke(tGPsdata *p,
}
if (p->gpl->flag & GP_LAYER_LOCKED) {
p->status = GP_STATUS_ERROR;
- if (G.debug & G_DEBUG) {
- printf("Error: Cannot paint on locked layer\n");
- }
return;
}
@@ -1573,7 +1548,6 @@ static void annotation_paint_initstroke(tGPsdata *p,
if (has_layer_to_erase == false) {
p->status = GP_STATUS_CAPTURE;
- // if (G.debug & G_DEBUG)
printf("Error: Eraser will not be affecting anything (gpencil_paint_init)\n");
return;
}
@@ -1593,9 +1567,6 @@ static void annotation_paint_initstroke(tGPsdata *p,
if (p->gpf == NULL) {
p->status = GP_STATUS_ERROR;
- if (G.debug & G_DEBUG) {
- printf("Error: No frame created (gpencil_paint_init)\n");
- }
return;
}
@@ -2063,9 +2034,6 @@ static void annotation_draw_apply(wmOperator *op, tGPsdata *p, Depsgraph *depsgr
BKE_report(op->reports, RPT_ERROR, "Cannot paint stroke");
p->status = GP_STATUS_ERROR;
- if (G.debug & G_DEBUG) {
- printf("Error: Grease-Pencil Paint - Add Point Invalid\n");
- }
return;
}
@@ -2221,29 +2189,22 @@ static int annotation_draw_exec(bContext *C, wmOperator *op)
tGPsdata *p = NULL;
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- /* printf("GPencil - Starting Re-Drawing\n"); */
-
/* try to initialize context data needed while drawing */
if (!annotation_draw_init(C, op, NULL)) {
if (op->customdata) {
MEM_freeN(op->customdata);
}
- /* printf("\tGP - no valid data\n"); */
return OPERATOR_CANCELLED;
}
p = op->customdata;
- /* printf("\tGP - Start redrawing stroke\n"); */
-
/* loop over the stroke RNA elements recorded (i.e. progress of mouse movement),
* setting the relevant values in context at each step, then applying
*/
RNA_BEGIN (op->ptr, itemptr, "stroke") {
float mousef[2];
- /* printf("\t\tGP - stroke elem\n"); */
-
/* get relevant data for this point from stroke */
RNA_float_get_array(&itemptr, "mouse", mousef);
p->mval[0] = (int)mousef[0];
@@ -2277,8 +2238,6 @@ static int annotation_draw_exec(bContext *C, wmOperator *op)
}
RNA_END;
- /* printf("\tGP - done\n"); */
-
/* cleanup */
annotation_draw_exit(C, op);
@@ -2301,18 +2260,11 @@ static int annotation_draw_invoke(bContext *C, wmOperator *op, const wmEvent *ev
RNA_enum_set(op->ptr, "mode", GP_PAINTMODE_ERASER);
}
- if (G.debug & G_DEBUG) {
- printf("GPencil - Starting Drawing\n");
- }
-
/* try to initialize context data needed while drawing */
if (!annotation_draw_init(C, op, event)) {
if (op->customdata) {
MEM_freeN(op->customdata);
}
- if (G.debug & G_DEBUG) {
- printf("\tGP - no valid data\n");
- }
return OPERATOR_CANCELLED;
}
@@ -2361,7 +2313,6 @@ static int annotation_draw_invoke(bContext *C, wmOperator *op, const wmEvent *ev
/* only start drawing immediately if we're allowed to do so... */
if (RNA_boolean_get(op->ptr, "wait_for_input") == false) {
/* hotkey invoked - start drawing */
- /* printf("\tGP - set first spot\n"); */
p->status = GP_STATUS_PAINTING;
/* handle the initial drawing - i.e. for just doing a simple dot */
@@ -2370,7 +2321,6 @@ static int annotation_draw_invoke(bContext *C, wmOperator *op, const wmEvent *ev
}
else {
/* toolbar invoked - don't start drawing yet... */
- /* printf("\tGP - hotkey invoked... waiting for click-drag\n"); */
op->flag |= OP_IS_MODAL_CURSOR_REGION;
}
@@ -2399,8 +2349,6 @@ static tGPsdata *annotation_stroke_begin(bContext *C, wmOperator *op)
p->status = GP_STATUS_ERROR;
}
- /* printf("\t\tGP - start stroke\n"); */
-
/* we may need to set up paint env again if we're resuming */
/* XXX: watch it with the paintmode! in future,
* it'd be nice to allow changing paint-mode when in sketching-sessions */
@@ -2537,8 +2485,6 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve
}
}
- // printf("\tGP - handle modal event...\n");
-
/* Exit painting mode (and/or end current stroke)
*
* NOTE: cannot do RIGHTMOUSE (as is standard for canceling)
@@ -2547,7 +2493,6 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve
if (event->val == KM_PRESS &&
ELEM(event->type, EVT_RETKEY, EVT_PADENTER, EVT_ESCKEY, EVT_SPACEKEY, EVT_EKEY)) {
/* exit() ends the current stroke before cleaning up */
- /* printf("\t\tGP - end of paint op + end of stroke\n"); */
p->status = GP_STATUS_DONE;
estate = OPERATOR_FINISHED;
}
@@ -2571,7 +2516,6 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve
if (sketch) {
/* end stroke only, and then wait to resume painting soon */
- /* printf("\t\tGP - end stroke only\n"); */
annotation_stroke_end(op);
/* If eraser mode is on, turn it off after the stroke finishes
@@ -2602,7 +2546,6 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve
WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
}
else {
- /* printf("\t\tGP - end of stroke + op\n"); */
p->status = GP_STATUS_DONE;
estate = OPERATOR_FINISHED;
}
@@ -2619,18 +2562,6 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve
ARegion *current_region = BKE_area_find_region_xy(
p->area, RGN_TYPE_ANY, event->x, event->y);
- if (G.debug & G_DEBUG) {
- printf("found alternative region %p (old was %p) - at %d %d (area: %d %d -> %d %d)\n",
- current_region,
- p->region,
- event->x,
- event->y,
- p->area->totrct.xmin,
- p->area->totrct.ymin,
- p->area->totrct.xmax,
- p->area->totrct.ymax);
- }
-
if (current_region) {
/* Assume that since we found the cursor in here, it is in bounds
* and that this should be the region that we begin drawing in
@@ -2642,10 +2573,6 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve
/* Out of bounds, or invalid in some other way */
p->status = GP_STATUS_ERROR;
estate = OPERATOR_CANCELLED;
-
- if (G.debug & G_DEBUG) {
- printf("%s: Region under cursor is out of bounds, so cannot be drawn on\n", __func__);
- }
}
}
else if (p->region) {
@@ -2657,10 +2584,6 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve
/* No region */
p->status = GP_STATUS_ERROR;
estate = OPERATOR_CANCELLED;
-
- if (G.debug & G_DEBUG) {
- printf("%s: No active region found in GP Paint session data\n", __func__);
- }
}
if (in_bounds) {
@@ -2719,7 +2642,6 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve
}
else {
/* event handled, so just tag as running modal */
- /* printf("\t\t\t\tGP - add point handled!\n"); */
estate = OPERATOR_RUNNING_MODAL;
}
}
@@ -2729,7 +2651,6 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve
/* just resize the brush (local version)
* TODO: fix the hardcoded size jumps (set to make a visible difference) and hardcoded keys
*/
- /* printf("\t\tGP - resize eraser\n"); */
switch (event->type) {
case WHEELDOWNMOUSE: /* larger */
case EVT_PADPLUSKEY:
@@ -2787,12 +2708,6 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve
case OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH:
/* event doesn't need to be handled */
-#if 0
- printf("unhandled event -> %d (mmb? = %d | mmv? = %d)\n",
- event->type,
- event->type == MIDDLEMOUSE,
- event->type == MOUSEMOVE);
-#endif
break;
}
diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c
index ee3536c2f3f..406a7ac77fc 100644
--- a/source/blender/editors/gpencil/gpencil_convert.c
+++ b/source/blender/editors/gpencil/gpencil_convert.c
@@ -389,9 +389,6 @@ static void gpencil_stroke_path_animation_preprocess_gaps(tGpTimingData *gtd,
*r_tot_gaps_time = (float)(*nbr_gaps) * gtd->gap_duration;
gtd->tot_time += *r_tot_gaps_time;
- if (G.debug & G_DEBUG) {
- printf("%f, %f, %f, %d\n", gtd->tot_time, delta_time, *r_tot_gaps_time, *nbr_gaps);
- }
if (gtd->gap_randomness > 0.0f) {
BLI_rng_srandom(rng, gtd->seed);
}
@@ -464,9 +461,6 @@ static void gpencil_stroke_path_animation_add_keyframes(ReportList *reports,
INSERTKEY_FAST);
last_valid_time = cfra;
}
- else if (G.debug & G_DEBUG) {
- printf("\t Skipping start point %d, too close from end point %d\n", i, end_stroke_idx);
- }
}
else if (i == end_stroke_idx) {
/* Always try to insert end point of a curve (should be safe enough, anyway...) */
@@ -546,13 +540,6 @@ static void gpencil_stroke_path_animation(bContext *C,
act = ED_id_action_ensure(bmain, (ID *)cu);
fcu = ED_action_fcurve_ensure(bmain, act, NULL, &ptr, "eval_time", 0);
- if (G.debug & G_DEBUG) {
- printf("%s: tot len: %f\t\ttot time: %f\n", __func__, gtd->tot_dist, gtd->tot_time);
- for (int i = 0; i < gtd->num_points; i++) {
- printf("\tpoint %d:\t\tlen: %f\t\ttime: %f\n", i, gtd->dists[i], gtd->times[i]);
- }
- }
-
if (gtd->mode == GP_STROKECONVERT_TIMING_LINEAR) {
float cfra;
@@ -610,10 +597,6 @@ static void gpencil_stroke_path_animation(bContext *C,
time_range = (float)(gtd->end_frame - gtd->start_frame);
}
- if (G.debug & G_DEBUG) {
- printf("GP Stroke Path Conversion: Starting keying!\n");
- }
-
gpencil_stroke_path_animation_add_keyframes(
reports, ptr, prop, depsgraph, fcu, cu, gtd, rng, time_range, nbr_gaps, tot_gaps_time);
@@ -623,14 +606,6 @@ static void gpencil_stroke_path_animation(bContext *C,
/* As we used INSERTKEY_FAST mode, we need to recompute all curve's handles now */
calchandles_fcurve(fcu);
- if (G.debug & G_DEBUG) {
- printf("%s: \ntot len: %f\t\ttot time: %f\n", __func__, gtd->tot_dist, gtd->tot_time);
- for (int i = 0; i < gtd->num_points; i++) {
- printf("\tpoint %d:\t\tlen: %f\t\ttime: %f\n", i, gtd->dists[i], gtd->times[i]);
- }
- printf("\n\n");
- }
-
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
/* send updates */
@@ -1588,14 +1563,8 @@ static int gpencil_convert_layer_exec(bContext *C, wmOperator *op)
C, op->reports, gpd, gpl, mode, norm_weights, rad_fac, link_strokes, &gtd);
/* free temp memory */
- if (gtd.dists) {
- MEM_freeN(gtd.dists);
- gtd.dists = NULL;
- }
- if (gtd.times) {
- MEM_freeN(gtd.times);
- gtd.times = NULL;
- }
+ MEM_SAFE_FREE(gtd.dists);
+ MEM_SAFE_FREE(gtd.times);
/* notifiers */
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c
index 67e1bd5294b..0c88d678ef4 100644
--- a/source/blender/editors/gpencil/gpencil_fill.c
+++ b/source/blender/editors/gpencil/gpencil_fill.c
@@ -646,7 +646,8 @@ static bool gpencil_render_offscreen(tGPDfill *tgpf)
tgpf->sizey = (int)tgpf->region->winy;
char err_out[256] = "unknown";
- GPUOffScreen *offscreen = GPU_offscreen_create(tgpf->sizex, tgpf->sizey, true, false, err_out);
+ GPUOffScreen *offscreen = GPU_offscreen_create(
+ tgpf->sizex, tgpf->sizey, true, GPU_RGBA8, err_out);
if (offscreen == NULL) {
printf("GPencil - Fill - Unable to create fill buffer\n");
return false;
diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h
index d1a1e417d9e..b6730cb123b 100644
--- a/source/blender/editors/gpencil/gpencil_intern.h
+++ b/source/blender/editors/gpencil/gpencil_intern.h
@@ -375,6 +375,7 @@ void GPENCIL_OT_select_less(struct wmOperatorType *ot);
void GPENCIL_OT_select_first(struct wmOperatorType *ot);
void GPENCIL_OT_select_last(struct wmOperatorType *ot);
void GPENCIL_OT_select_alternate(struct wmOperatorType *ot);
+void GPENCIL_OT_select_random(struct wmOperatorType *ot);
void GPENCIL_OT_select_vertex_color(struct wmOperatorType *ot);
void GPENCIL_OT_duplicate(struct wmOperatorType *ot);
diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c
index 8640ffa67cf..a8bd3b11bb1 100644
--- a/source/blender/editors/gpencil/gpencil_interpolate.c
+++ b/source/blender/editors/gpencil/gpencil_interpolate.c
@@ -890,9 +890,9 @@ static int gpencil_interpolate_modal(bContext *C, wmOperator *op, const wmEvent
}
case MOUSEMOVE: /* calculate new position */
{
- /* only handle mousemove if not doing numinput */
+ /* Only handle mouse-move if not doing numeric-input. */
if (has_numinput == false) {
- /* update shift based on position of mouse */
+ /* Update shift based on position of mouse. */
gpencil_mouse_update_shift(tgpi, op, event);
/* update screen */
diff --git a/source/blender/editors/gpencil/gpencil_mesh.c b/source/blender/editors/gpencil/gpencil_mesh.c
index 1882285a230..0939d53736b 100644
--- a/source/blender/editors/gpencil/gpencil_mesh.c
+++ b/source/blender/editors/gpencil/gpencil_mesh.c
@@ -316,7 +316,8 @@ static int gpencil_bake_mesh_animation_exec(bContext *C, wmOperator *op)
ob_eval->obmat,
frame_offset,
use_seams,
- use_faces);
+ use_faces,
+ true);
/* Reproject all un-tagged created strokes. */
if (project_type != GP_REPROJECT_KEEP) {
diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c
index 35640cf3b66..8c78a402e81 100644
--- a/source/blender/editors/gpencil/gpencil_ops.c
+++ b/source/blender/editors/gpencil/gpencil_ops.c
@@ -555,6 +555,7 @@ void ED_operatortypes_gpencil(void)
WM_operatortype_append(GPENCIL_OT_select_first);
WM_operatortype_append(GPENCIL_OT_select_last);
WM_operatortype_append(GPENCIL_OT_select_alternate);
+ WM_operatortype_append(GPENCIL_OT_select_random);
WM_operatortype_append(GPENCIL_OT_select_vertex_color);
WM_operatortype_append(GPENCIL_OT_duplicate);
diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c
index d6f6dbb2b10..d2dbf6ab2a6 100644
--- a/source/blender/editors/gpencil/gpencil_paint.c
+++ b/source/blender/editors/gpencil/gpencil_paint.c
@@ -976,10 +976,6 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p)
/* exit with error if no valid points from this stroke */
if (totelem == 0) {
- if (G.debug & G_DEBUG) {
- printf("Error: No valid points in stroke buffer to convert (tot=%d)\n",
- gpd->runtime.sbuffer_used);
- }
return;
}
@@ -996,6 +992,9 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p)
gps->inittime = p->inittime;
gps->uv_scale = 1.0f;
+ /* Set stroke caps. */
+ gps->caps[0] = gps->caps[1] = (short)brush->gpencil_settings->caps_type;
+
/* allocate enough memory for a continuous array for storage points */
const int subdivide = brush->gpencil_settings->draw_subdivide;
@@ -1946,9 +1945,6 @@ static bool gpencil_session_initdata(bContext *C, wmOperator *op, tGPsdata *p)
/* make sure the active view (at the starting time) is a 3d-view */
if (curarea == NULL) {
p->status = GP_STATUS_ERROR;
- if (G.debug & G_DEBUG) {
- printf("Error: No active view for painting\n");
- }
return 0;
}
@@ -1977,11 +1973,6 @@ static bool gpencil_session_initdata(bContext *C, wmOperator *op, tGPsdata *p)
if (region->regiondata == NULL) {
p->status = GP_STATUS_ERROR;
- if (G.debug & G_DEBUG) {
- printf(
- "Error: 3D-View active region doesn't have any region data, so cannot be "
- "drawable\n");
- }
return 0;
}
@@ -2007,9 +1998,6 @@ static bool gpencil_session_initdata(bContext *C, wmOperator *op, tGPsdata *p)
/* unsupported views */
default: {
p->status = GP_STATUS_ERROR;
- if (G.debug & G_DEBUG) {
- printf("Error: Active view not appropriate for Grease Pencil drawing\n");
- }
return 0;
}
}
@@ -2018,9 +2006,6 @@ static bool gpencil_session_initdata(bContext *C, wmOperator *op, tGPsdata *p)
gpd_ptr = ED_gpencil_data_get_pointers(C, &p->ownerPtr);
if ((gpd_ptr == NULL) || ED_gpencil_data_owner_is_annotation(&p->ownerPtr)) {
p->status = GP_STATUS_ERROR;
- if (G.debug & G_DEBUG) {
- printf("Error: Current context doesn't allow for any Grease Pencil data\n");
- }
return 0;
}
@@ -2144,9 +2129,6 @@ static void gpencil_paint_initstroke(tGPsdata *p,
if ((paintmode != GP_PAINTMODE_ERASER) && (p->gpl->flag & GP_LAYER_LOCKED)) {
p->status = GP_STATUS_ERROR;
- if (G.debug & G_DEBUG) {
- printf("Error: Cannot paint on locked layer\n");
- }
return;
}
@@ -2225,9 +2207,6 @@ static void gpencil_paint_initstroke(tGPsdata *p,
if (p->gpf == NULL) {
p->status = GP_STATUS_ERROR;
- if (G.debug & G_DEBUG) {
- printf("Error: No frame created (gpencil_paint_init)\n");
- }
if (!IS_AUTOKEY_ON(scene)) {
BKE_report(p->reports, RPT_INFO, "No available frame for creating stroke");
}
@@ -2821,9 +2800,6 @@ static void gpencil_draw_apply(bContext *C, wmOperator *op, tGPsdata *p, Depsgra
BKE_report(op->reports, RPT_ERROR, "Cannot paint stroke");
p->status = GP_STATUS_ERROR;
- if (G.debug & G_DEBUG) {
- printf("Error: Grease-Pencil Paint - Add Point Invalid\n");
- }
return;
}
@@ -3195,10 +3171,6 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = (bGPdata *)ob->data;
- if (G.debug & G_DEBUG) {
- printf("GPencil - Starting Drawing\n");
- }
-
/* support for tablets eraser pen */
if (gpencil_is_tablet_eraser_active(event)) {
RNA_enum_set(op->ptr, "mode", GP_PAINTMODE_ERASER);
@@ -3236,9 +3208,6 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event
if (op->customdata) {
MEM_freeN(op->customdata);
}
- if (G.debug & G_DEBUG) {
- printf("\tGP - no valid data\n");
- }
return OPERATOR_CANCELLED;
}
@@ -3727,18 +3696,6 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
ARegion *current_region = BKE_area_find_region_xy(
p->area, RGN_TYPE_ANY, event->x, event->y);
- if (G.debug & G_DEBUG) {
- printf("found alternative region %p (old was %p) - at %d %d (area: %d %d -> %d %d)\n",
- current_region,
- p->region,
- event->x,
- event->y,
- p->area->totrct.xmin,
- p->area->totrct.ymin,
- p->area->totrct.xmax,
- p->area->totrct.ymax);
- }
-
if (current_region) {
/* Assume that since we found the cursor in here, it is in bounds
* and that this should be the region that we begin drawing in
@@ -3750,10 +3707,6 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* Out of bounds, or invalid in some other way */
p->status = GP_STATUS_ERROR;
estate = OPERATOR_CANCELLED;
-
- if (G.debug & G_DEBUG) {
- printf("%s: Region under cursor is out of bounds, so cannot be drawn on\n", __func__);
- }
}
}
else if (p->region) {
@@ -3765,10 +3718,6 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* No region */
p->status = GP_STATUS_ERROR;
estate = OPERATOR_CANCELLED;
-
- if (G.debug & G_DEBUG) {
- printf("%s: No active region found in GP Paint session data\n", __func__);
- }
}
if (in_bounds) {
diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c
index cf49aefe2ea..5ecb6d9a212 100644
--- a/source/blender/editors/gpencil/gpencil_primitive.c
+++ b/source/blender/editors/gpencil/gpencil_primitive.c
@@ -335,6 +335,9 @@ static void gpencil_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi)
gps->uv_scale = 1.0f;
gps->inittime = 0.0f;
+ /* Set stroke caps. */
+ gps->caps[0] = gps->caps[1] = (short)brush->gpencil_settings->caps_type;
+
/* Apply the vertex color to fill. */
ED_gpencil_fill_vertex_color_set(ts, brush, gps);
@@ -1827,11 +1830,6 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e
else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_MOVE)) {
tgpi->flag = IN_CURVE_EDIT;
}
- else {
- if (G.debug & G_DEBUG) {
- printf("GP Add Primitive Modal: LEFTMOUSE %d, Status = %d\n", event->val, tgpi->flag);
- }
- }
break;
}
case EVT_SPACEKEY: /* confirm */
@@ -1946,9 +1944,9 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e
if (ELEM(tgpi->flag, IN_CURVE_EDIT)) {
break;
}
- /* only handle mousemove if not doing numinput */
+ /* Only handle mouse-move if not doing numeric-input. */
if (has_numinput == false) {
- /* update position of mouse */
+ /* Update position of mouse. */
copy_v2_v2(tgpi->end, tgpi->mval);
copy_v2_v2(tgpi->start, tgpi->origin);
if (tgpi->flag == IDLE) {
diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
index 14caf0c08a7..869254cef3b 100644
--- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c
+++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
@@ -968,10 +968,7 @@ static void gpencil_brush_clone_free(tGP_BrushEditData *gso)
tGPSB_CloneBrushData *data = gso->customdata;
/* free strokes array */
- if (data->new_strokes) {
- MEM_freeN(data->new_strokes);
- data->new_strokes = NULL;
- }
+ MEM_SAFE_FREE(data->new_strokes);
/* free copybuf colormap */
if (data->new_colors) {
diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c
index 69734fa1ba8..93bae7d3614 100644
--- a/source/blender/editors/gpencil/gpencil_select.c
+++ b/source/blender/editors/gpencil/gpencil_select.c
@@ -33,6 +33,7 @@
#include "BLI_ghash.h"
#include "BLI_lasso_2d.h"
#include "BLI_math_vector.h"
+#include "BLI_rand.h"
#include "BLI_utildefines.h"
#include "DNA_gpencil_types.h"
@@ -193,6 +194,28 @@ static void deselect_all_selected(bContext *C)
CTX_DATA_END;
}
+static void select_all_stroke_points(bGPdata *gpd, bGPDstroke *gps, bool select)
+{
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ if (select) {
+ pt->flag |= GP_SPOINT_SELECT;
+ }
+ else {
+ pt->flag &= ~GP_SPOINT_SELECT;
+ }
+ }
+
+ if (select) {
+ gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps);
+ }
+ else {
+ gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_reset(gps);
+ }
+}
+
static void select_all_curve_points(bGPdata *gpd, bGPDstroke *gps, bGPDcurve *gpc, bool deselect)
{
for (int i = 0; i < gpc->tot_curve_points; i++) {
@@ -513,6 +536,218 @@ void GPENCIL_OT_select_alternate(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Select Random Operator
+ * \{ */
+
+static int gpencil_select_random_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ if ((gpd == NULL) || (GPENCIL_NONE_EDIT_MODE(gpd))) {
+ return OPERATOR_CANCELLED;
+ }
+
+ const bool unselect_ends = RNA_boolean_get(op->ptr, "unselect_ends");
+ const bool select = (RNA_enum_get(op->ptr, "action") == SEL_SELECT);
+ const float randfac = RNA_float_get(op->ptr, "ratio");
+ const int seed = WM_operator_properties_select_random_seed_increment_get(op);
+ const int start = (unselect_ends) ? 1 : 0;
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
+
+ int selectmode;
+ if (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) {
+ selectmode = gpencil_select_mode_from_sculpt(ts->gpencil_selectmode_sculpt);
+ }
+ else if (ob && ob->mode == OB_MODE_VERTEX_GPENCIL) {
+ selectmode = gpencil_select_mode_from_vertex(ts->gpencil_selectmode_vertex);
+ }
+ else {
+ selectmode = ts->gpencil_selectmode_edit;
+ }
+
+ bool changed = false;
+ int seed_iter = seed;
+ int stroke_idx = 0;
+
+ if (is_curve_edit) {
+ GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
+ {
+ /* Only apply to unselected strokes (if select). */
+ if (select) {
+ if ((gps->flag & GP_STROKE_SELECT) || (gps->totpoints == 0)) {
+ continue;
+ }
+ }
+ else {
+ if (((gps->flag & GP_STROKE_SELECT) == 0) || (gps->totpoints == 0)) {
+ continue;
+ }
+ }
+
+ /* Different seed by stroke. */
+ seed_iter += gps->totpoints + stroke_idx;
+ stroke_idx++;
+
+ if (selectmode == GP_SELECTMODE_STROKE) {
+ RNG *rng = BLI_rng_new(seed_iter);
+ const unsigned int j = BLI_rng_get_uint(rng) % gps->totpoints;
+ bool select_stroke = ((gps->totpoints * randfac) <= j) ? true : false;
+ select_stroke ^= select;
+ /* Curve function has select parameter inverted. */
+ select_all_curve_points(gpd, gps, gps->editcurve, !select_stroke);
+ changed = true;
+ BLI_rng_free(rng);
+ }
+ else {
+ int elem_map_len = 0;
+ bGPDcurve_point **elem_map = MEM_mallocN(sizeof(*elem_map) * gpc->tot_curve_points,
+ __func__);
+ bGPDcurve_point *ptc;
+ for (int i = start; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ elem_map[elem_map_len++] = gpc_pt;
+ }
+
+ BLI_array_randomize(elem_map, sizeof(*elem_map), elem_map_len, seed_iter);
+ const int count_select = elem_map_len * randfac;
+ for (int i = 0; i < count_select; i++) {
+ ptc = elem_map[i];
+ if (select) {
+ ptc->flag |= GP_SPOINT_SELECT;
+ BEZT_SEL_ALL(&ptc->bezt);
+ }
+ else {
+ ptc->flag &= ~GP_SPOINT_SELECT;
+ BEZT_DESEL_ALL(&ptc->bezt);
+ }
+ }
+ MEM_freeN(elem_map);
+
+ /* unselect start and end points */
+ if (unselect_ends) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[0];
+ gpc_pt->flag &= ~GP_SPOINT_SELECT;
+ BEZT_DESEL_ALL(&gpc_pt->bezt);
+
+ gpc_pt = &gpc->curve_points[gpc->tot_curve_points - 1];
+ gpc_pt->flag &= ~GP_SPOINT_SELECT;
+ BEZT_DESEL_ALL(&gpc_pt->bezt);
+ }
+
+ BKE_gpencil_curve_sync_selection(gpd, gps);
+ }
+
+ changed = true;
+ }
+ GP_EDITABLE_CURVES_END(gps_iter);
+ }
+ else {
+ CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
+ /* Only apply to unselected strokes (if select). */
+ if (select) {
+ if ((gps->flag & GP_STROKE_SELECT) || (gps->totpoints == 0)) {
+ continue;
+ }
+ }
+ else {
+ if (((gps->flag & GP_STROKE_SELECT) == 0) || (gps->totpoints == 0)) {
+ continue;
+ }
+ }
+
+ /* Different seed by stroke. */
+ seed_iter += gps->totpoints + stroke_idx;
+ stroke_idx++;
+
+ if (selectmode == GP_SELECTMODE_STROKE) {
+ RNG *rng = BLI_rng_new(seed_iter);
+ const unsigned int j = BLI_rng_get_uint(rng) % gps->totpoints;
+ bool select_stroke = ((gps->totpoints * randfac) <= j) ? true : false;
+ select_stroke ^= select;
+ select_all_stroke_points(gpd, gps, select_stroke);
+ changed = true;
+ BLI_rng_free(rng);
+ }
+ else {
+ int elem_map_len = 0;
+ bGPDspoint **elem_map = MEM_mallocN(sizeof(*elem_map) * gps->totpoints, __func__);
+ bGPDspoint *pt;
+ for (int i = start; i < gps->totpoints; i++) {
+ pt = &gps->points[i];
+ elem_map[elem_map_len++] = pt;
+ }
+
+ BLI_array_randomize(elem_map, sizeof(*elem_map), elem_map_len, seed_iter);
+ const int count_select = elem_map_len * randfac;
+ for (int i = 0; i < count_select; i++) {
+ pt = elem_map[i];
+ if (select) {
+ pt->flag |= GP_SPOINT_SELECT;
+ }
+ else {
+ pt->flag &= ~GP_SPOINT_SELECT;
+ }
+ }
+ MEM_freeN(elem_map);
+
+ /* unselect start and end points */
+ if (unselect_ends) {
+ pt = &gps->points[0];
+ pt->flag &= ~GP_SPOINT_SELECT;
+
+ pt = &gps->points[gps->totpoints - 1];
+ pt->flag &= ~GP_SPOINT_SELECT;
+ }
+
+ BKE_gpencil_stroke_sync_selection(gpd, gps);
+ }
+
+ changed = true;
+ }
+ CTX_DATA_END;
+ }
+
+ if (changed) {
+ /* updates */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
+
+ /* copy on write tag is needed, or else no refresh happens */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE);
+
+ WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL);
+ WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_select_random(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Random";
+ ot->idname = "GPENCIL_OT_select_random";
+ ot->description = "Select random points for non selected strokes";
+
+ /* callbacks */
+ ot->exec = gpencil_select_random_exec;
+ ot->poll = gpencil_select_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ WM_operator_properties_select_random(ot);
+ RNA_def_boolean(ot->srna,
+ "unselect_ends",
+ false,
+ "Unselect Ends",
+ "Do not select the first and last point of the stroke");
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Select Grouped Operator
* \{ */
diff --git a/source/blender/editors/gpencil/gpencil_undo.c b/source/blender/editors/gpencil/gpencil_undo.c
index ede1d3eefaa..99b8b672327 100644
--- a/source/blender/editors/gpencil/gpencil_undo.c
+++ b/source/blender/editors/gpencil/gpencil_undo.c
@@ -125,7 +125,7 @@ static void gpencil_undo_free_node(bGPundonode *undo_node)
*/
undo_node->gpd->adt = NULL;
- BKE_gpencil_free(undo_node->gpd, false);
+ BKE_gpencil_free_data(undo_node->gpd, false);
MEM_freeN(undo_node->gpd);
}
@@ -133,8 +133,6 @@ void gpencil_undo_push(bGPdata *gpd)
{
bGPundonode *undo_node;
- // printf("\t\tGP - undo push\n");
-
if (cur_node) {
/* Remove all undone nodes from stack. */
undo_node = cur_node->next;
diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c
index ba3d3b584d7..5cc52303cd6 100644
--- a/source/blender/editors/gpencil/gpencil_utils.c
+++ b/source/blender/editors/gpencil/gpencil_utils.c
@@ -341,7 +341,7 @@ bool ED_gpencil_has_keyframe_v3d(Scene *UNUSED(scene), Object *ob, int cfra)
return (gpl->actframe->framenum == cfra);
}
/* XXX: disabled as could be too much of a penalty */
- /* return BKE_gpencil_layer_frame_find(gpl, cfra); */
+ // return BKE_gpencil_layer_frame_find(gpl, cfra);
}
}
diff --git a/source/blender/editors/include/ED_keyframes_draw.h b/source/blender/editors/include/ED_keyframes_draw.h
index d2d22dd38dc..61e37f20b1b 100644
--- a/source/blender/editors/include/ED_keyframes_draw.h
+++ b/source/blender/editors/include/ED_keyframes_draw.h
@@ -28,6 +28,7 @@ extern "C" {
#endif
struct AnimData;
+struct AnimKeylistDrawList;
struct FCurve;
struct MaskLayer;
struct Object;
@@ -42,6 +43,14 @@ struct bGPDlayer;
/* draw simple diamond-shape keyframe */
/* caller should set up vertex format, bind GPU_SHADER_KEYFRAME_DIAMOND,
* immBegin(GPU_PRIM_POINTS, n), then call this n times */
+typedef struct KeyframeShaderBindings {
+ uint pos_id;
+ uint size_id;
+ uint color_id;
+ uint outline_color_id;
+ uint flags_id;
+} KeyframeShaderBindings;
+
void draw_keyframe_shape(float x,
float y,
float size,
@@ -49,11 +58,7 @@ void draw_keyframe_shape(float x,
short key_type,
short mode,
float alpha,
- unsigned int pos_id,
- unsigned int size_id,
- unsigned int color_id,
- unsigned int outline_color_id,
- unsigned int flags_id,
+ const KeyframeShaderBindings *sh_bindings,
short handle_type,
short extreme_type);
@@ -61,65 +66,65 @@ void draw_keyframe_shape(float x,
/* Channel Drawing ------------------ */
/* F-Curve */
-void draw_fcurve_channel(struct View2D *v2d,
+void draw_fcurve_channel(struct AnimKeylistDrawList *draw_list,
struct AnimData *adt,
struct FCurve *fcu,
float ypos,
float yscale_fac,
int saction_flag);
/* Action Group Summary */
-void draw_agroup_channel(struct View2D *v2d,
+void draw_agroup_channel(struct AnimKeylistDrawList *draw_list,
struct AnimData *adt,
struct bActionGroup *agrp,
float ypos,
float yscale_fac,
int saction_flag);
/* Action Summary */
-void draw_action_channel(struct View2D *v2d,
+void draw_action_channel(struct AnimKeylistDrawList *draw_list,
struct AnimData *adt,
struct bAction *act,
float ypos,
float yscale_fac,
int saction_flag);
/* Object Summary */
-void draw_object_channel(struct View2D *v2d,
+void draw_object_channel(struct AnimKeylistDrawList *draw_list,
struct bDopeSheet *ads,
struct Object *ob,
float ypos,
float yscale_fac,
int saction_flag);
/* Scene Summary */
-void draw_scene_channel(struct View2D *v2d,
+void draw_scene_channel(struct AnimKeylistDrawList *draw_list,
struct bDopeSheet *ads,
struct Scene *sce,
float ypos,
float yscale_fac,
int saction_flag);
/* DopeSheet Summary */
-void draw_summary_channel(
- struct View2D *v2d, struct bAnimContext *ac, float ypos, float yscale_fac, int saction_flag);
-/* Grease Pencil datablock summary */
-void draw_gpencil_channel(struct View2D *v2d,
- struct bDopeSheet *ads,
- struct bGPdata *gpd,
+void draw_summary_channel(struct AnimKeylistDrawList *draw_list,
+ struct bAnimContext *ac,
float ypos,
float yscale_fac,
int saction_flag);
/* Grease Pencil Layer */
-void draw_gpl_channel(struct View2D *v2d,
+void draw_gpl_channel(struct AnimKeylistDrawList *draw_list,
struct bDopeSheet *ads,
struct bGPDlayer *gpl,
float ypos,
float yscale_fac,
int saction_flag);
/* Mask Layer */
-void draw_masklay_channel(struct View2D *v2d,
+void draw_masklay_channel(struct AnimKeylistDrawList *draw_list,
struct bDopeSheet *ads,
struct MaskLayer *masklay,
float ypos,
float yscale_fac,
int saction_flag);
+struct AnimKeylistDrawList *ED_keylist_draw_list_create(void);
+void ED_keylist_draw_list_flush(struct AnimKeylistDrawList *draw_list, struct View2D *v2d);
+void ED_keylist_draw_list_free(struct AnimKeylistDrawList *draw_list);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/editors/include/ED_keyframes_keylist.h b/source/blender/editors/include/ED_keyframes_keylist.h
index be3eac66771..3a9750c1206 100644
--- a/source/blender/editors/include/ED_keyframes_keylist.h
+++ b/source/blender/editors/include/ED_keyframes_keylist.h
@@ -23,6 +23,8 @@
#pragma once
+#include "BLI_range.h"
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -40,6 +42,8 @@ struct bGPDlayer;
/* ****************************** Base Structs ****************************** */
+struct AnimKeylist;
+
/* Information about the stretch of time from current to the next column */
typedef struct ActKeyBlockInfo {
/* Combination of flags from all curves. */
@@ -133,59 +137,74 @@ typedef enum eKeyframeExtremeDrawOpts {
/* ******************************* Methods ****************************** */
+struct AnimKeylist *ED_keylist_create(void);
+void ED_keylist_free(struct AnimKeylist *keylist);
+const struct ActKeyColumn *ED_keylist_find_exact(const struct AnimKeylist *keylist, float cfra);
+const struct ActKeyColumn *ED_keylist_find_next(const struct AnimKeylist *keylist, float cfra);
+const struct ActKeyColumn *ED_keylist_find_prev(const struct AnimKeylist *keylist, float cfra);
+const struct ActKeyColumn *ED_keylist_find_any_between(const struct AnimKeylist *keylist,
+ const Range2f frame_range);
+bool ED_keylist_is_empty(const struct AnimKeylist *keylist);
+const struct ListBase /* ActKeyColumn */ *ED_keylist_listbase(const struct AnimKeylist *keylist);
+bool ED_keylist_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_range);
+
/* Key-data Generation --------------- */
/* F-Curve */
void fcurve_to_keylist(struct AnimData *adt,
struct FCurve *fcu,
- struct DLRBT_Tree *keys,
- int saction_flag);
+ struct AnimKeylist *keylist,
+ const int saction_flag);
/* Action Group */
void agroup_to_keylist(struct AnimData *adt,
struct bActionGroup *agrp,
- struct DLRBT_Tree *keys,
- int saction_flag);
+ struct AnimKeylist *keylist,
+ const int saction_flag);
/* Action */
void action_to_keylist(struct AnimData *adt,
struct bAction *act,
- struct DLRBT_Tree *keys,
- int saction_flag);
+ struct AnimKeylist *keylist,
+ const int saction_flag);
/* Object */
void ob_to_keylist(struct bDopeSheet *ads,
struct Object *ob,
- struct DLRBT_Tree *keys,
- int saction_flag);
+ struct AnimKeylist *keylist,
+ const int saction_flag);
/* Cache File */
void cachefile_to_keylist(struct bDopeSheet *ads,
struct CacheFile *cache_file,
- struct DLRBT_Tree *keys,
- int saction_flag);
+ struct AnimKeylist *keylist,
+ const int saction_flag);
/* Scene */
void scene_to_keylist(struct bDopeSheet *ads,
struct Scene *sce,
- struct DLRBT_Tree *keys,
- int saction_flag);
+ struct AnimKeylist *keylist,
+ const int saction_flag);
/* DopeSheet Summary */
-void summary_to_keylist(struct bAnimContext *ac, struct DLRBT_Tree *keys, int saction_flag);
+void summary_to_keylist(struct bAnimContext *ac,
+ struct AnimKeylist *keylist,
+ const int saction_flag);
/* Grease Pencil datablock summary */
void gpencil_to_keylist(struct bDopeSheet *ads,
struct bGPdata *gpd,
- struct DLRBT_Tree *keys,
+ struct AnimKeylist *keylist,
const bool active);
/* Grease Pencil Layer */
-void gpl_to_keylist(struct bDopeSheet *ads, struct bGPDlayer *gpl, struct DLRBT_Tree *keys);
+void gpl_to_keylist(struct bDopeSheet *ads, struct bGPDlayer *gpl, struct AnimKeylist *keylist);
/* Mask */
-void mask_to_keylist(struct bDopeSheet *ads, struct MaskLayer *masklay, struct DLRBT_Tree *keys);
+void mask_to_keylist(struct bDopeSheet *ads,
+ struct MaskLayer *masklay,
+ struct AnimKeylist *keylist);
/* ActKeyColumn API ---------------- */
/* Comparator callback used for ActKeyColumns and cframe float-value pointer */
short compare_ak_cfraPtr(void *node, void *data);
/* Checks if ActKeyColumn has any block data */
-bool actkeyblock_is_valid(ActKeyColumn *ac);
+bool actkeyblock_is_valid(const ActKeyColumn *ac);
/* Checks if ActKeyColumn can be used as a block (i.e. drawn/used to detect "holds") */
-int actkeyblock_get_valid_hold(ActKeyColumn *ac);
+int actkeyblock_get_valid_hold(const ActKeyColumn *ac);
#ifdef __cplusplus
}
diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h
index 888dcd9d428..3141c8f707b 100644
--- a/source/blender/editors/include/ED_object.h
+++ b/source/blender/editors/include/ED_object.h
@@ -53,6 +53,7 @@ struct uiLayout;
struct wmKeyConfig;
struct wmOperator;
struct wmOperatorType;
+struct wmEvent;
/* object_edit.c */
/* context.object */
@@ -199,6 +200,9 @@ void ED_object_parent(struct Object *ob,
struct Object *parent,
const int type,
const char *substr);
+char *ED_object_ot_drop_named_material_tooltip(struct bContext *C,
+ struct PointerRNA *properties,
+ const struct wmEvent *event);
/* bitflags for enter/exit editmode */
enum {
diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h
index 7ccdc49d291..1708c3598b1 100644
--- a/source/blender/editors/include/UI_icons.h
+++ b/source/blender/editors/include/UI_icons.h
@@ -713,9 +713,9 @@ DEF_ICON(UV_EDGESEL)
DEF_ICON(UV_FACESEL)
DEF_ICON(UV_ISLANDSEL)
DEF_ICON(UV_SYNC_SELECT)
-DEF_ICON_BLANK(240)
-DEF_ICON_BLANK(241)
-DEF_ICON_BLANK(242)
+DEF_ICON(GP_CAPS_FLAT)
+DEF_ICON(GP_CAPS_ROUND)
+DEF_ICON(FIXED_SIZE)
DEF_ICON(TRANSFORM_ORIGINS)
DEF_ICON(GIZMO)
DEF_ICON(ORIENTATION_CURSOR)
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index a6e465d04e8..30be3588b5a 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -2577,10 +2577,7 @@ void ED_keymap_ui(struct wmKeyConfig *keyconf);
void ED_uilisttypes_ui(void);
void UI_drop_color_copy(struct wmDrag *drag, struct wmDropBox *drop);
-bool UI_drop_color_poll(struct bContext *C,
- struct wmDrag *drag,
- const struct wmEvent *event,
- const char **r_tooltip);
+bool UI_drop_color_poll(struct bContext *C, struct wmDrag *drag, const struct wmEvent *event);
bool UI_context_copy_to_selected_list(struct bContext *C,
struct PointerRNA *ptr,
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
index a2b25aed582..fd75be5b847 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -4017,9 +4017,11 @@ uiBut *ui_but_change_type(uiBut *but, eButType new_type)
UNUSED_VARS_NDEBUG(found_layout);
ui_button_group_replace_but_ptr(uiLayoutGetBlock(but->layout), old_but_ptr, but);
}
+#ifdef WITH_PYTHON
if (UI_editsource_enable_check()) {
UI_editsource_but_replace(old_but_ptr, but);
}
+#endif
}
return but;
@@ -6171,7 +6173,7 @@ int UI_but_return_value_get(uiBut *but)
void UI_but_drag_set_id(uiBut *but, ID *id)
{
but->dragtype = WM_DRAG_ID;
- if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) {
+ if (but->dragflag & UI_BUT_DRAGPOIN_FREE) {
WM_drag_data_free(but->dragtype, but->dragpoin);
but->dragflag &= ~UI_BUT_DRAGPOIN_FREE;
}
@@ -6198,7 +6200,7 @@ void UI_but_drag_set_asset(uiBut *but,
but->dragtype = WM_DRAG_ASSET;
ui_def_but_icon(but, icon, 0); /* no flag UI_HAS_ICON, so icon doesn't draw in button */
- if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) {
+ if (but->dragflag & UI_BUT_DRAGPOIN_FREE) {
WM_drag_data_free(but->dragtype, but->dragpoin);
}
but->dragpoin = asset_drag;
@@ -6210,7 +6212,7 @@ void UI_but_drag_set_asset(uiBut *but,
void UI_but_drag_set_rna(uiBut *but, PointerRNA *ptr)
{
but->dragtype = WM_DRAG_RNA;
- if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) {
+ if (but->dragflag & UI_BUT_DRAGPOIN_FREE) {
WM_drag_data_free(but->dragtype, but->dragpoin);
but->dragflag &= ~UI_BUT_DRAGPOIN_FREE;
}
@@ -6220,7 +6222,7 @@ void UI_but_drag_set_rna(uiBut *but, PointerRNA *ptr)
void UI_but_drag_set_path(uiBut *but, const char *path, const bool use_free)
{
but->dragtype = WM_DRAG_PATH;
- if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) {
+ if (but->dragflag & UI_BUT_DRAGPOIN_FREE) {
WM_drag_data_free(but->dragtype, but->dragpoin);
but->dragflag &= ~UI_BUT_DRAGPOIN_FREE;
}
@@ -6233,7 +6235,7 @@ void UI_but_drag_set_path(uiBut *but, const char *path, const bool use_free)
void UI_but_drag_set_name(uiBut *but, const char *name)
{
but->dragtype = WM_DRAG_NAME;
- if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) {
+ if (but->dragflag & UI_BUT_DRAGPOIN_FREE) {
WM_drag_data_free(but->dragtype, but->dragpoin);
but->dragflag &= ~UI_BUT_DRAGPOIN_FREE;
}
@@ -6251,7 +6253,7 @@ void UI_but_drag_set_image(
{
but->dragtype = WM_DRAG_PATH;
ui_def_but_icon(but, icon, 0); /* no flag UI_HAS_ICON, so icon doesn't draw in button */
- if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) {
+ if (but->dragflag & UI_BUT_DRAGPOIN_FREE) {
WM_drag_data_free(but->dragtype, but->dragpoin);
but->dragflag &= ~UI_BUT_DRAGPOIN_FREE;
}
diff --git a/source/blender/editors/interface/interface_align.c b/source/blender/editors/interface/interface_align.c
index dbfdfbf7950..3149675ac04 100644
--- a/source/blender/editors/interface/interface_align.c
+++ b/source/blender/editors/interface/interface_align.c
@@ -343,7 +343,7 @@ static int ui_block_align_butal_cmp(const void *a, const void *b)
* stupid UI code produces widgets which have the same TOP and LEFT positions...
* We do not care really,
* because this happens when UI is way too small to be usable anyway. */
- /* BLI_assert(0); */
+ // BLI_assert(0);
return 0;
}
diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c
index d917534895d..b953d88c896 100644
--- a/source/blender/editors/interface/interface_context_menu.c
+++ b/source/blender/editors/interface/interface_context_menu.c
@@ -373,13 +373,7 @@ static void ui_but_user_menu_add(bContext *C, uiBut *but, bUserMenu *um)
BLI_assert(ui_but_is_user_menu_compatible(C, but));
char drawstr[sizeof(but->drawstr)];
- STRNCPY(drawstr, but->drawstr);
- if (but->flag & UI_BUT_HAS_SEP_CHAR) {
- char *sep = strrchr(drawstr, UI_SEP_CHAR);
- if (sep) {
- *sep = '\0';
- }
- }
+ ui_but_drawstr_without_sep_char(but, drawstr, sizeof(drawstr));
MenuType *mt = NULL;
if (but->optype) {
@@ -952,7 +946,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *ev
}
/* If the button represents an id, it can set the "id" context pointer. */
- if (U.experimental.use_asset_browser && ED_asset_can_mark_single_from_context(C)) {
+ if (U.experimental.use_extended_asset_browser && ED_asset_can_mark_single_from_context(C)) {
ID *id = CTX_data_pointer_get_type(C, "id", &RNA_ID).data;
/* Gray out items depending on if data-block is an asset. Preferably this could be done via
diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c
index 65104885d98..ebebf69bc11 100644
--- a/source/blender/editors/interface/interface_draw.c
+++ b/source/blender/editors/interface/interface_draw.c
@@ -2281,7 +2281,7 @@ static void ui_shadowbox(const rctf *rect, uint pos, uint color, float shadsize,
immVertex2fv(pos, v3);
/* corner shape */
- /* immAttr4ub(color, 0, 0, 0, alpha); */ /* Not needed, done above in previous tri */
+ // immAttr4ub(color, 0, 0, 0, alpha); /* Not needed, done above in previous tri. */
immVertex2fv(pos, v3);
immAttr4ub(color, 0, 0, 0, 0);
immVertex2fv(pos, v4);
@@ -2293,7 +2293,7 @@ static void ui_shadowbox(const rctf *rect, uint pos, uint color, float shadsize,
immVertex2fv(pos, v3);
/* bottom quad */
- /* immAttr4ub(color, 0, 0, 0, alpha); */ /* Not needed, done above in previous tri */
+ // immAttr4ub(color, 0, 0, 0, alpha); /* Not needed, done above in previous tri. */
immVertex2fv(pos, v3);
immAttr4ub(color, 0, 0, 0, 0);
immVertex2fv(pos, v6);
diff --git a/source/blender/editors/interface/interface_eyedropper_driver.c b/source/blender/editors/interface/interface_eyedropper_driver.c
index 8762a4819d4..ccf0e727da8 100644
--- a/source/blender/editors/interface/interface_eyedropper_driver.c
+++ b/source/blender/editors/interface/interface_eyedropper_driver.c
@@ -84,10 +84,7 @@ static void driverdropper_exit(bContext *C, wmOperator *op)
{
WM_cursor_modal_restore(CTX_wm_window(C));
- if (op->customdata) {
- MEM_freeN(op->customdata);
- op->customdata = NULL;
- }
+ MEM_SAFE_FREE(op->customdata);
}
static void driverdropper_sample(bContext *C, wmOperator *op, const wmEvent *event)
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index bfc03a95949..76f6640c714 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -3086,11 +3086,6 @@ static void ui_textedit_set_cursor_pos(uiBut *but, uiHandleButtonData *data, con
UI_fontstyle_set(&fstyle);
- if (fstyle.kerning == 1) {
- /* for BLF_width */
- BLF_enable(fstyle.uifont_id, BLF_KERNING_DEFAULT);
- }
-
ui_but_text_password_hide(password_str, but, false);
if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) {
@@ -3141,10 +3136,6 @@ static void ui_textedit_set_cursor_pos(uiBut *but, uiHandleButtonData *data, con
but->pos = glyph_data[1] + but->ofs;
}
- if (fstyle.kerning == 1) {
- BLF_disable(fstyle.uifont_id, BLF_KERNING_DEFAULT);
- }
-
ui_but_text_password_hide(password_str, but, true);
}
@@ -3438,10 +3429,7 @@ static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data)
const bool is_num_but = ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER);
bool no_zero_strip = false;
- if (data->str) {
- MEM_freeN(data->str);
- data->str = NULL;
- }
+ MEM_SAFE_FREE(data->str);
#ifdef USE_DRAG_MULTINUM
/* this can happen from multi-drag */
@@ -6037,7 +6025,7 @@ static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, co
* the slot menu fails to switch a second time.
*
* The active state of the button could be maintained some other way
- * and remove this mousemove event.
+ * and remove this mouse-move event.
*/
WM_event_add_mousemove(data->window);
@@ -6779,7 +6767,7 @@ static bool ui_numedit_but_HSVCIRCLE(uiBut *but,
ui_color_picker_hsv_to_rgb(hsv, rgb);
- if ((cpicker->use_luminosity_lock)) {
+ if (cpicker->use_luminosity_lock) {
if (!is_zero_v3(rgb)) {
normalize_v3_length(rgb, cpicker->luminosity_lock_value);
}
@@ -8376,7 +8364,7 @@ static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState s
}
}
- /* wait for mousemove to enable drag */
+ /* Wait for mouse-move to enable drag. */
if (state == BUTTON_STATE_WAIT_DRAG) {
but->flag &= ~UI_SELECT;
}
@@ -8635,10 +8623,7 @@ static void button_activate_exit(
}
/* clean up button */
- if (but->active) {
- MEM_freeN(but->active);
- but->active = NULL;
- }
+ MEM_SAFE_FREE(but->active);
but->flag &= ~(UI_ACTIVE | UI_SELECT);
but->flag |= UI_BUT_LAST_ACTIVE;
@@ -8646,9 +8631,9 @@ static void button_activate_exit(
ui_but_update(but);
}
- /* adds empty mousemove in queue for re-init handler, in case mouse is
+ /* Adds empty mouse-move in queue for re-initialize handler, in case mouse is
* still over a button. We cannot just check for this ourselves because
- * at this point the mouse may be over a button in another region */
+ * at this point the mouse may be over a button in another region. */
if (mousemove) {
WM_event_add_mousemove(CTX_wm_window(C));
}
diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c
index 0ffc5659191..f739830cfdb 100644
--- a/source/blender/editors/interface/interface_icons.c
+++ b/source/blender/editors/interface/interface_icons.c
@@ -299,13 +299,14 @@ static void vicon_keytype_draw_wrapper(
const float yco = y + h / 2 + 0.5f;
GPUVertFormat *format = immVertexFormat();
- const uint pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- const uint size_id = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
- uint color_id = GPU_vertformat_attr_add(
+ KeyframeShaderBindings sh_bindings;
+ sh_bindings.pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ sh_bindings.size_id = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+ sh_bindings.color_id = GPU_vertformat_attr_add(
format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
- uint outline_color_id = GPU_vertformat_attr_add(
+ sh_bindings.outline_color_id = GPU_vertformat_attr_add(
format, "outlineColor", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
- const uint flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT);
+ sh_bindings.flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT);
GPU_program_point_size(true);
immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND);
@@ -326,11 +327,7 @@ static void vicon_keytype_draw_wrapper(
key_type,
KEYFRAME_SHAPE_BOTH,
alpha,
- pos_id,
- size_id,
- color_id,
- outline_color_id,
- flags_id,
+ &sh_bindings,
handle_type,
KEYFRAME_EXTREME_NONE);
@@ -1014,7 +1011,7 @@ static void init_iconfile_list(struct ListBase *list)
int index = 1;
for (int i = 0; i < totfile; i++) {
- if ((dir[i].type & S_IFREG)) {
+ if (dir[i].type & S_IFREG) {
const char *filename = dir[i].relname;
if (BLI_path_extension_check(filename, ".png")) {
@@ -2425,6 +2422,7 @@ void UI_icon_draw_ex(float x,
ImBuf *UI_icon_alert_imbuf_get(eAlertIcon icon)
{
#ifdef WITH_HEADLESS
+ UNUSED_VARS(icon);
return NULL;
#else
const int ALERT_IMG_SIZE = 256;
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index 6b0b8e8df8f..d61104f094e 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -1177,6 +1177,8 @@ uiBut *ui_list_find_mouse_over_ex(const struct ARegion *region,
bool ui_but_contains_password(const uiBut *but) ATTR_WARN_UNUSED_RESULT;
+size_t ui_but_drawstr_without_sep_char(const uiBut *but, char *str, size_t str_maxlen)
+ ATTR_NONNULL(1, 2);
size_t ui_but_drawstr_len_without_sep_char(const uiBut *but);
size_t ui_but_tip_len_only_first_line(const uiBut *but);
diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c
index 03c67e9b046..ec5a30f7793 100644
--- a/source/blender/editors/interface/interface_layout.c
+++ b/source/blender/editors/interface/interface_layout.c
@@ -282,7 +282,7 @@ static int ui_layout_vary_direction(uiLayout *layout)
static bool ui_layout_variable_size(uiLayout *layout)
{
- /* Note that this code is probably a bit flakey, we'd probably want to know whether it's
+ /* Note that this code is probably a bit flaky, we'd probably want to know whether it's
* variable in X and/or Y, etc. But for now it mimics previous one,
* with addition of variable flag set for children of grid-flow layouts. */
return ui_layout_vary_direction(layout) == UI_ITEM_VARY_X || layout->variable_size;
diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c
index 3ab49b8773b..dd10d942fc9 100644
--- a/source/blender/editors/interface/interface_ops.c
+++ b/source/blender/editors/interface/interface_ops.c
@@ -998,55 +998,69 @@ static bool copy_to_selected_button(bContext *C, bool all, bool poll)
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
/* if there is a valid property that is editable... */
- if (ptr.data && prop) {
- char *path = NULL;
- bool use_path_from_id;
- ListBase lb = {NULL};
-
- if (UI_context_copy_to_selected_list(C, &ptr, prop, &lb, &use_path_from_id, &path) &&
- !BLI_listbase_is_empty(&lb)) {
- LISTBASE_FOREACH (CollectionPointerLink *, link, &lb) {
- if (link->ptr.data != ptr.data) {
- if (use_path_from_id) {
- /* Path relative to ID. */
- lprop = NULL;
- RNA_id_pointer_create(link->ptr.owner_id, &idptr);
- RNA_path_resolve_property(&idptr, path, &lptr, &lprop);
- }
- else if (path) {
- /* Path relative to elements from list. */
- lprop = NULL;
- RNA_path_resolve_property(&link->ptr, path, &lptr, &lprop);
- }
- else {
- lptr = link->ptr;
- lprop = prop;
- }
+ if (ptr.data == NULL || prop == NULL) {
+ return false;
+ }
- if (lptr.data == ptr.data) {
- /* lptr might not be the same as link->ptr! */
- continue;
- }
+ char *path = NULL;
+ bool use_path_from_id;
+ ListBase lb = {NULL};
- if (lprop == prop) {
- if (RNA_property_editable(&lptr, lprop)) {
- if (poll) {
- success = true;
- break;
- }
- if (RNA_property_copy(bmain, &lptr, &ptr, prop, (all) ? -1 : index)) {
- RNA_property_update(C, &lptr, prop);
- success = true;
- }
- }
- }
- }
- }
- }
+ if (!UI_context_copy_to_selected_list(C, &ptr, prop, &lb, &use_path_from_id, &path)) {
+ return false;
+ }
+ if (BLI_listbase_is_empty(&lb)) {
MEM_SAFE_FREE(path);
- BLI_freelistN(&lb);
+ return false;
}
+ LISTBASE_FOREACH (CollectionPointerLink *, link, &lb) {
+ if (link->ptr.data == ptr.data) {
+ continue;
+ }
+
+ if (use_path_from_id) {
+ /* Path relative to ID. */
+ lprop = NULL;
+ RNA_id_pointer_create(link->ptr.owner_id, &idptr);
+ RNA_path_resolve_property(&idptr, path, &lptr, &lprop);
+ }
+ else if (path) {
+ /* Path relative to elements from list. */
+ lprop = NULL;
+ RNA_path_resolve_property(&link->ptr, path, &lptr, &lprop);
+ }
+ else {
+ lptr = link->ptr;
+ lprop = prop;
+ }
+
+ if (lptr.data == ptr.data) {
+ /* lptr might not be the same as link->ptr! */
+ continue;
+ }
+
+ if (lprop != prop) {
+ continue;
+ }
+
+ if (!RNA_property_editable(&lptr, lprop)) {
+ continue;
+ }
+
+ if (poll) {
+ success = true;
+ break;
+ }
+ if (RNA_property_copy(bmain, &lptr, &ptr, prop, (all) ? -1 : index)) {
+ RNA_property_update(C, &lptr, prop);
+ success = true;
+ }
+ }
+
+ MEM_SAFE_FREE(path);
+ BLI_freelistN(&lb);
+
return success;
}
@@ -1557,7 +1571,7 @@ static int edittranslation_exec(bContext *C, wmOperator *op)
}
/* Try to find a valid po file for current language... */
edittranslation_find_po_file(root, uilng, popath, FILE_MAX);
- /* printf("po path: %s\n", popath); */
+ // printf("po path: %s\n", popath);
if (popath[0] == '\0') {
BKE_reportf(
op->reports, RPT_ERROR, "No valid po found for language '%s' under %s", uilng, root);
@@ -1759,10 +1773,7 @@ static void UI_OT_button_string_clear(wmOperatorType *ot)
/** \name Drop Color Operator
* \{ */
-bool UI_drop_color_poll(struct bContext *C,
- wmDrag *drag,
- const wmEvent *UNUSED(event),
- const char **UNUSED(r_tooltip))
+bool UI_drop_color_poll(struct bContext *C, wmDrag *drag, const wmEvent *UNUSED(event))
{
/* should only return true for regions that include buttons, for now
* return true always */
diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c
index 97d01ac3763..a64797af24f 100644
--- a/source/blender/editors/interface/interface_panel.c
+++ b/source/blender/editors/interface/interface_panel.c
@@ -1447,10 +1447,6 @@ void UI_panel_category_draw_all(ARegion *region, const char *category_id_active)
is_alpha = (region->overlap && (theme_col_back[3] != 255));
- if (fstyle->kerning == 1) {
- BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
- }
-
BLF_enable(fontid, BLF_ROTATION);
BLF_rotation(fontid, M_PI_2);
// UI_fontstyle_set(&style->widget);
@@ -1620,10 +1616,6 @@ void UI_panel_category_draw_all(ARegion *region, const char *category_id_active)
GPU_line_smooth(false);
BLF_disable(fontid, BLF_ROTATION);
-
- if (fstyle->kerning == 1) {
- BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
- }
}
#undef TABS_PADDING_BETWEEN_FACTOR
diff --git a/source/blender/editors/interface/interface_query.c b/source/blender/editors/interface/interface_query.c
index 8534c95b6fd..09429bb6df5 100644
--- a/source/blender/editors/interface/interface_query.c
+++ b/source/blender/editors/interface/interface_query.c
@@ -23,6 +23,7 @@
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_rect.h"
+#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "DNA_screen_types.h"
@@ -553,6 +554,12 @@ size_t ui_but_drawstr_len_without_sep_char(const uiBut *but)
return strlen(but->drawstr);
}
+size_t ui_but_drawstr_without_sep_char(const uiBut *but, char *str, size_t str_maxlen)
+{
+ size_t str_len_clip = ui_but_drawstr_len_without_sep_char(but);
+ return BLI_strncpy_rlen(str, but->drawstr, min_zz(str_len_clip + 1, str_maxlen));
+}
+
size_t ui_but_tip_len_only_first_line(const uiBut *but)
{
if (but->tip == NULL) {
diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c
index 10bc3760b42..a8f289702f8 100644
--- a/source/blender/editors/interface/interface_region_tooltip.c
+++ b/source/blender/editors/interface/interface_region_tooltip.c
@@ -1175,9 +1175,6 @@ static ARegion *ui_tooltip_create_with_data(bContext *C,
data->wrap_width = min_ii(UI_TIP_MAXWIDTH * U.pixelsize / aspect, winx - (UI_TIP_PADDING * 2));
font_flag |= BLF_WORD_WRAP;
- if (data->fstyle.kerning == 1) {
- font_flag |= BLF_KERNING_DEFAULT;
- }
BLF_enable(data->fstyle.uifont_id, font_flag);
BLF_enable(blf_mono_font, font_flag);
BLF_wordwrap(data->fstyle.uifont_id, data->wrap_width);
diff --git a/source/blender/editors/interface/interface_style.c b/source/blender/editors/interface/interface_style.c
index 88ab6a377d0..804156ba48c 100644
--- a/source/blender/editors/interface/interface_style.c
+++ b/source/blender/editors/interface/interface_style.c
@@ -83,7 +83,6 @@ static uiStyle *ui_style_new(ListBase *styles, const char *name, short uifont_id
style->paneltitle.uifont_id = uifont_id;
style->paneltitle.points = UI_DEFAULT_TITLE_POINTS;
- style->paneltitle.kerning = 1;
style->paneltitle.shadow = 3;
style->paneltitle.shadx = 0;
style->paneltitle.shady = -1;
@@ -92,7 +91,6 @@ static uiStyle *ui_style_new(ListBase *styles, const char *name, short uifont_id
style->grouplabel.uifont_id = uifont_id;
style->grouplabel.points = UI_DEFAULT_TITLE_POINTS;
- style->grouplabel.kerning = 1;
style->grouplabel.shadow = 3;
style->grouplabel.shadx = 0;
style->grouplabel.shady = -1;
@@ -101,7 +99,6 @@ static uiStyle *ui_style_new(ListBase *styles, const char *name, short uifont_id
style->widgetlabel.uifont_id = uifont_id;
style->widgetlabel.points = UI_DEFAULT_TEXT_POINTS;
- style->widgetlabel.kerning = 1;
style->widgetlabel.shadow = 3;
style->widgetlabel.shadx = 0;
style->widgetlabel.shady = -1;
@@ -110,7 +107,6 @@ static uiStyle *ui_style_new(ListBase *styles, const char *name, short uifont_id
style->widget.uifont_id = uifont_id;
style->widget.points = UI_DEFAULT_TEXT_POINTS;
- style->widget.kerning = 1;
style->widget.shadow = 1;
style->widget.shady = -1;
style->widget.shadowalpha = 0.5f;
@@ -164,9 +160,6 @@ void UI_fontstyle_draw_ex(const uiFontStyle *fs,
BLF_shadow(fs->uifont_id, fs->shadow, shadow_color);
BLF_shadow_offset(fs->uifont_id, fs->shadx, fs->shady);
}
- if (fs->kerning == 1) {
- font_flag |= BLF_KERNING_DEFAULT;
- }
if (fs_params->word_wrap == 1) {
font_flag |= BLF_WORD_WRAP;
}
@@ -278,19 +271,12 @@ void UI_fontstyle_draw_rotated(const uiFontStyle *fs,
BLF_shadow_offset(fs->uifont_id, fs->shadx, fs->shady);
}
- if (fs->kerning == 1) {
- BLF_enable(fs->uifont_id, BLF_KERNING_DEFAULT);
- }
-
BLF_draw(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX);
BLF_disable(fs->uifont_id, BLF_ROTATION);
BLF_disable(fs->uifont_id, BLF_CLIPPING);
if (fs->shadow) {
BLF_disable(fs->uifont_id, BLF_SHADOW);
}
- if (fs->kerning == 1) {
- BLF_disable(fs->uifont_id, BLF_KERNING_DEFAULT);
- }
}
/**
@@ -302,18 +288,10 @@ void UI_fontstyle_draw_rotated(const uiFontStyle *fs,
void UI_fontstyle_draw_simple(
const uiFontStyle *fs, float x, float y, const char *str, const uchar col[4])
{
- if (fs->kerning == 1) {
- BLF_enable(fs->uifont_id, BLF_KERNING_DEFAULT);
- }
-
UI_fontstyle_set(fs);
BLF_position(fs->uifont_id, x, y, 0.0f);
BLF_color4ubv(fs->uifont_id, col);
BLF_draw(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX);
-
- if (fs->kerning == 1) {
- BLF_disable(fs->uifont_id, BLF_KERNING_DEFAULT);
- }
}
/**
@@ -326,10 +304,6 @@ void UI_fontstyle_draw_simple_backdrop(const uiFontStyle *fs,
const float col_fg[4],
const float col_bg[4])
{
- if (fs->kerning == 1) {
- BLF_enable(fs->uifont_id, BLF_KERNING_DEFAULT);
- }
-
UI_fontstyle_set(fs);
{
@@ -357,10 +331,6 @@ void UI_fontstyle_draw_simple_backdrop(const uiFontStyle *fs,
BLF_position(fs->uifont_id, x, y, 0.0f);
BLF_color4fv(fs->uifont_id, col_fg);
BLF_draw(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX);
-
- if (fs->kerning == 1) {
- BLF_disable(fs->uifont_id, BLF_KERNING_DEFAULT);
- }
}
/* ************** helpers ************************ */
@@ -405,21 +375,8 @@ const uiStyle *UI_style_get_dpi(void)
int UI_fontstyle_string_width(const uiFontStyle *fs, const char *str)
{
- int width;
-
- if (fs->kerning == 1) {
- /* for BLF_width */
- BLF_enable(fs->uifont_id, BLF_KERNING_DEFAULT);
- }
-
UI_fontstyle_set(fs);
- width = BLF_width(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX);
-
- if (fs->kerning == 1) {
- BLF_disable(fs->uifont_id, BLF_KERNING_DEFAULT);
- }
-
- return width;
+ return (int)BLF_width(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX);
}
int UI_fontstyle_height_max(const uiFontStyle *fs)
diff --git a/source/blender/editors/interface/interface_template_asset_view.cc b/source/blender/editors/interface/interface_template_asset_view.cc
index b4ba6a7feab..9b601727e29 100644
--- a/source/blender/editors/interface/interface_template_asset_view.cc
+++ b/source/blender/editors/interface/interface_template_asset_view.cc
@@ -44,7 +44,7 @@
#include "interface_intern.h"
struct AssetViewListData {
- AssetLibraryReference asset_library;
+ AssetLibraryReference asset_library_ref;
bScreen *screen;
};
@@ -62,7 +62,7 @@ static void asset_view_item_but_drag_set(uiBut *but,
/* Context can be null here, it's only needed for a File Browser specific hack that should go
* away before too long. */
ED_asset_handle_get_full_library_path(
- nullptr, &list_data->asset_library, asset_handle, blend_path);
+ nullptr, &list_data->asset_library_ref, asset_handle, blend_path);
if (blend_path[0]) {
ImBuf *imbuf = ED_assetlist_asset_image_get(asset_handle);
@@ -136,7 +136,7 @@ static void asset_view_listener(uiList *ui_list, wmRegionListenerParams *params)
}
}
- if (ED_assetlist_listen(&list_data->asset_library, params->notifier)) {
+ if (ED_assetlist_listen(&list_data->asset_library_ref, params->notifier)) {
ED_region_tag_redraw(params->region);
}
}
@@ -153,7 +153,7 @@ uiListType *UI_UL_asset_view()
}
static void asset_view_template_refresh_asset_collection(
- const AssetLibraryReference &asset_library,
+ const AssetLibraryReference &asset_library_ref,
const AssetFilterSettings &filter_settings,
PointerRNA &assets_dataptr,
const char *assets_propname)
@@ -175,7 +175,7 @@ static void asset_view_template_refresh_asset_collection(
RNA_property_collection_clear(&assets_dataptr, assets_prop);
- ED_assetlist_iterate(&asset_library, [&](AssetHandle asset) {
+ ED_assetlist_iterate(&asset_library_ref, [&](AssetHandle asset) {
if (!ED_asset_filter_matches_asset(&filter_settings, &asset)) {
/* Don't do anything else, but return true to continue iterating. */
return true;
@@ -216,25 +216,25 @@ void uiTemplateAssetView(uiLayout *layout,
PropertyRNA *asset_library_prop = RNA_struct_find_property(asset_library_dataptr,
asset_library_propname);
- AssetLibraryReference asset_library = ED_asset_library_reference_from_enum_value(
+ AssetLibraryReference asset_library_ref = ED_asset_library_reference_from_enum_value(
RNA_property_enum_get(asset_library_dataptr, asset_library_prop));
uiLayout *row = uiLayoutRow(col, true);
uiItemFullR(row, asset_library_dataptr, asset_library_prop, RNA_NO_INDEX, 0, 0, "", 0);
- if (asset_library.type != ASSET_LIBRARY_LOCAL) {
+ if (asset_library_ref.type != ASSET_LIBRARY_LOCAL) {
uiItemO(row, "", ICON_FILE_REFRESH, "ASSET_OT_list_refresh");
}
- ED_assetlist_storage_fetch(&asset_library, C);
- ED_assetlist_ensure_previews_job(&asset_library, C);
- const int tot_items = ED_assetlist_size(&asset_library);
+ ED_assetlist_storage_fetch(&asset_library_ref, C);
+ ED_assetlist_ensure_previews_job(&asset_library_ref, C);
+ const int tot_items = ED_assetlist_size(&asset_library_ref);
asset_view_template_refresh_asset_collection(
- asset_library, *filter_settings, *assets_dataptr, assets_propname);
+ asset_library_ref, *filter_settings, *assets_dataptr, assets_propname);
AssetViewListData *list_data = (AssetViewListData *)MEM_mallocN(sizeof(*list_data),
"AssetViewListData");
- list_data->asset_library = asset_library;
+ list_data->asset_library_ref = asset_library_ref;
list_data->screen = CTX_wm_screen(C);
/* TODO can we have some kind of model-view API to handle referencing, filtering and lazy loading
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 766840909cc..0e5a6a79137 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -84,6 +84,8 @@
#include "ED_screen.h"
#include "ED_undo.h"
+#include "RE_engine.h"
+
#include "RNA_access.h"
#include "WM_api.h"
@@ -2621,6 +2623,72 @@ static void constraint_active_func(bContext *UNUSED(C), void *ob_v, void *con_v)
ED_object_constraint_active_set(ob_v, con_v);
}
+static void constraint_ops_extra_draw(bContext *C, uiLayout *layout, void *con_v)
+{
+ PointerRNA op_ptr;
+ uiLayout *row;
+ bConstraint *con = (bConstraint *)con_v;
+
+ PointerRNA ptr;
+ Object *ob = ED_object_active_context(C);
+
+ RNA_pointer_create(&ob->id, &RNA_Constraint, con, &ptr);
+ uiLayoutSetContextPointer(layout, "constraint", &ptr);
+ uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT);
+
+ uiLayoutSetUnitsX(layout, 4.0f);
+
+ /* Apply. */
+ uiItemO(layout,
+ CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Apply"),
+ ICON_CHECKMARK,
+ "CONSTRAINT_OT_apply");
+
+ /* Duplicate. */
+ uiItemO(layout,
+ CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Duplicate"),
+ ICON_DUPLICATE,
+ "CONSTRAINT_OT_copy");
+
+ uiItemO(layout,
+ CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy to Selected"),
+ 0,
+ "CONSTRAINT_OT_copy_to_selected");
+
+ uiItemS(layout);
+
+ /* Move to first. */
+ row = uiLayoutColumn(layout, false);
+ uiItemFullO(row,
+ "CONSTRAINT_OT_move_to_index",
+ IFACE_("Move to First"),
+ ICON_TRIA_UP,
+ NULL,
+ WM_OP_INVOKE_DEFAULT,
+ 0,
+ &op_ptr);
+ RNA_int_set(&op_ptr, "index", 0);
+ if (!con->prev) {
+ uiLayoutSetEnabled(row, false);
+ }
+
+ /* Move to last. */
+ row = uiLayoutColumn(layout, false);
+ uiItemFullO(row,
+ "CONSTRAINT_OT_move_to_index",
+ IFACE_("Move to Last"),
+ ICON_TRIA_DOWN,
+ NULL,
+ WM_OP_INVOKE_DEFAULT,
+ 0,
+ &op_ptr);
+ ListBase *constraint_list = ED_object_constraint_list_from_constraint(ob, con, NULL);
+ RNA_int_set(&op_ptr, "index", BLI_listbase_count(constraint_list) - 1);
+ if (!con->next) {
+ uiLayoutSetEnabled(row, false);
+ }
+}
+
static void draw_constraint_header(uiLayout *layout, Object *ob, bConstraint *con)
{
bPoseChannel *pchan = BKE_pose_channel_active(ob);
@@ -2652,11 +2720,13 @@ static void draw_constraint_header(uiLayout *layout, Object *ob, bConstraint *co
UI_block_emboss_set(block, UI_EMBOSS);
+ uiLayout *row = uiLayoutRow(layout, true);
+
if (proxy_protected == 0) {
- uiItemR(layout, &ptr, "name", 0, "", ICON_NONE);
+ uiItemR(row, &ptr, "name", 0, "", ICON_NONE);
}
else {
- uiItemL(layout, con->name, ICON_NONE);
+ uiItemL(row, con->name, ICON_NONE);
}
/* proxy-protected constraints cannot be edited, so hide up/down + close buttons */
@@ -2697,22 +2767,22 @@ static void draw_constraint_header(uiLayout *layout, Object *ob, bConstraint *co
UI_block_emboss_set(block, UI_EMBOSS);
}
else {
- /* enabled */
- UI_block_emboss_set(block, UI_EMBOSS_NONE_OR_STATUS);
- uiItemR(layout, &ptr, "mute", 0, "", 0);
- UI_block_emboss_set(block, UI_EMBOSS);
+ /* Enabled eye icon. */
+ uiItemR(row, &ptr, "enabled", 0, "", ICON_NONE);
- uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT);
+ /* Extra operators menu. */
+ uiItemMenuF(row, "", ICON_DOWNARROW_HLT, constraint_ops_extra_draw, con);
/* Close 'button' - emboss calls here disable drawing of 'button' behind X */
- UI_block_emboss_set(block, UI_EMBOSS_NONE);
- uiItemO(layout, "", ICON_X, "CONSTRAINT_OT_delete");
- UI_block_emboss_set(block, UI_EMBOSS);
-
- /* Some extra padding at the end, so the 'x' icon isn't too close to drag button. */
- uiItemS(layout);
+ sub = uiLayoutRow(row, false);
+ uiLayoutSetEmboss(sub, UI_EMBOSS_NONE);
+ uiLayoutSetOperatorContext(sub, WM_OP_INVOKE_DEFAULT);
+ uiItemO(sub, "", ICON_X, "CONSTRAINT_OT_delete");
}
+ /* Some extra padding at the end, so the 'x' icon isn't too close to drag button. */
+ uiItemS(layout);
+
/* Set but-locks for protected settings (magic numbers are used here!) */
if (proxy_protected) {
UI_block_lock_set(block, true, TIP_("Cannot edit Proxy-Protected Constraint"));
@@ -6395,6 +6465,41 @@ void uiTemplateCacheFile(uiLayout *layout,
row = uiLayoutRow(layout, false);
uiItemR(row, &fileptr, "is_sequence", 0, NULL, ICON_NONE);
+ /* Only enable render procedural option if the active engine supports it. */
+ const struct RenderEngineType *engine_type = CTX_data_engine_type(C);
+
+ Scene *scene = CTX_data_scene(C);
+ const bool engine_supports_procedural = RE_engine_supports_alembic_procedural(engine_type,
+ scene);
+
+ if (!engine_supports_procedural) {
+ row = uiLayoutRow(layout, false);
+ /* For Cycles, verify that experimental features are enabled. */
+ if (BKE_scene_uses_cycles(scene) && !BKE_scene_uses_cycles_experimental_features(scene)) {
+ uiItemL(row,
+ "The Cycles Alembic Procedural is only available with the experimental feature set",
+ ICON_INFO);
+ }
+ else {
+ uiItemL(row, "The active render engine does not have an Alembic Procedural", ICON_INFO);
+ }
+ }
+
+ row = uiLayoutRow(layout, false);
+ uiLayoutSetActive(row, engine_supports_procedural);
+ uiItemR(row, &fileptr, "use_render_procedural", 0, NULL, ICON_NONE);
+
+ const bool use_render_procedural = RNA_boolean_get(&fileptr, "use_render_procedural");
+ const bool use_prefetch = RNA_boolean_get(&fileptr, "use_prefetch");
+
+ row = uiLayoutRow(layout, false);
+ uiLayoutSetEnabled(row, use_render_procedural);
+ uiItemR(row, &fileptr, "use_prefetch", 0, NULL, ICON_NONE);
+
+ sub = uiLayoutRow(layout, false);
+ uiLayoutSetEnabled(sub, use_prefetch && use_render_procedural);
+ uiItemR(sub, &fileptr, "prefetch_cache_size", 0, NULL, ICON_NONE);
+
row = uiLayoutRowWithHeading(layout, true, IFACE_("Override Frame"));
sub = uiLayoutRow(row, true);
uiLayoutSetPropDecorate(sub, false);
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c
index d3481c449ac..48f638dac33 100644
--- a/source/blender/editors/interface/interface_widgets.c
+++ b/source/blender/editors/interface/interface_widgets.c
@@ -1576,11 +1576,6 @@ float UI_text_clip_middle_ex(const uiFontStyle *fstyle,
/* need to set this first */
UI_fontstyle_set(fstyle);
- if (fstyle->kerning == 1) {
- /* for BLF_width */
- BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
- }
-
float strwidth = BLF_width(fstyle->uifont_id, str, max_len);
if ((okwidth > 0.0f) && (strwidth > okwidth)) {
@@ -1674,10 +1669,6 @@ float UI_text_clip_middle_ex(const uiFontStyle *fstyle,
strwidth = BLF_width(fstyle->uifont_id, str, max_len);
}
- if (fstyle->kerning == 1) {
- BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
- }
-
BLI_assert(strwidth <= okwidth);
return strwidth;
@@ -1736,11 +1727,6 @@ static void ui_text_clip_cursor(const uiFontStyle *fstyle, uiBut *but, const rct
/* need to set this first */
UI_fontstyle_set(fstyle);
- if (fstyle->kerning == 1) {
- /* for BLF_width */
- BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
- }
-
/* define ofs dynamically */
if (but->ofs > but->pos) {
but->ofs = but->pos;
@@ -1785,10 +1771,6 @@ static void ui_text_clip_cursor(const uiFontStyle *fstyle, uiBut *but, const rct
}
}
}
-
- if (fstyle->kerning == 1) {
- BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
- }
}
/**
@@ -1806,11 +1788,6 @@ static void ui_text_clip_right_label(const uiFontStyle *fstyle, uiBut *but, cons
/* need to set this first */
UI_fontstyle_set(fstyle);
- if (fstyle->kerning == 1) {
- /* for BLF_width */
- BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
- }
-
but->strwidth = BLF_width(fstyle->uifont_id, but->drawstr, sizeof(but->drawstr));
but->ofs = 0;
@@ -1870,10 +1847,6 @@ static void ui_text_clip_right_label(const uiFontStyle *fstyle, uiBut *but, cons
but->strwidth = strwidth;
but->drawstr[drawstr_len] = 0;
}
-
- if (fstyle->kerning == 1) {
- BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
- }
}
#ifdef WITH_INPUT_IME
@@ -1985,11 +1958,6 @@ static void widget_draw_text(const uiFontStyle *fstyle,
align = UI_STYLE_TEXT_CENTER;
}
- if (fstyle->kerning == 1) {
- /* for BLF_width */
- BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
- }
-
/* Special case: when we're entering text for multiple buttons,
* don't draw the text for any of the multi-editing buttons */
if (UNLIKELY(but->flag & UI_BUT_DRAG_MULTI)) {
@@ -2151,10 +2119,6 @@ static void widget_draw_text(const uiFontStyle *fstyle,
#endif
}
- if (fstyle->kerning == 1) {
- BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
- }
-
#if 0
ui_rasterpos_safe(x, y, but->aspect);
transopts = ui_translate_buttons();
@@ -2232,10 +2196,6 @@ static void widget_draw_text(const uiFontStyle *fstyle,
}
if (ul_index != -1) {
- if (fstyle->kerning == 1) {
- BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
- }
-
int ul_width = round_fl_to_int(BLF_width(fstyle->uifont_id, "_", 2));
struct UnderlineData ul_data = {
@@ -2256,10 +2216,6 @@ static void widget_draw_text(const uiFontStyle *fstyle,
BLF_position(fstyle->uifont_id, pos_x, pos_y, 0.0f);
BLF_color4ubv(fstyle->uifont_id, wcol->text);
BLF_draw(fstyle->uifont_id, "_", 2);
-
- if (fstyle->kerning == 1) {
- BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
- }
}
}
}
@@ -5369,11 +5325,6 @@ void ui_draw_menu_item(const uiFontStyle *fstyle,
/* need to set this first */
UI_fontstyle_set(fstyle);
- if (fstyle->kerning == 1) {
- /* for BLF_width */
- BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
- }
-
if (separator_type == UI_MENU_ITEM_SEPARATOR_SHORTCUT) {
/* Shrink rect to exclude the shortcut string. */
rect->xmax -= BLF_width(fstyle->uifont_id, cpoin + 1, INT_MAX) + UI_DPI_ICON_SIZE;
@@ -5398,10 +5349,6 @@ void ui_draw_menu_item(const uiFontStyle *fstyle,
else {
BLI_assert_msg(0, "Unknwon menu item separator type");
}
-
- if (fstyle->kerning == 1) {
- BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT);
- }
}
}
diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c
index 12890552b1d..bbff37221e8 100644
--- a/source/blender/editors/io/io_alembic.c
+++ b/source/blender/editors/io/io_alembic.c
@@ -615,6 +615,7 @@ static void ui_alembic_import_settings(uiLayout *layout, PointerRNA *imfptr)
uiItemR(col, imfptr, "set_frame_range", 0, NULL, ICON_NONE);
uiItemR(col, imfptr, "is_sequence", 0, NULL, ICON_NONE);
uiItemR(col, imfptr, "validate_meshes", 0, NULL, ICON_NONE);
+ uiItemR(col, imfptr, "always_add_cache_reader", 0, NULL, ICON_NONE);
}
static void wm_alembic_import_draw(bContext *UNUSED(C), wmOperator *op)
@@ -645,6 +646,7 @@ static int wm_alembic_import_exec(bContext *C, wmOperator *op)
const bool is_sequence = RNA_boolean_get(op->ptr, "is_sequence");
const bool set_frame_range = RNA_boolean_get(op->ptr, "set_frame_range");
const bool validate_meshes = RNA_boolean_get(op->ptr, "validate_meshes");
+ const bool always_add_cache_reader = RNA_boolean_get(op->ptr, "always_add_cache_reader");
const bool as_background_job = RNA_boolean_get(op->ptr, "as_background_job");
int offset = 0;
@@ -672,6 +674,7 @@ static int wm_alembic_import_exec(bContext *C, wmOperator *op)
sequence_len,
offset,
validate_meshes,
+ always_add_cache_reader,
as_background_job);
return as_background_job || ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
@@ -722,6 +725,13 @@ void WM_OT_alembic_import(wmOperatorType *ot)
"Check imported mesh objects for invalid data (slow)");
RNA_def_boolean(ot->srna,
+ "always_add_cache_reader",
+ false,
+ "Always Add Cache Reader",
+ "Add cache modifiers and constraints to imported objects even if they are not "
+ "animated so that they can be updated when reloading the Alembic archive");
+
+ RNA_def_boolean(ot->srna,
"is_sequence",
false,
"Is Sequence",
diff --git a/source/blender/editors/io/io_ops.c b/source/blender/editors/io/io_ops.c
index 9fa34a1c55d..b2788ee49a2 100644
--- a/source/blender/editors/io/io_ops.c
+++ b/source/blender/editors/io/io_ops.c
@@ -52,6 +52,7 @@ void ED_operatortypes_io(void)
WM_operatortype_append(WM_OT_alembic_export);
#endif
#ifdef WITH_USD
+ WM_operatortype_append(WM_OT_usd_import);
WM_operatortype_append(WM_OT_usd_export);
#endif
diff --git a/source/blender/editors/io/io_usd.c b/source/blender/editors/io/io_usd.c
index 0eadb38abb5..d0007d9e5be 100644
--- a/source/blender/editors/io/io_usd.c
+++ b/source/blender/editors/io/io_usd.c
@@ -22,23 +22,30 @@
*/
#ifdef WITH_USD
+# include "DNA_modifier_types.h"
# include "DNA_space_types.h"
+# include <string.h>
# include "BKE_context.h"
# include "BKE_main.h"
# include "BKE_report.h"
+# include "BLI_blenlib.h"
# include "BLI_path_util.h"
# include "BLI_string.h"
# include "BLI_utildefines.h"
# include "BLT_translation.h"
+# include "ED_object.h"
+
# include "MEM_guardedalloc.h"
# include "RNA_access.h"
# include "RNA_define.h"
+# include "RNA_enum_types.h"
+
# include "UI_interface.h"
# include "UI_resources.h"
@@ -50,6 +57,8 @@
# include "io_usd.h"
# include "usd.h"
+# include "stdio.h"
+
const EnumPropertyItem rna_enum_usd_export_evaluation_mode_items[] = {
{DAG_EVAL_RENDER,
"RENDER",
@@ -242,4 +251,274 @@ void WM_OT_usd_export(struct wmOperatorType *ot)
"are different settings for viewport and rendering");
}
+/* ====== USD Import ====== */
+
+static int wm_usd_import_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ eUSDOperatorOptions *options = MEM_callocN(sizeof(eUSDOperatorOptions), "eUSDOperatorOptions");
+ options->as_background_job = true;
+ op->customdata = options;
+
+ return WM_operator_filesel(C, op, event);
+}
+
+static int wm_usd_import_exec(bContext *C, wmOperator *op)
+{
+ if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
+ BKE_report(op->reports, RPT_ERROR, "No filename given");
+ return OPERATOR_CANCELLED;
+ }
+
+ char filename[FILE_MAX];
+ RNA_string_get(op->ptr, "filepath", filename);
+
+ eUSDOperatorOptions *options = (eUSDOperatorOptions *)op->customdata;
+ const bool as_background_job = (options != NULL && options->as_background_job);
+ MEM_SAFE_FREE(op->customdata);
+
+ const float scale = RNA_float_get(op->ptr, "scale");
+
+ const bool set_frame_range = RNA_boolean_get(op->ptr, "set_frame_range");
+
+ const bool read_mesh_uvs = RNA_boolean_get(op->ptr, "read_mesh_uvs");
+ const bool read_mesh_colors = RNA_boolean_get(op->ptr, "read_mesh_colors");
+
+ char mesh_read_flag = MOD_MESHSEQ_READ_VERT | MOD_MESHSEQ_READ_POLY;
+ if (read_mesh_uvs) {
+ mesh_read_flag |= MOD_MESHSEQ_READ_UV;
+ }
+ if (read_mesh_colors) {
+ mesh_read_flag |= MOD_MESHSEQ_READ_COLOR;
+ }
+
+ const bool import_cameras = RNA_boolean_get(op->ptr, "import_cameras");
+ const bool import_curves = RNA_boolean_get(op->ptr, "import_curves");
+ const bool import_lights = RNA_boolean_get(op->ptr, "import_lights");
+ const bool import_materials = RNA_boolean_get(op->ptr, "import_materials");
+ const bool import_meshes = RNA_boolean_get(op->ptr, "import_meshes");
+ const bool import_volumes = RNA_boolean_get(op->ptr, "import_volumes");
+
+ const bool import_subdiv = RNA_boolean_get(op->ptr, "import_subdiv");
+
+ const bool import_instance_proxies = RNA_boolean_get(op->ptr, "import_instance_proxies");
+
+ const bool import_visible_only = RNA_boolean_get(op->ptr, "import_visible_only");
+
+ const bool create_collection = RNA_boolean_get(op->ptr, "create_collection");
+
+ char *prim_path_mask = malloc(1024);
+ RNA_string_get(op->ptr, "prim_path_mask", prim_path_mask);
+
+ const bool import_guide = RNA_boolean_get(op->ptr, "import_guide");
+ const bool import_proxy = RNA_boolean_get(op->ptr, "import_proxy");
+ const bool import_render = RNA_boolean_get(op->ptr, "import_render");
+
+ const bool import_usd_preview = RNA_boolean_get(op->ptr, "import_usd_preview");
+ const bool set_material_blend = RNA_boolean_get(op->ptr, "set_material_blend");
+
+ const float light_intensity_scale = RNA_float_get(op->ptr, "light_intensity_scale");
+
+ /* TODO(makowalski): Add support for sequences. */
+ const bool is_sequence = false;
+ int offset = 0;
+ int sequence_len = 1;
+
+ /* Switch out of edit mode to avoid being stuck in it (T54326). */
+ Object *obedit = CTX_data_edit_object(C);
+ if (obedit) {
+ ED_object_mode_set(C, OB_MODE_EDIT);
+ }
+
+ const bool validate_meshes = false;
+ const bool use_instancing = false;
+
+ struct USDImportParams params = {.scale = scale,
+ .is_sequence = is_sequence,
+ .set_frame_range = set_frame_range,
+ .sequence_len = sequence_len,
+ .offset = offset,
+ .validate_meshes = validate_meshes,
+ .mesh_read_flag = mesh_read_flag,
+ .import_cameras = import_cameras,
+ .import_curves = import_curves,
+ .import_lights = import_lights,
+ .import_materials = import_materials,
+ .import_meshes = import_meshes,
+ .import_volumes = import_volumes,
+ .prim_path_mask = prim_path_mask,
+ .import_subdiv = import_subdiv,
+ .import_instance_proxies = import_instance_proxies,
+ .create_collection = create_collection,
+ .import_guide = import_guide,
+ .import_proxy = import_proxy,
+ .import_render = import_render,
+ .import_visible_only = import_visible_only,
+ .use_instancing = use_instancing,
+ .import_usd_preview = import_usd_preview,
+ .set_material_blend = set_material_blend,
+ .light_intensity_scale = light_intensity_scale};
+
+ const bool ok = USD_import(C, filename, &params, as_background_job);
+
+ return as_background_job || ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
+}
+
+static void wm_usd_import_draw(bContext *UNUSED(C), wmOperator *op)
+{
+ uiLayout *layout = op->layout;
+ struct PointerRNA *ptr = op->ptr;
+
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+
+ uiLayout *box = uiLayoutBox(layout);
+ uiLayout *col = uiLayoutColumnWithHeading(box, true, IFACE_("Data Types"));
+ uiItemR(col, ptr, "import_cameras", 0, NULL, ICON_NONE);
+ uiItemR(col, ptr, "import_curves", 0, NULL, ICON_NONE);
+ uiItemR(col, ptr, "import_lights", 0, NULL, ICON_NONE);
+ uiItemR(col, ptr, "import_materials", 0, NULL, ICON_NONE);
+ uiItemR(col, ptr, "import_meshes", 0, NULL, ICON_NONE);
+ uiItemR(col, ptr, "import_volumes", 0, NULL, ICON_NONE);
+ uiItemR(box, ptr, "prim_path_mask", 0, NULL, ICON_NONE);
+ uiItemR(box, ptr, "scale", 0, NULL, ICON_NONE);
+
+ box = uiLayoutBox(layout);
+ col = uiLayoutColumnWithHeading(box, true, IFACE_("Mesh Data"));
+ uiItemR(col, ptr, "read_mesh_uvs", 0, NULL, ICON_NONE);
+ uiItemR(col, ptr, "read_mesh_colors", 0, NULL, ICON_NONE);
+ col = uiLayoutColumnWithHeading(box, true, IFACE_("Include"));
+ uiItemR(col, ptr, "import_subdiv", 0, IFACE_("Subdivision"), ICON_NONE);
+ uiItemR(col, ptr, "import_instance_proxies", 0, NULL, ICON_NONE);
+ uiItemR(col, ptr, "import_visible_only", 0, NULL, ICON_NONE);
+ uiItemR(col, ptr, "import_guide", 0, NULL, ICON_NONE);
+ uiItemR(col, ptr, "import_proxy", 0, NULL, ICON_NONE);
+ uiItemR(col, ptr, "import_render", 0, NULL, ICON_NONE);
+
+ col = uiLayoutColumnWithHeading(box, true, IFACE_("Options"));
+ uiItemR(col, ptr, "set_frame_range", 0, NULL, ICON_NONE);
+ uiItemR(col, ptr, "relative_path", 0, NULL, ICON_NONE);
+ uiItemR(col, ptr, "create_collection", 0, NULL, ICON_NONE);
+ uiItemR(box, ptr, "light_intensity_scale", 0, NULL, ICON_NONE);
+
+ box = uiLayoutBox(layout);
+ col = uiLayoutColumnWithHeading(box, true, IFACE_("Experimental"));
+ uiItemR(col, ptr, "import_usd_preview", 0, NULL, ICON_NONE);
+ uiLayoutSetEnabled(col, RNA_boolean_get(ptr, "import_materials"));
+ uiLayout *row = uiLayoutRow(col, true);
+ uiItemR(row, ptr, "set_material_blend", 0, NULL, ICON_NONE);
+ uiLayoutSetEnabled(row, RNA_boolean_get(ptr, "import_usd_preview"));
+}
+
+void WM_OT_usd_import(struct wmOperatorType *ot)
+{
+ ot->name = "Import USD";
+ ot->description = "Import USD stage into current scene";
+ ot->idname = "WM_OT_usd_import";
+
+ ot->invoke = wm_usd_import_invoke;
+ ot->exec = wm_usd_import_exec;
+ ot->poll = WM_operator_winactive;
+ ot->ui = wm_usd_import_draw;
+
+ WM_operator_properties_filesel(ot,
+ FILE_TYPE_FOLDER | FILE_TYPE_USD,
+ FILE_BLENDER,
+ FILE_OPENFILE,
+ WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH | WM_FILESEL_SHOW_PROPS,
+ FILE_DEFAULTDISPLAY,
+ FILE_SORT_ALPHA);
+
+ RNA_def_float(
+ ot->srna,
+ "scale",
+ 1.0f,
+ 0.0001f,
+ 1000.0f,
+ "Scale",
+ "Value by which to enlarge or shrink the objects with respect to the world's origin",
+ 0.0001f,
+ 1000.0f);
+
+ RNA_def_boolean(ot->srna,
+ "set_frame_range",
+ true,
+ "Set Frame Range",
+ "Update the scene's start and end frame to match those of the USD archive");
+
+ RNA_def_boolean(ot->srna, "import_cameras", true, "Cameras", "");
+ RNA_def_boolean(ot->srna, "import_curves", true, "Curves", "");
+ RNA_def_boolean(ot->srna, "import_lights", true, "Lights", "");
+ RNA_def_boolean(ot->srna, "import_materials", true, "Materials", "");
+ RNA_def_boolean(ot->srna, "import_meshes", true, "Meshes", "");
+ RNA_def_boolean(ot->srna, "import_volumes", true, "Volumes", "");
+
+ RNA_def_boolean(ot->srna,
+ "import_subdiv",
+ false,
+ "Import Subdivision Scheme",
+ "Create subdivision surface modifiers based on the USD "
+ "SubdivisionScheme attribute");
+
+ RNA_def_boolean(ot->srna,
+ "import_instance_proxies",
+ true,
+ "Import Instance Proxies",
+ "Create unique Blender objects for USD instances");
+
+ RNA_def_boolean(ot->srna,
+ "import_visible_only",
+ true,
+ "Visible Primitives Only",
+ "Do not import invisible USD primitives. "
+ "Only applies to primitives with a non-animated visibility attribute. "
+ "Primitives with animated visibility will always be imported");
+
+ RNA_def_boolean(ot->srna,
+ "create_collection",
+ false,
+ "Create Collection",
+ "Add all imported objects to a new collection");
+
+ RNA_def_boolean(ot->srna, "read_mesh_uvs", true, "UV Coordinates", "Read mesh UV coordinates");
+
+ RNA_def_boolean(ot->srna, "read_mesh_colors", false, "Vertex Colors", "Read mesh vertex colors");
+
+ RNA_def_string(ot->srna,
+ "prim_path_mask",
+ NULL,
+ 1024,
+ "Path Mask",
+ "Import only the subset of the USD scene rooted at the given primitive");
+
+ RNA_def_boolean(ot->srna, "import_guide", false, "Guide", "Import guide geometry");
+
+ RNA_def_boolean(ot->srna, "import_proxy", true, "Proxy", "Import proxy geometry");
+
+ RNA_def_boolean(ot->srna, "import_render", true, "Render", "Import final render geometry");
+
+ RNA_def_boolean(ot->srna,
+ "import_usd_preview",
+ false,
+ "Import USD Preview",
+ "Convert UsdPreviewSurface shaders to Principled BSDF shader networks");
+
+ RNA_def_boolean(ot->srna,
+ "set_material_blend",
+ true,
+ "Set Material Blend",
+ "If the Import USD Preview option is enabled, "
+ "the material blend method will automatically be set based on the "
+ "shader's opacity and opacityThreshold inputs");
+
+ RNA_def_float(ot->srna,
+ "light_intensity_scale",
+ 1.0f,
+ 0.0001f,
+ 10000.0f,
+ "Light Intensity Scale",
+ "Scale for the intensity of imported lights",
+ 0.0001f,
+ 1000.0f);
+}
+
#endif /* WITH_USD */
diff --git a/source/blender/editors/io/io_usd.h b/source/blender/editors/io/io_usd.h
index 671984b6f34..7424cc0df32 100644
--- a/source/blender/editors/io/io_usd.h
+++ b/source/blender/editors/io/io_usd.h
@@ -26,3 +26,5 @@
struct wmOperatorType;
void WM_OT_usd_export(struct wmOperatorType *ot);
+
+void WM_OT_usd_import(struct wmOperatorType *ot);
diff --git a/source/blender/editors/mask/mask_add.c b/source/blender/editors/mask/mask_add.c
index ad71f4d9da7..110f4541e8f 100644
--- a/source/blender/editors/mask/mask_add.c
+++ b/source/blender/editors/mask/mask_add.c
@@ -521,7 +521,7 @@ static int add_vertex_exec(bContext *C, wmOperator *op)
MaskLayer *mask_layer = BKE_mask_layer_active(mask);
- if (mask_layer && mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+ if (mask_layer && mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
mask_layer = NULL;
}
diff --git a/source/blender/editors/mask/mask_draw.c b/source/blender/editors/mask/mask_draw.c
index de8ea8e21eb..22232e9c87e 100644
--- a/source/blender/editors/mask/mask_draw.c
+++ b/source/blender/editors/mask/mask_draw.c
@@ -191,7 +191,7 @@ static void draw_spline_points(const bContext *C,
const char draw_type)
{
const bool is_spline_sel = (spline->flag & SELECT) &&
- (mask_layer->restrictflag & MASK_RESTRICT_SELECT) == 0;
+ (mask_layer->visibility_flag & MASK_HIDE_SELECT) == 0;
const bool is_smooth = (draw_flag & MASK_DRAWFLAG_SMOOTH) != 0;
uchar rgb_spline[4];
@@ -529,7 +529,7 @@ static void draw_spline_curve(const bContext *C,
uchar rgb_tmp[4];
const bool is_spline_sel = (spline->flag & SELECT) &&
- (mask_layer->restrictflag & MASK_RESTRICT_SELECT) == 0;
+ (mask_layer->visibility_flag & MASK_HIDE_SELECT) == 0;
const bool is_smooth = (draw_flag & MASK_DRAWFLAG_SMOOTH) != 0;
const bool is_fill = (spline->flag & MASK_SPLINE_NOFILL) == 0;
@@ -604,7 +604,7 @@ static void draw_mask_layers(const bContext *C,
mask_layer = mask_layer->next, i++) {
const bool is_active = (i == mask->masklay_act);
- if (mask_layer->restrictflag & MASK_RESTRICT_VIEW) {
+ if (mask_layer->visibility_flag & MASK_HIDE_VIEW) {
continue;
}
@@ -613,7 +613,7 @@ static void draw_mask_layers(const bContext *C,
/* draw curve itself first... */
draw_spline_curve(C, mask_layer, spline, draw_flag, draw_type, is_active, width, height);
- if (!(mask_layer->restrictflag & MASK_RESTRICT_SELECT)) {
+ if (!(mask_layer->visibility_flag & MASK_HIDE_SELECT)) {
/* ...and then handles over the curve so they're nicely visible */
draw_spline_points(C, mask_layer, spline, draw_flag, draw_type);
}
diff --git a/source/blender/editors/mask/mask_ops.c b/source/blender/editors/mask/mask_ops.c
index 6fa7457ce14..fd5925bbd0c 100644
--- a/source/blender/editors/mask/mask_ops.c
+++ b/source/blender/editors/mask/mask_ops.c
@@ -287,7 +287,7 @@ static bool spline_under_mouse_get(const bContext *C,
}
for (MaskLayer *mask_layer = mask->masklayers.first; mask_layer != NULL;
mask_layer = mask_layer->next) {
- if (mask_layer->restrictflag & MASK_RESTRICT_SELECT) {
+ if (mask_layer->visibility_flag & MASK_HIDE_SELECT) {
continue;
}
@@ -1322,7 +1322,7 @@ static int cyclic_toggle_exec(bContext *C, wmOperator *UNUSED(op))
Mask *mask = CTX_data_edit_mask(C);
LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) {
- if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+ if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
@@ -1403,7 +1403,7 @@ static int delete_exec(bContext *C, wmOperator *UNUSED(op))
MaskSpline *spline;
int mask_layer_shape_ofs = 0;
- if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+ if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
@@ -1523,7 +1523,7 @@ static int mask_switch_direction_exec(bContext *C, wmOperator *UNUSED(op))
LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) {
bool changed_layer = false;
- if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+ if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
@@ -1581,7 +1581,7 @@ static int mask_normals_make_consistent_exec(bContext *C, wmOperator *UNUSED(op)
LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) {
bool changed_layer = false;
- if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+ if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
@@ -1642,7 +1642,7 @@ static int set_handle_type_exec(bContext *C, wmOperator *op)
bool changed = false;
LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) {
- if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+ if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
@@ -1724,9 +1724,9 @@ static int mask_hide_view_clear_exec(bContext *C, wmOperator *op)
LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) {
- if (mask_layer->restrictflag & OB_RESTRICT_VIEWPORT) {
+ if (mask_layer->visibility_flag & OB_HIDE_VIEWPORT) {
ED_mask_layer_select_set(mask_layer, select);
- mask_layer->restrictflag &= ~OB_RESTRICT_VIEWPORT;
+ mask_layer->visibility_flag &= ~OB_HIDE_VIEWPORT;
changed = true;
}
}
@@ -1766,7 +1766,7 @@ static int mask_hide_view_set_exec(bContext *C, wmOperator *op)
LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) {
- if (mask_layer->restrictflag & MASK_RESTRICT_SELECT) {
+ if (mask_layer->visibility_flag & MASK_HIDE_SELECT) {
continue;
}
@@ -1774,7 +1774,7 @@ static int mask_hide_view_set_exec(bContext *C, wmOperator *op)
if (ED_mask_layer_select_check(mask_layer)) {
ED_mask_layer_select_set(mask_layer, false);
- mask_layer->restrictflag |= OB_RESTRICT_VIEWPORT;
+ mask_layer->visibility_flag |= OB_HIDE_VIEWPORT;
changed = true;
if (mask_layer == BKE_mask_layer_active(mask)) {
BKE_mask_layer_active_set(mask, NULL);
@@ -1783,7 +1783,7 @@ static int mask_hide_view_set_exec(bContext *C, wmOperator *op)
}
else {
if (!ED_mask_layer_select_check(mask_layer)) {
- mask_layer->restrictflag |= OB_RESTRICT_VIEWPORT;
+ mask_layer->visibility_flag |= OB_HIDE_VIEWPORT;
changed = true;
if (mask_layer == BKE_mask_layer_active(mask)) {
BKE_mask_layer_active_set(mask, NULL);
@@ -1825,7 +1825,7 @@ static int mask_feather_weight_clear_exec(bContext *C, wmOperator *UNUSED(op))
bool changed = false;
LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) {
- if (mask_layer->restrictflag & (MASK_RESTRICT_SELECT | MASK_RESTRICT_VIEW)) {
+ if (mask_layer->visibility_flag & (MASK_HIDE_SELECT | MASK_HIDE_VIEW)) {
continue;
}
diff --git a/source/blender/editors/mask/mask_query.c b/source/blender/editors/mask/mask_query.c
index cd51026d20c..e66c4e45e27 100644
--- a/source/blender/editors/mask/mask_query.c
+++ b/source/blender/editors/mask/mask_query.c
@@ -85,7 +85,7 @@ bool ED_mask_find_nearest_diff_point(const bContext *C,
*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->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+ if (mask_layer_orig->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
@@ -245,7 +245,7 @@ MaskSplinePoint *ED_mask_point_find_nearest(const bContext *C,
mask_layer_orig != NULL;
mask_layer_orig = mask_layer_orig->next, mask_layer_eval = mask_layer_eval->next) {
- if (mask_layer_orig->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+ if (mask_layer_orig->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
@@ -409,7 +409,7 @@ bool ED_mask_feather_find_nearest(const bContext *C,
int i, tot_feather_point;
float(*feather_points)[2], (*fp)[2];
- if (mask_layer_orig->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+ if (mask_layer_orig->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
@@ -638,7 +638,7 @@ bool ED_mask_selected_minmax(const bContext *C,
INIT_MINMAX2(min, max);
for (MaskLayer *mask_layer = mask_eval->masklayers.first; mask_layer != NULL;
mask_layer = mask_layer->next) {
- if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+ if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
for (MaskSpline *spline = mask_layer->splines.first; spline != NULL; spline = spline->next) {
diff --git a/source/blender/editors/mask/mask_relationships.c b/source/blender/editors/mask/mask_relationships.c
index 971e1c948c9..9c4740b3087 100644
--- a/source/blender/editors/mask/mask_relationships.c
+++ b/source/blender/editors/mask/mask_relationships.c
@@ -46,7 +46,7 @@ static int mask_parent_clear_exec(bContext *C, wmOperator *UNUSED(op))
Mask *mask = CTX_data_edit_mask(C);
LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) {
- if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+ if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
@@ -138,7 +138,7 @@ static int mask_parent_set_exec(bContext *C, wmOperator *UNUSED(op))
}
LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) {
- if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+ if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
diff --git a/source/blender/editors/mask/mask_select.c b/source/blender/editors/mask/mask_select.c
index 3bb05a27c54..6a1be8dcef3 100644
--- a/source/blender/editors/mask/mask_select.c
+++ b/source/blender/editors/mask/mask_select.c
@@ -68,7 +68,7 @@ bool ED_mask_spline_select_check(const MaskSpline *spline)
bool ED_mask_layer_select_check(const MaskLayer *mask_layer)
{
- if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+ if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
return false;
}
@@ -110,7 +110,7 @@ void ED_mask_spline_select_set(MaskSpline *spline, const bool do_select)
void ED_mask_layer_select_set(MaskLayer *mask_layer, const bool do_select)
{
- if (mask_layer->restrictflag & MASK_RESTRICT_SELECT) {
+ if (mask_layer->visibility_flag & MASK_HIDE_SELECT) {
if (do_select == true) {
return;
}
@@ -134,7 +134,7 @@ void ED_mask_select_toggle_all(Mask *mask, int action)
LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) {
- if (mask_layer->restrictflag & MASK_RESTRICT_VIEW) {
+ if (mask_layer->visibility_flag & MASK_HIDE_VIEW) {
continue;
}
@@ -142,7 +142,7 @@ void ED_mask_select_toggle_all(Mask *mask, int action)
/* we don't have generic functions for this, its restricted to this operator
* if one day we need to re-use such functionality, they can be split out */
- if (mask_layer->restrictflag & MASK_RESTRICT_SELECT) {
+ if (mask_layer->visibility_flag & MASK_HIDE_SELECT) {
continue;
}
LISTBASE_FOREACH (MaskSpline *, spline, &mask_layer->splines) {
@@ -164,9 +164,9 @@ void ED_mask_select_flush_all(Mask *mask)
LISTBASE_FOREACH (MaskSpline *, spline, &mask_layer->splines) {
spline->flag &= ~SELECT;
- /* intentionally _dont_ do this in the mask layer loop
- * so we clear flags on all splines */
- if (mask_layer->restrictflag & MASK_RESTRICT_VIEW) {
+ /* Intentionally *don't* do this in the mask layer loop
+ * so we clear flags on all splines. */
+ if (mask_layer->visibility_flag & MASK_HIDE_VIEW) {
continue;
}
@@ -465,7 +465,7 @@ static int box_select_exec(bContext *C, wmOperator *op)
/* do actual selection */
LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) {
- if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+ if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
@@ -550,7 +550,7 @@ static bool do_lasso_select_mask(bContext *C,
/* do actual selection */
LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) {
- if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+ if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
@@ -691,7 +691,7 @@ static int circle_select_exec(bContext *C, wmOperator *op)
/* do actual selection */
LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) {
- if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+ if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
@@ -820,7 +820,7 @@ static int mask_select_linked_exec(bContext *C, wmOperator *UNUSED(op))
/* do actual selection */
LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) {
- if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+ if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
@@ -870,7 +870,7 @@ static int mask_select_more_less(bContext *C, bool more)
Mask *mask = CTX_data_edit_mask(C);
LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) {
- if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+ if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
diff --git a/source/blender/editors/mask/mask_shapekey.c b/source/blender/editors/mask/mask_shapekey.c
index a5a3489c143..6620096c39a 100644
--- a/source/blender/editors/mask/mask_shapekey.c
+++ b/source/blender/editors/mask/mask_shapekey.c
@@ -144,7 +144,7 @@ static int mask_shape_key_feather_reset_exec(bContext *C, wmOperator *UNUSED(op)
LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) {
- if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+ if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
@@ -238,7 +238,7 @@ static int mask_shape_key_rekey_exec(bContext *C, wmOperator *op)
const bool do_location = RNA_boolean_get(op->ptr, "location");
LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) {
- if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+ if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
diff --git a/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c b/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c
index 38d530ba911..1af489b60ce 100644
--- a/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c
+++ b/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c
@@ -468,7 +468,9 @@ void MESH_GGT_spin(struct wmGizmoGroupType *gzgt)
gzgt->poll = ED_gizmo_poll_or_unlink_delayed_from_tool;
gzgt->setup = gizmo_mesh_spin_init_setup;
- gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag;
+ /* This works well with right click selection but overrides left-mouse selection
+ * when clicking which is needed to create a full 360 degree revolution, see: T89912. */
+ // gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag;
gzgt->refresh = gizmo_mesh_spin_init_refresh;
gzgt->message_subscribe = gizmo_mesh_spin_init_message_subscribe;
gzgt->draw_prepare = gizmo_mesh_spin_init_draw_prepare;
diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c
index 73f6a3f3238..3e3593d18fd 100644
--- a/source/blender/editors/mesh/editmesh_knife.c
+++ b/source/blender/editors/mesh/editmesh_knife.c
@@ -2630,24 +2630,24 @@ static void knife_init_colors(KnifeColors *colors)
}
/* called when modal loop selection gets set up... */
-static void knifetool_init(bContext *C,
+static void knifetool_init(ViewContext *vc,
KnifeTool_OpData *kcd,
const bool only_select,
const bool cut_through,
const bool is_interactive)
{
- Scene *scene = CTX_data_scene(C);
- Object *obedit = CTX_data_edit_object(C);
+ kcd->vc = *vc;
+
+ Scene *scene = vc->scene;
+ Object *obedit = vc->obedit;
/* assign the drawing handle for drawing preview line... */
kcd->scene = scene;
kcd->ob = obedit;
- kcd->region = CTX_wm_region(C);
+ kcd->region = vc->region;
invert_m4_m4_safe_ortho(kcd->ob_imat, kcd->ob->obmat);
- em_setup_viewcontext(C, &kcd->vc);
-
kcd->em = BKE_editmesh_from_object(kcd->ob);
/* cut all the way through the mesh if use_occlude_geometry button not pushed */
@@ -2694,14 +2694,14 @@ static void knifetool_init(bContext *C,
}
/* called when modal loop selection is done... */
-static void knifetool_exit_ex(bContext *C, KnifeTool_OpData *kcd)
+static void knifetool_exit_ex(KnifeTool_OpData *kcd)
{
if (!kcd) {
return;
}
if (kcd->is_interactive) {
- WM_cursor_modal_restore(CTX_wm_window(C));
+ WM_cursor_modal_restore(kcd->vc.win);
/* deactivate the extra drawing stuff in 3D-View */
ED_region_draw_cb_exit(kcd->region->type, kcd->draw_handle);
@@ -2735,10 +2735,10 @@ static void knifetool_exit_ex(bContext *C, KnifeTool_OpData *kcd)
/* destroy kcd itself */
MEM_freeN(kcd);
}
-static void knifetool_exit(bContext *C, wmOperator *op)
+static void knifetool_exit(wmOperator *op)
{
KnifeTool_OpData *kcd = op->customdata;
- knifetool_exit_ex(C, kcd);
+ knifetool_exit_ex(kcd);
op->customdata = NULL;
}
@@ -2827,10 +2827,10 @@ static void knifetool_finish(wmOperator *op)
/** \name Operator (#MESH_OT_knife_tool)
* \{ */
-static void knifetool_cancel(bContext *C, wmOperator *op)
+static void knifetool_cancel(bContext *UNUSED(C), wmOperator *op)
{
/* this is just a wrapper around exit() */
- knifetool_exit(C, op);
+ knifetool_exit(op);
}
wmKeyMap *knifetool_modal_keymap(wmKeyConfig *keyconf)
@@ -2872,7 +2872,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event)
bool do_refresh = false;
if (!obedit || obedit->type != OB_MESH || BKE_editmesh_from_object(obedit) != kcd->em) {
- knifetool_exit(C, op);
+ knifetool_exit(op);
ED_workspace_status_text(C, NULL);
return OPERATOR_FINISHED;
}
@@ -2893,7 +2893,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* finish */
ED_region_tag_redraw(kcd->region);
- knifetool_exit(C, op);
+ knifetool_exit(op);
ED_workspace_status_text(C, NULL);
return OPERATOR_CANCELLED;
@@ -2902,7 +2902,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event)
ED_region_tag_redraw(kcd->region);
knifetool_finish(op);
- knifetool_exit(C, op);
+ knifetool_exit(op);
ED_workspace_status_text(C, NULL);
return OPERATOR_FINISHED;
@@ -3066,8 +3066,11 @@ static int knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event)
const bool cut_through = !RNA_boolean_get(op->ptr, "use_occlude_geometry");
const bool wait_for_input = RNA_boolean_get(op->ptr, "wait_for_input");
+ ViewContext vc;
KnifeTool_OpData *kcd;
+ em_setup_viewcontext(C, &vc);
+
if (only_select) {
Object *obedit = CTX_data_edit_object(C);
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -3080,7 +3083,7 @@ static int knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event)
/* alloc new customdata */
kcd = op->customdata = MEM_callocN(sizeof(KnifeTool_OpData), __func__);
- knifetool_init(C, kcd, only_select, cut_through, true);
+ knifetool_init(&vc, kcd, only_select, cut_through, true);
op->flag |= OP_IS_MODAL_CURSOR_REGION;
@@ -3165,7 +3168,7 @@ static bool edbm_mesh_knife_point_isect(LinkNode *polys, const float cent_ss[2])
/**
* \param use_tag: When set, tag all faces inside the polylines.
*/
-void EDBM_mesh_knife(bContext *C, LinkNode *polys, bool use_tag, bool cut_through)
+void EDBM_mesh_knife(ViewContext *vc, LinkNode *polys, bool use_tag, bool cut_through)
{
KnifeTool_OpData *kcd;
@@ -3176,7 +3179,7 @@ void EDBM_mesh_knife(bContext *C, LinkNode *polys, bool use_tag, bool cut_throug
kcd = MEM_callocN(sizeof(KnifeTool_OpData), __func__);
- knifetool_init(C, kcd, only_select, cut_through, is_interactive);
+ knifetool_init(vc, kcd, only_select, cut_through, is_interactive);
kcd->ignore_edge_snapping = true;
kcd->ignore_vert_snapping = true;
@@ -3313,7 +3316,7 @@ void EDBM_mesh_knife(bContext *C, LinkNode *polys, bool use_tag, bool cut_throug
#undef F_ISECT_SET_OUTSIDE
}
- knifetool_exit_ex(C, kcd);
+ knifetool_exit_ex(kcd);
kcd = NULL;
}
}
diff --git a/source/blender/editors/mesh/editmesh_knife_project.c b/source/blender/editors/mesh/editmesh_knife_project.c
index 09b17acf56d..669a09b3fd3 100644
--- a/source/blender/editors/mesh/editmesh_knife_project.c
+++ b/source/blender/editors/mesh/editmesh_knife_project.c
@@ -32,6 +32,8 @@
#include "BKE_curve.h"
#include "BKE_customdata.h"
#include "BKE_editmesh.h"
+#include "BKE_layer.h"
+#include "BKE_lib_id.h"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_object.h"
@@ -114,7 +116,7 @@ static LinkNode *knifeproject_poly_from_object(const bContext *C,
BKE_nurbList_free(&nurbslist);
if (me_eval_needs_free) {
- BKE_mesh_free((struct Mesh *)me_eval);
+ BKE_id_free(NULL, (ID *)me_eval);
}
}
@@ -124,21 +126,39 @@ static LinkNode *knifeproject_poly_from_object(const bContext *C,
static int knifeproject_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
- Object *obedit = CTX_data_edit_object(C);
- BMEditMesh *em = BKE_editmesh_from_object(obedit);
const bool cut_through = RNA_boolean_get(op->ptr, "cut_through");
LinkNode *polys = NULL;
CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
- if (ob != obedit) {
- polys = knifeproject_poly_from_object(C, scene, ob, polys);
+ if (BKE_object_is_in_editmode(ob)) {
+ continue;
}
+ polys = knifeproject_poly_from_object(C, scene, ob, polys);
}
CTX_DATA_END;
- if (polys) {
- EDBM_mesh_knife(C, polys, true, cut_through);
+ if (polys == NULL) {
+ BKE_report(op->reports,
+ RPT_ERROR,
+ "No other selected objects have wire or boundary edges to use for projection");
+ return OPERATOR_CANCELLED;
+ }
+
+ ViewContext vc;
+ em_setup_viewcontext(C, &vc);
+
+ /* TODO: Ideally meshes would occlude each other, currently they don't
+ * since each knife-project runs as a separate operation. */
+ uint objects_len;
+ Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
+ vc.view_layer, vc.v3d, &objects_len);
+ for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
+ Object *obedit = objects[ob_index];
+ ED_view3d_viewcontext_init_object(&vc, obedit);
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+
+ EDBM_mesh_knife(&vc, polys, true, cut_through);
/* select only tagged faces */
BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false);
@@ -148,16 +168,12 @@ static int knifeproject_exec(bContext *C, wmOperator *op)
BM_mesh_elem_hflag_enable_test(em->bm, BM_FACE, BM_ELEM_SELECT, true, false, BM_ELEM_TAG);
BM_mesh_select_mode_flush(em->bm);
-
- BLI_linklist_freeN(polys);
-
- return OPERATOR_FINISHED;
}
+ MEM_freeN(objects);
+
+ BLI_linklist_freeN(polys);
- BKE_report(op->reports,
- RPT_ERROR,
- "No other selected objects have wire or boundary edges to use for projection");
- return OPERATOR_CANCELLED;
+ return OPERATOR_FINISHED;
}
void MESH_OT_knife_project(wmOperatorType *ot)
diff --git a/source/blender/editors/mesh/editmesh_preselect_edgering.c b/source/blender/editors/mesh/editmesh_preselect_edgering.c
index 43e36957dc9..c58f29917b1 100644
--- a/source/blender/editors/mesh/editmesh_preselect_edgering.c
+++ b/source/blender/editors/mesh/editmesh_preselect_edgering.c
@@ -20,6 +20,8 @@
#include "MEM_guardedalloc.h"
+#include "DNA_userdef_types.h"
+
#include "BLI_math.h"
#include "BLI_stack.h"
@@ -160,16 +162,21 @@ void EDBM_preselect_edgering_draw(struct EditMesh_PreSelEdgeRing *psel, const fl
}
GPU_depth_test(GPU_DEPTH_NONE);
+ GPU_blend(GPU_BLEND_ALPHA);
GPU_matrix_push();
GPU_matrix_mul(matrix);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
- immUniformThemeColor3(TH_GIZMO_PRIMARY);
-
if (psel->edges_len > 0) {
+ float viewport[4];
+ GPU_viewport_size_get_f(viewport);
+
+ immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR);
+ immUniform2fv("viewportSize", &viewport[2]);
+ immUniformThemeColor3(TH_GIZMO_PRIMARY);
+ immUniform1f("lineWidth", U.pixelsize);
immBegin(GPU_PRIM_LINES, psel->edges_len * 2);
for (int i = 0; i < psel->edges_len; i++) {
@@ -178,10 +185,18 @@ void EDBM_preselect_edgering_draw(struct EditMesh_PreSelEdgeRing *psel, const fl
}
immEnd();
+ immUnbindProgram();
}
if (psel->verts_len > 0) {
- GPU_point_size(3.0f);
+ GPU_program_point_size(true);
+ immBindBuiltinProgram(GPU_SHADER_3D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA);
+ immUniformThemeColor3(TH_GIZMO_PRIMARY);
+
+ /* Same size as an edit mode vertex */
+ immUniform1f("size",
+ 2.0 * U.pixelsize *
+ (max_ff(1.0f, UI_GetThemeValuef(TH_VERTEX_SIZE) * (float)M_SQRT2 / 2.0f)));
immBegin(GPU_PRIM_POINTS, psel->verts_len);
@@ -190,14 +205,15 @@ void EDBM_preselect_edgering_draw(struct EditMesh_PreSelEdgeRing *psel, const fl
}
immEnd();
+ immUnbindProgram();
+ GPU_program_point_size(false);
}
- immUnbindProgram();
-
GPU_matrix_pop();
/* Reset default */
GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
+ GPU_blend(GPU_BLEND_NONE);
}
static void view3d_preselect_mesh_edgering_update_verts_from_edge(
diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c
index 830c9abb41e..8e38d41f971 100644
--- a/source/blender/editors/mesh/editmesh_select.c
+++ b/source/blender/editors/mesh/editmesh_select.c
@@ -1860,10 +1860,16 @@ void MESH_OT_loop_select(wmOperatorType *ot)
ot->flag = OPTYPE_UNDO;
/* properties */
- RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", "Extend the selection");
- RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Remove from the selection");
- RNA_def_boolean(ot->srna, "toggle", 0, "Toggle Select", "Toggle the selection");
- RNA_def_boolean(ot->srna, "ring", 0, "Select Ring", "Select ring");
+ PropertyRNA *prop;
+
+ prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", "Extend the selection");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Remove from the selection");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(ot->srna, "toggle", 0, "Toggle Select", "Toggle the selection");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(ot->srna, "ring", 0, "Select Ring", "Select ring");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
void MESH_OT_edgering_select(wmOperatorType *ot)
@@ -1880,10 +1886,16 @@ void MESH_OT_edgering_select(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_UNDO;
- RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection");
- RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Remove from the selection");
- RNA_def_boolean(ot->srna, "toggle", 0, "Toggle Select", "Toggle the selection");
- RNA_def_boolean(ot->srna, "ring", 1, "Select Ring", "Select ring");
+ /* Properties. */
+ PropertyRNA *prop;
+ prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Remove from the selection");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(ot->srna, "toggle", 0, "Toggle Select", "Toggle the selection");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(ot->srna, "ring", 1, "Select Ring", "Select ring");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
/** \} */
diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c
index 41a9f426798..956658bd2b7 100644
--- a/source/blender/editors/mesh/editmesh_tools.c
+++ b/source/blender/editors/mesh/editmesh_tools.c
@@ -467,40 +467,50 @@ static int edbm_delete_exec(bContext *C, wmOperator *op)
BMEditMesh *em = BKE_editmesh_from_object(obedit);
const int type = RNA_enum_get(op->ptr, "type");
- BM_custom_loop_normals_to_vector_layer(em->bm);
-
switch (type) {
case MESH_DELETE_VERT: /* Erase Vertices */
- if (!(em->bm->totvertsel &&
- EDBM_op_callf(em, op, "delete geom=%hv context=%i", BM_ELEM_SELECT, DEL_VERTS))) {
+ if (em->bm->totvertsel == 0) {
+ continue;
+ }
+ BM_custom_loop_normals_to_vector_layer(em->bm);
+ if (!EDBM_op_callf(em, op, "delete geom=%hv context=%i", BM_ELEM_SELECT, DEL_VERTS)) {
continue;
}
break;
case MESH_DELETE_EDGE: /* Erase Edges */
- if (!(em->bm->totedgesel &&
- EDBM_op_callf(em, op, "delete geom=%he context=%i", BM_ELEM_SELECT, DEL_EDGES))) {
+ if (em->bm->totedgesel == 0) {
+ continue;
+ }
+ BM_custom_loop_normals_to_vector_layer(em->bm);
+ if (!EDBM_op_callf(em, op, "delete geom=%he context=%i", BM_ELEM_SELECT, DEL_EDGES)) {
continue;
}
break;
case MESH_DELETE_FACE: /* Erase Faces */
- if (!(em->bm->totfacesel &&
- EDBM_op_callf(em, op, "delete geom=%hf context=%i", BM_ELEM_SELECT, DEL_FACES))) {
+ if (em->bm->totfacesel == 0) {
+ continue;
+ }
+ BM_custom_loop_normals_to_vector_layer(em->bm);
+ if (!EDBM_op_callf(em, op, "delete geom=%hf context=%i", BM_ELEM_SELECT, DEL_FACES)) {
continue;
}
break;
- case MESH_DELETE_EDGE_FACE:
- /* Edges and Faces */
- if (!((em->bm->totedgesel || em->bm->totfacesel) &&
- EDBM_op_callf(
- em, op, "delete geom=%hef context=%i", BM_ELEM_SELECT, DEL_EDGESFACES))) {
+ case MESH_DELETE_EDGE_FACE: /* Edges and Faces */
+ if ((em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) {
+ continue;
+ }
+ BM_custom_loop_normals_to_vector_layer(em->bm);
+ if (!EDBM_op_callf(
+ em, op, "delete geom=%hef context=%i", BM_ELEM_SELECT, DEL_EDGESFACES)) {
continue;
}
break;
- case MESH_DELETE_ONLY_FACE:
- /* Only faces. */
- if (!(em->bm->totfacesel &&
- EDBM_op_callf(
- em, op, "delete geom=%hf context=%i", BM_ELEM_SELECT, DEL_ONLYFACES))) {
+ case MESH_DELETE_ONLY_FACE: /* Only faces. */
+ if (em->bm->totfacesel == 0) {
+ continue;
+ }
+ BM_custom_loop_normals_to_vector_layer(em->bm);
+ if (!EDBM_op_callf(em, op, "delete geom=%hf context=%i", BM_ELEM_SELECT, DEL_ONLYFACES)) {
continue;
}
break;
@@ -2183,6 +2193,61 @@ static bool flip_custom_normals(BMesh *bm, BMLoopNorEditDataArray *lnors_ed_arr)
/* -------------------------------------------------------------------- */
/** \name Flip Normals Operator
* \{ */
+
+static void edbm_flip_normals_custom_loop_normals(Object *obedit, BMEditMesh *em)
+{
+ if (!CustomData_has_layer(&em->bm->ldata, CD_CUSTOMLOOPNORMAL)) {
+ return;
+ }
+
+ /* The mesh has custom normal data, flip them. */
+ BMesh *bm = em->bm;
+
+ BM_lnorspace_update(bm);
+ BMLoopNorEditDataArray *lnors_ed_arr = BM_loop_normal_editdata_array_init(bm, false);
+ BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata;
+
+ for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
+ negate_v3(lnor_ed->nloc);
+
+ BKE_lnor_space_custom_normal_to_data(
+ bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], lnor_ed->nloc, lnor_ed->clnors_data);
+ }
+ BM_loop_normal_editdata_array_free(lnors_ed_arr);
+ EDBM_update(obedit->data,
+ &(const struct EDBMUpdate_Params){
+ .calc_looptri = true,
+ .calc_normals = false,
+ .is_destructive = false,
+ });
+}
+
+static void edbm_flip_normals_face_winding(wmOperator *op, Object *obedit, BMEditMesh *em)
+{
+
+ bool has_flipped_faces = false;
+
+ /* See if we have any custom normals to flip. */
+ BMLoopNorEditDataArray *lnors_ed_arr = flip_custom_normals_init_data(em->bm);
+
+ if (EDBM_op_callf(em, op, "reverse_faces faces=%hf flip_multires=%b", BM_ELEM_SELECT, true)) {
+ has_flipped_faces = true;
+ }
+
+ if (flip_custom_normals(em->bm, lnors_ed_arr) || has_flipped_faces) {
+ EDBM_update(obedit->data,
+ &(const struct EDBMUpdate_Params){
+ .calc_looptri = true,
+ .calc_normals = false,
+ .is_destructive = false,
+ });
+ }
+
+ if (lnors_ed_arr != NULL) {
+ BM_loop_normal_editdata_array_free(lnors_ed_arr);
+ }
+}
+
static int edbm_flip_normals_exec(bContext *C, wmOperator *op)
{
const bool only_clnors = RNA_boolean_get(op->ptr, "only_clnors");
@@ -2197,56 +2262,16 @@ static int edbm_flip_normals_exec(bContext *C, wmOperator *op)
BMEditMesh *em = BKE_editmesh_from_object(obedit);
if (only_clnors) {
- if (CustomData_has_layer(&em->bm->ldata, CD_CUSTOMLOOPNORMAL)) {
- /* The mesh has custom normal data, flip them. */
- BMesh *bm = em->bm;
-
- BM_lnorspace_update(bm);
- BMLoopNorEditDataArray *lnors_ed_arr = BM_loop_normal_editdata_array_init(bm, false);
- BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata;
-
- for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) {
- negate_v3(lnor_ed->nloc);
-
- BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[lnor_ed->loop_index],
- lnor_ed->nloc,
- lnor_ed->clnors_data);
- }
- BM_loop_normal_editdata_array_free(lnors_ed_arr);
- EDBM_update(obedit->data,
- &(const struct EDBMUpdate_Params){
- .calc_looptri = true,
- .calc_normals = false,
- .is_destructive = false,
- });
+ if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) {
+ continue;
}
- continue;
- }
-
- if (em->bm->totfacesel == 0) {
- continue;
- }
-
- bool has_flipped_faces = false;
-
- /* See if we have any custom normals to flip. */
- BMLoopNorEditDataArray *lnors_ed_arr = flip_custom_normals_init_data(em->bm);
-
- if (EDBM_op_callf(em, op, "reverse_faces faces=%hf flip_multires=%b", BM_ELEM_SELECT, true)) {
- has_flipped_faces = true;
- }
-
- if (flip_custom_normals(em->bm, lnors_ed_arr) || has_flipped_faces) {
- EDBM_update(obedit->data,
- &(const struct EDBMUpdate_Params){
- .calc_looptri = true,
- .calc_normals = false,
- .is_destructive = false,
- });
+ edbm_flip_normals_custom_loop_normals(obedit, em);
}
-
- if (lnors_ed_arr != NULL) {
- BM_loop_normal_editdata_array_free(lnors_ed_arr);
+ else {
+ if (em->bm->totfacesel == 0) {
+ continue;
+ }
+ edbm_flip_normals_face_winding(op, obedit, em);
}
}
@@ -2748,9 +2773,6 @@ void MESH_OT_vertices_smooth(wmOperatorType *ot)
static int edbm_do_smooth_laplacian_vertex_exec(bContext *C, wmOperator *op)
{
- BMIter fiter;
- BMFace *f;
- int tot_invalid = 0;
int tot_unselected = 0;
ViewLayer *view_layer = CTX_data_view_layer(C);
@@ -2777,22 +2799,6 @@ static int edbm_do_smooth_laplacian_vertex_exec(bContext *C, wmOperator *op)
if (em->bm->totvertsel == 0) {
tot_unselected++;
- tot_invalid++;
- continue;
- }
-
- bool is_invalid = false;
- /* Check if select faces are triangles. */
- BM_ITER_MESH (f, &fiter, em->bm, BM_FACES_OF_MESH) {
- if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
- if (f->len > 4) {
- tot_invalid++;
- is_invalid = true;
- break;
- }
- }
- }
- if (is_invalid) {
continue;
}
@@ -2841,10 +2847,6 @@ static int edbm_do_smooth_laplacian_vertex_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_WARNING, "No selected vertex");
return OPERATOR_CANCELLED;
}
- if (tot_invalid == objects_len) {
- BKE_report(op->reports, RPT_WARNING, "Selected faces must be triangles or quads");
- return OPERATOR_CANCELLED;
- }
return OPERATOR_FINISHED;
}
@@ -4666,7 +4668,7 @@ static int edbm_separate_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
const int type = RNA_enum_get(op->ptr, "type");
- int retval = 0;
+ bool changed_multi = false;
if (ED_operator_editmesh(C)) {
uint bases_len = 0;
@@ -4676,6 +4678,7 @@ static int edbm_separate_exec(bContext *C, wmOperator *op)
for (uint bs_index = 0; bs_index < bases_len; bs_index++) {
Base *base = bases[bs_index];
BMEditMesh *em = BKE_editmesh_from_object(base->object);
+ bool changed = false;
if (type == 0) {
if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) {
@@ -4690,20 +4693,20 @@ static int edbm_separate_exec(bContext *C, wmOperator *op)
/* editmode separate */
switch (type) {
case MESH_SEPARATE_SELECTED:
- retval = mesh_separate_selected(bmain, scene, view_layer, base, em->bm);
+ changed = mesh_separate_selected(bmain, scene, view_layer, base, em->bm);
break;
case MESH_SEPARATE_MATERIAL:
- retval = mesh_separate_material(bmain, scene, view_layer, base, em->bm);
+ changed = mesh_separate_material(bmain, scene, view_layer, base, em->bm);
break;
case MESH_SEPARATE_LOOSE:
- retval = mesh_separate_loose(bmain, scene, view_layer, base, em->bm);
+ changed = mesh_separate_loose(bmain, scene, view_layer, base, em->bm);
break;
default:
BLI_assert(0);
break;
}
- if (retval) {
+ if (changed) {
EDBM_update(base->object->data,
&(const struct EDBMUpdate_Params){
.calc_looptri = true,
@@ -4711,6 +4714,7 @@ static int edbm_separate_exec(bContext *C, wmOperator *op)
.is_destructive = true,
});
}
+ changed_multi |= changed;
}
MEM_freeN(bases);
}
@@ -4727,7 +4731,7 @@ static int edbm_separate_exec(bContext *C, wmOperator *op)
Mesh *me = ob->data;
if (!ID_IS_LINKED(me)) {
BMesh *bm_old = NULL;
- int retval_iter = 0;
+ bool changed = false;
bm_old = BM_mesh_create(&bm_mesh_allocsize_default,
&((struct BMeshCreateParams){
@@ -4738,17 +4742,17 @@ static int edbm_separate_exec(bContext *C, wmOperator *op)
switch (type) {
case MESH_SEPARATE_MATERIAL:
- retval_iter = mesh_separate_material(bmain, scene, view_layer, base_iter, bm_old);
+ changed = mesh_separate_material(bmain, scene, view_layer, base_iter, bm_old);
break;
case MESH_SEPARATE_LOOSE:
- retval_iter = mesh_separate_loose(bmain, scene, view_layer, base_iter, bm_old);
+ changed = mesh_separate_loose(bmain, scene, view_layer, base_iter, bm_old);
break;
default:
BLI_assert(0);
break;
}
- if (retval_iter) {
+ if (changed) {
BM_mesh_bm_to_me(bmain,
bm_old,
me,
@@ -4762,14 +4766,14 @@ static int edbm_separate_exec(bContext *C, wmOperator *op)
BM_mesh_free(bm_old);
- retval |= retval_iter;
+ changed_multi |= changed;
}
}
}
CTX_DATA_END;
}
- if (retval) {
+ if (changed_multi) {
/* delay depsgraph recalc until all objects are duplicated */
DEG_relations_tag_update(bmain);
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, NULL);
@@ -5572,24 +5576,24 @@ static int edbm_tris_convert_to_quads_exec(bContext *C, wmOperator *op)
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
view_layer, CTX_wm_view3d(C), &objects_len);
- bool is_face_pair;
+ const bool do_seam = RNA_boolean_get(op->ptr, "seam");
+ const bool do_sharp = RNA_boolean_get(op->ptr, "sharp");
+ const bool do_uvs = RNA_boolean_get(op->ptr, "uvs");
+ const bool do_vcols = RNA_boolean_get(op->ptr, "vcols");
+ const bool do_materials = RNA_boolean_get(op->ptr, "materials");
+ float angle_face_threshold, angle_shape_threshold;
+ bool is_face_pair;
{
int totelem_sel[3];
EDBM_mesh_stats_multi(objects, objects_len, NULL, totelem_sel);
is_face_pair = (totelem_sel[2] == 2);
}
- for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
- Object *obedit = objects[ob_index];
-
- BMEditMesh *em = BKE_editmesh_from_object(obedit);
- bool do_seam, do_sharp, do_uvs, do_vcols, do_materials;
- float angle_face_threshold, angle_shape_threshold;
+ /* When joining exactly 2 faces, no limit.
+ * this is useful for one off joins while editing. */
+ {
PropertyRNA *prop;
-
- /* When joining exactly 2 faces, no limit.
- * this is useful for one off joins while editing. */
prop = RNA_struct_find_property(op->ptr, "face_threshold");
if (is_face_pair && (RNA_property_is_set(op->ptr, prop) == false)) {
angle_face_threshold = DEG2RADF(180.0f);
@@ -5605,12 +5609,15 @@ static int edbm_tris_convert_to_quads_exec(bContext *C, wmOperator *op)
else {
angle_shape_threshold = RNA_property_float_get(op->ptr, prop);
}
+ }
+
+ for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
+ Object *obedit = objects[ob_index];
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
- do_seam = RNA_boolean_get(op->ptr, "seam");
- do_sharp = RNA_boolean_get(op->ptr, "sharp");
- do_uvs = RNA_boolean_get(op->ptr, "uvs");
- do_vcols = RNA_boolean_get(op->ptr, "vcols");
- do_materials = RNA_boolean_get(op->ptr, "materials");
+ if (em->bm->totfacesel == 0) {
+ continue;
+ }
BM_custom_loop_normals_to_vector_layer(em->bm);
@@ -6335,7 +6342,7 @@ static int edbm_dissolve_degenerate_exec(bContext *C, wmOperator *op)
BMesh *bm = em->bm;
if (!EDBM_op_callf(em, op, "dissolve_degenerate edges=%he dist=%f", BM_ELEM_SELECT, thresh)) {
- return OPERATOR_CANCELLED;
+ continue;
}
/* tricky to maintain correct selection here, so just flush up from verts */
@@ -8620,7 +8627,7 @@ static int edbm_point_normals_modal(bContext *C, wmOperator *op, const wmEvent *
RNA_enum_set(op->ptr, "mode", mode);
}
- /* Only handle mousemove event in case we are in mouse mode. */
+ /* Only handle mouse-move event in case we are in mouse mode. */
if (event->type == MOUSEMOVE || force_mousemove) {
if (mode == EDBM_CLNOR_POINTTO_MODE_MOUSE) {
ARegion *region = CTX_wm_region(C);
@@ -9527,6 +9534,10 @@ static int edbm_set_normals_from_faces_exec(bContext *C, wmOperator *op)
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
BMesh *bm = em->bm;
+ if (bm->totfacesel == 0) {
+ continue;
+ }
+
BMFace *f;
BMVert *v;
BMEdge *e;
@@ -9538,18 +9549,11 @@ static int edbm_set_normals_from_faces_exec(bContext *C, wmOperator *op)
BKE_editmesh_ensure_autosmooth(em, obedit->data);
BKE_editmesh_lnorspace_update(em, obedit->data);
- float(*vnors)[3] = MEM_callocN(sizeof(*vnors) * bm->totvert, __func__);
- BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
- if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
- BM_ITER_ELEM (v, &viter, f, BM_VERTS_OF_FACE) {
- const int v_index = BM_elem_index_get(v);
- add_v3_v3(vnors[v_index], f->no);
- }
- }
- }
- for (int i = 0; i < bm->totvert; i++) {
- if (!is_zero_v3(vnors[i]) && normalize_v3(vnors[i]) < CLNORS_VALID_VEC_LEN) {
- zero_v3(vnors[i]);
+ float(*vnors)[3] = MEM_mallocN(sizeof(*vnors) * bm->totvert, __func__);
+ {
+ int v_index;
+ BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, v_index) {
+ BM_vert_calc_normal_ex(v, BM_ELEM_SELECT, vnors[v_index]);
}
}
diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c
index fc9e1aa8b1a..f52cd94b8dc 100644
--- a/source/blender/editors/mesh/editmesh_undo.c
+++ b/source/blender/editors/mesh/editmesh_undo.c
@@ -755,11 +755,11 @@ static void undomesh_free_data(UndoMesh *um)
#endif
if (me->key) {
- BKE_key_free(me->key);
+ BKE_key_free_data(me->key);
MEM_freeN(me->key);
}
- BKE_mesh_free(me);
+ BKE_mesh_free_data_for_undo(me);
}
static Object *editmesh_object_from_context(bContext *C)
diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c
index 85c646d689c..c6a8e771362 100644
--- a/source/blender/editors/mesh/editmesh_utils.c
+++ b/source/blender/editors/mesh/editmesh_utils.c
@@ -355,10 +355,7 @@ void EDBM_mesh_clear(BMEditMesh *em)
/* free tessellation data */
em->tottri = 0;
- if (em->looptris) {
- MEM_freeN(em->looptris);
- em->looptris = NULL;
- }
+ MEM_SAFE_FREE(em->looptris);
}
void EDBM_mesh_load(Main *bmain, Object *ob)
diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.c
index b2379610f65..c075d2550cb 100644
--- a/source/blender/editors/mesh/mesh_data.c
+++ b/source/blender/editors/mesh/mesh_data.c
@@ -1007,15 +1007,8 @@ static int mesh_customdata_custom_splitnormals_add_exec(bContext *C, wmOperator
if (me->flag & ME_AUTOSMOOTH) {
float(*polynors)[3] = MEM_mallocN(sizeof(*polynors) * (size_t)me->totpoly, __func__);
- BKE_mesh_calc_normals_poly(me->mvert,
- NULL,
- me->totvert,
- me->mloop,
- me->mpoly,
- me->totloop,
- me->totpoly,
- polynors,
- true);
+ BKE_mesh_calc_normals_poly(
+ me->mvert, me->totvert, me->mloop, me->totloop, me->mpoly, me->totpoly, polynors);
BKE_edges_sharp_from_angle_set(me->mvert,
me->totvert,
diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h
index f25317e8e85..03c99e40d1e 100644
--- a/source/blender/editors/mesh/mesh_intern.h
+++ b/source/blender/editors/mesh/mesh_intern.h
@@ -150,7 +150,10 @@ void MESH_OT_face_split_by_edges(struct wmOperatorType *ot);
/* *** editmesh_knife.c *** */
void MESH_OT_knife_tool(struct wmOperatorType *ot);
void MESH_OT_knife_project(struct wmOperatorType *ot);
-void EDBM_mesh_knife(struct bContext *C, struct LinkNode *polys, bool use_tag, bool cut_through);
+void EDBM_mesh_knife(struct ViewContext *vc,
+ struct LinkNode *polys,
+ bool use_tag,
+ bool cut_through);
struct wmKeyMap *knifetool_modal_keymap(struct wmKeyConfig *keyconf);
diff --git a/source/blender/editors/mesh/mesh_mirror.c b/source/blender/editors/mesh/mesh_mirror.c
index 5eb69aab48b..1a3e6a59588 100644
--- a/source/blender/editors/mesh/mesh_mirror.c
+++ b/source/blender/editors/mesh/mesh_mirror.c
@@ -365,10 +365,7 @@ void ED_mesh_mirrtopo_init(BMEditMesh *em,
void ED_mesh_mirrtopo_free(MirrTopoStore_t *mesh_topo_store)
{
- if (mesh_topo_store->index_lookup) {
- MEM_freeN(mesh_topo_store->index_lookup);
- }
- mesh_topo_store->index_lookup = NULL;
+ MEM_SAFE_FREE(mesh_topo_store->index_lookup);
mesh_topo_store->prev_vert_tot = -1;
mesh_topo_store->prev_edge_tot = -1;
}
diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c
index 8ae74fbfafa..12b52907057 100644
--- a/source/blender/editors/object/object_add.c
+++ b/source/blender/editors/object/object_add.c
@@ -2844,7 +2844,8 @@ static int object_convert_exec(bContext *C, wmOperator *op)
matrix,
0,
use_seams,
- use_faces);
+ use_faces,
+ true);
/* Remove unused materials. */
int actcol = ob_gpencil->actcol;
@@ -3530,7 +3531,7 @@ static int object_add_named_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- basen->object->restrictflag &= ~OB_RESTRICT_VIEWPORT;
+ basen->object->visibility_flag &= ~OB_HIDE_VIEWPORT;
int mval[2];
if (object_add_drop_xy_get(C, op, &mval)) {
diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c
index 43358f51396..0a2df655395 100644
--- a/source/blender/editors/object/object_bake_api.c
+++ b/source/blender/editors/object/object_bake_api.c
@@ -1311,7 +1311,7 @@ static int bake(const BakeAPIRender *bkr,
}
else {
ob_cage_eval = DEG_get_evaluated_object(depsgraph, ob_cage);
- ob_cage_eval->restrictflag |= OB_RESTRICT_RENDER;
+ ob_cage_eval->visibility_flag |= OB_HIDE_RENDER;
ob_cage_eval->base_flag &= ~(BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER);
}
}
@@ -1411,7 +1411,7 @@ static int bake(const BakeAPIRender *bkr,
/* initialize highpoly_data */
highpoly[i].ob = ob_iter;
highpoly[i].ob_eval = DEG_get_evaluated_object(depsgraph, ob_iter);
- highpoly[i].ob_eval->restrictflag &= ~OB_RESTRICT_RENDER;
+ highpoly[i].ob_eval->visibility_flag &= ~OB_HIDE_RENDER;
highpoly[i].ob_eval->base_flag |= (BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER);
highpoly[i].me = BKE_mesh_new_from_object(NULL, highpoly[i].ob_eval, false, false);
@@ -1427,10 +1427,10 @@ static int bake(const BakeAPIRender *bkr,
BLI_assert(i == tot_highpoly);
if (ob_cage != NULL) {
- ob_cage_eval->restrictflag |= OB_RESTRICT_RENDER;
+ ob_cage_eval->visibility_flag |= OB_HIDE_RENDER;
ob_cage_eval->base_flag &= ~(BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER);
}
- ob_low_eval->restrictflag |= OB_RESTRICT_RENDER;
+ ob_low_eval->visibility_flag |= OB_HIDE_RENDER;
ob_low_eval->base_flag &= ~(BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER);
/* populate the pixel arrays with the corresponding face data for each high poly object */
@@ -1473,7 +1473,7 @@ static int bake(const BakeAPIRender *bkr,
}
else {
/* If low poly is not renderable it should have failed long ago. */
- BLI_assert((ob_low_eval->restrictflag & OB_RESTRICT_RENDER) == 0);
+ BLI_assert((ob_low_eval->visibility_flag & OB_HIDE_RENDER) == 0);
if (RE_bake_has_engine(re)) {
ok = RE_bake_engine(re,
diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c
index 4970338973d..e0419e0a4cc 100644
--- a/source/blender/editors/object/object_constraint.c
+++ b/source/blender/editors/object/object_constraint.c
@@ -432,11 +432,8 @@ static void test_constraint(
* free the points array and request a rebind...
*/
if ((data->points == NULL) || (data->numpoints != data->chainlen + 1)) {
- /* free the points array */
- if (data->points) {
- MEM_freeN(data->points);
- data->points = NULL;
- }
+ MEM_SAFE_FREE(data->points);
+ data->numpoints = 0;
/* clear the bound flag, forcing a rebind next time this is evaluated */
data->flag &= ~CONSTRAINT_SPLINEIK_BOUND;
@@ -1486,13 +1483,11 @@ static int constraint_delete_exec(bContext *C, wmOperator *op)
/* free the constraint */
if (BKE_constraint_remove_ex(lb, ob, con, true)) {
- /* there's no active constraint now, so make sure this is the case */
- BKE_constraints_active_set(&ob->constraints, NULL);
/* needed to set the flags on posebones correctly */
ED_object_constraint_update(bmain, ob);
/* relations */
- DEG_relations_tag_update(CTX_data_main(C));
+ DEG_relations_tag_update(bmain);
/* notifiers */
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT | NA_REMOVED, ob);
@@ -1510,10 +1505,10 @@ static int constraint_delete_exec(bContext *C, wmOperator *op)
static int constraint_delete_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
int retval;
- if (edit_constraint_invoke_properties(C, op, event, &retval)) {
- return constraint_delete_exec(C, op);
+ if (!edit_constraint_invoke_properties(C, op, event, &retval)) {
+ return OPERATOR_CANCELLED;
}
- return OPERATOR_CANCELLED;
+ return constraint_delete_exec(C, op);
}
void CONSTRAINT_OT_delete(wmOperatorType *ot)
@@ -1537,6 +1532,320 @@ void CONSTRAINT_OT_delete(wmOperatorType *ot)
/** \} */
/* ------------------------------------------------------------------- */
+/** \name Apply Constraint Operator
+ * \{ */
+
+static int constraint_apply_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Main *bmain = CTX_data_main(C);
+ Object *ob = ED_object_active_context(C);
+ bConstraint *con = edit_constraint_property_get(C, op, ob, 0);
+ bPoseChannel *pchan;
+ ListBase *constraints = ED_object_constraint_list_from_constraint(ob, con, &pchan);
+
+ /* Store name temporarily for report. */
+ char name[MAX_NAME];
+ strcpy(name, con->name);
+ const bool is_first_constraint = con != constraints->first;
+
+ /* Copy the constraint. */
+ bool success;
+ if (pchan) {
+ success = BKE_constraint_apply_and_remove_for_pose(
+ depsgraph, scene, constraints, ob, con, pchan);
+ }
+ else {
+ success = BKE_constraint_apply_and_remove_for_object(depsgraph, scene, constraints, ob, con);
+ }
+
+ if (!success) {
+ /* Couldn't remove due to some invalid data. */
+ return OPERATOR_CANCELLED;
+ }
+
+ /* Update for any children that may get moved. */
+ DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
+
+ /* Needed to set the flags on posebones correctly. */
+ ED_object_constraint_update(bmain, ob);
+
+ DEG_relations_tag_update(bmain);
+ WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT | NA_REMOVED, ob);
+ if (pchan) {
+ WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
+ }
+ else {
+ WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob);
+ }
+
+ if (RNA_boolean_get(op->ptr, "report")) {
+ if (is_first_constraint) {
+ BKE_report(op->reports,
+ RPT_INFO,
+ "Applied constraint was not first, result may not be as expected");
+ }
+ else {
+ /* Only add this report if the operator didn't cause another one. The purpose here is
+ * to alert that something happened, and the previous report will do that anyway. */
+ BKE_reportf(op->reports, RPT_INFO, "Applied constraint: %s", name);
+ }
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+static int constraint_apply_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ int retval;
+ if (!edit_constraint_invoke_properties(C, op, event, &retval)) {
+ return OPERATOR_CANCELLED;
+ }
+ return constraint_apply_exec(C, op);
+}
+
+void CONSTRAINT_OT_apply(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Apply Constraint";
+ ot->idname = "CONSTRAINT_OT_apply";
+ ot->description = "Apply constraint and remove from the stack";
+
+ /* callbacks */
+ ot->invoke = constraint_apply_invoke;
+ ot->exec = constraint_apply_exec;
+ ot->poll = edit_constraint_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ edit_constraint_properties(ot);
+ edit_constraint_report_property(ot);
+}
+
+/** \} */
+
+/* ------------------------------------------------------------------- */
+/** \name Copy Constraint Operator
+ * \{ */
+
+static int constraint_copy_exec(bContext *C, wmOperator *op)
+{
+ Main *bmain = CTX_data_main(C);
+ Object *ob = ED_object_active_context(C);
+ bConstraint *con = edit_constraint_property_get(C, op, ob, 0);
+ bPoseChannel *pchan;
+ ListBase *constraints = ED_object_constraint_list_from_constraint(ob, con, &pchan);
+
+ /* Store name temporarily for report. */
+ char name[MAX_NAME];
+ strcpy(name, con->name);
+
+ /* Copy the constraint. */
+ bConstraint *copy_con;
+ if (pchan) {
+ copy_con = BKE_constraint_copy_for_pose(ob, pchan, con);
+ }
+ else {
+ copy_con = BKE_constraint_copy_for_object(ob, con);
+ }
+
+ if (!copy_con) {
+ /* Couldn't remove due to some invalid data. */
+ return OPERATOR_CANCELLED;
+ }
+ /* Move constraint to correct position. */
+ const int new_index = BLI_findindex(constraints, con) + 1;
+ const int current_index = BLI_findindex(constraints, copy_con);
+ BLI_assert(new_index >= 0);
+ BLI_assert(current_index >= 0);
+ BLI_listbase_link_move(constraints, copy_con, new_index - current_index);
+
+ /* Needed to set the flags on posebones correctly. */
+ ED_object_constraint_update(bmain, ob);
+
+ DEG_relations_tag_update(bmain);
+ WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT | NA_ADDED, ob);
+
+ if (RNA_boolean_get(op->ptr, "report")) {
+ BKE_reportf(op->reports, RPT_INFO, "Copied constraint: %s", name);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+static int constraint_copy_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ int retval;
+ if (!edit_constraint_invoke_properties(C, op, event, &retval)) {
+ return OPERATOR_CANCELLED;
+ }
+ return constraint_copy_exec(C, op);
+}
+
+void CONSTRAINT_OT_copy(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Duplicate Constraint";
+ ot->idname = "CONSTRAINT_OT_copy";
+ ot->description = "Duplicate constraint at the same position in the stack";
+
+ /* callbacks */
+ ot->invoke = constraint_copy_invoke;
+ ot->exec = constraint_copy_exec;
+ ot->poll = edit_constraint_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ edit_constraint_properties(ot);
+ edit_constraint_report_property(ot);
+}
+
+/** \} */
+
+/* ------------------------------------------------------------------- */
+/** \name Copy Constraint To Selected Operator
+ * \{ */
+
+static int constraint_copy_to_selected_exec(bContext *C, wmOperator *op)
+{
+ Main *bmain = CTX_data_main(C);
+ Object *obact = ED_object_active_context(C);
+ bConstraint *con = edit_constraint_property_get(C, op, obact, 0);
+ bPoseChannel *pchan;
+ ED_object_constraint_list_from_constraint(obact, con, &pchan);
+
+ if (pchan) {
+ /* Don't do anything if bone doesn't exist or doesn't have any constraints. */
+ if (pchan->constraints.first == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "No constraints for copying");
+ return OPERATOR_CANCELLED;
+ }
+
+ Object *prev_ob = NULL;
+
+ /* Copy all constraints from active posebone to all selected posebones. */
+ CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, chan, selected_pose_bones, Object *, ob) {
+ /* If we're not handling the object we're copying from, copy all constraints over. */
+ if (pchan == chan) {
+ continue;
+ }
+
+ BKE_constraint_copy_for_pose(ob, chan, con);
+ /* Update flags (need to add here, not just copy). */
+ chan->constflag |= pchan->constflag;
+
+ if (prev_ob == ob) {
+ continue;
+ }
+
+ BKE_pose_tag_recalc(bmain, ob->pose);
+ DEG_id_tag_update((ID *)ob, ID_RECALC_GEOMETRY);
+ prev_ob = ob;
+ }
+ CTX_DATA_END;
+ }
+ else {
+ /* Copy all constraints from active object to all selected objects. */
+ CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) {
+ /* If we're not handling the object we're copying from, copy all constraints over. */
+ if (obact == ob) {
+ continue;
+ }
+
+ BKE_constraint_copy_for_object(ob, con);
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_TRANSFORM);
+ }
+ CTX_DATA_END;
+ }
+
+ /* Force depsgraph to get recalculated since new relationships added. */
+ DEG_relations_tag_update(bmain);
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+static int constraint_copy_to_selected_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ int retval;
+ if (!edit_constraint_invoke_properties(C, op, event, &retval)) {
+ return retval;
+ }
+ return constraint_copy_to_selected_exec(C, op);
+}
+
+static bool constraint_copy_to_selected_poll(bContext *C)
+{
+ PointerRNA ptr = CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint);
+ Object *obact = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C);
+ bConstraint *con = ptr.data;
+ bPoseChannel *pchan;
+ ED_object_constraint_list_from_constraint(obact, con, &pchan);
+
+ if (pchan) {
+ bool found = false;
+ CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, chan, selected_pose_bones, Object *, UNUSED(ob)) {
+ if (pchan != chan) {
+ /** NOTE: Can not return here, because CTX_DATA_BEGIN_WITH_ID allocated
+ * a list that needs to be freed by CTX_DATA_END. */
+ found = true;
+ break;
+ }
+ }
+ CTX_DATA_END;
+ if (found) {
+ return true;
+ }
+
+ CTX_wm_operator_poll_msg_set(C, "No other bones are selected");
+ return false;
+ }
+
+ if (!obact) {
+ CTX_wm_operator_poll_msg_set(C, "No selected object to copy from");
+ return false;
+ }
+
+ bool found = false;
+ CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
+ if (ob != obact) {
+ /** NOTE: Can not return here, because CTX_DATA_BEGIN allocated
+ * a list that needs to be freed by CTX_DATA_END. */
+ found = true;
+ break;
+ }
+ }
+ CTX_DATA_END;
+ if (found) {
+ return true;
+ }
+
+ CTX_wm_operator_poll_msg_set(C, "No other objects are selected");
+ return false;
+}
+
+void CONSTRAINT_OT_copy_to_selected(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Copy Constraint To Selected";
+ ot->idname = "CONSTRAINT_OT_copy_to_selected";
+ ot->description = "Copy constraint to other selected objects/bones";
+
+ /* api callbacks */
+ ot->exec = constraint_copy_to_selected_exec;
+ ot->invoke = constraint_copy_to_selected_invoke;
+ ot->poll = constraint_copy_to_selected_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ edit_constraint_properties(ot);
+}
+
+/** \} */
+
+/* ------------------------------------------------------------------- */
/** \name Move Down Constraint Operator
* \{ */
diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c
index 6108691b2f1..5697c2c973d 100644
--- a/source/blender/editors/object/object_edit.c
+++ b/source/blender/editors/object/object_edit.c
@@ -384,7 +384,7 @@ static int object_hide_collection_exec(bContext *C, wmOperator *op)
DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
if (v3d->flag & V3D_LOCAL_COLLECTIONS) {
- if (lc->runtime_flag & LAYER_COLLECTION_RESTRICT_VIEWPORT) {
+ if (lc->runtime_flag & LAYER_COLLECTION_HIDE_VIEWPORT) {
return OPERATOR_CANCELLED;
}
if (toggle) {
@@ -421,7 +421,7 @@ void ED_collection_hide_menu_draw(const bContext *C, uiLayout *layout)
continue;
}
- if (lc->collection->flag & COLLECTION_RESTRICT_VIEWPORT) {
+ if (lc->collection->flag & COLLECTION_HIDE_VIEWPORT) {
continue;
}
@@ -926,7 +926,7 @@ static bool editmode_toggle_poll(bContext *C)
}
/* if hidden but in edit mode, we still display */
- if ((ob->restrictflag & OB_RESTRICT_VIEWPORT) && !(ob->mode & OB_MODE_EDIT)) {
+ if ((ob->visibility_flag & OB_HIDE_VIEWPORT) && !(ob->mode & OB_MODE_EDIT)) {
return false;
}
diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h
index 6299fdcc7f7..10e016738d0 100644
--- a/source/blender/editors/object/object_intern.h
+++ b/source/blender/editors/object/object_intern.h
@@ -226,6 +226,9 @@ void POSE_OT_ik_add(struct wmOperatorType *ot);
void POSE_OT_ik_clear(struct wmOperatorType *ot);
void CONSTRAINT_OT_delete(struct wmOperatorType *ot);
+void CONSTRAINT_OT_apply(struct wmOperatorType *ot);
+void CONSTRAINT_OT_copy(struct wmOperatorType *ot);
+void CONSTRAINT_OT_copy_to_selected(struct wmOperatorType *ot);
void CONSTRAINT_OT_move_up(struct wmOperatorType *ot);
void CONSTRAINT_OT_move_to_index(struct wmOperatorType *ot);
diff --git a/source/blender/editors/object/object_modes.c b/source/blender/editors/object/object_modes.c
index 36a4f002978..2c58ef02486 100644
--- a/source/blender/editors/object/object_modes.c
+++ b/source/blender/editors/object/object_modes.c
@@ -585,7 +585,7 @@ void OBJECT_OT_transfer_mode(wmOperatorType *ot)
"use_flash_on_transfer",
true,
"Flash On Transfer",
- "Flash the target object when transfering the mode");
+ "Flash the target object when transferring the mode");
}
/** \} */
diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c
index a438c760d3b..c1928cf7f8a 100644
--- a/source/blender/editors/object/object_ops.c
+++ b/source/blender/editors/object/object_ops.c
@@ -183,6 +183,9 @@ void ED_operatortypes_object(void)
WM_operatortype_append(POSE_OT_ik_add);
WM_operatortype_append(POSE_OT_ik_clear);
WM_operatortype_append(CONSTRAINT_OT_delete);
+ WM_operatortype_append(CONSTRAINT_OT_apply);
+ WM_operatortype_append(CONSTRAINT_OT_copy);
+ WM_operatortype_append(CONSTRAINT_OT_copy_to_selected);
WM_operatortype_append(CONSTRAINT_OT_move_up);
WM_operatortype_append(CONSTRAINT_OT_move_down);
WM_operatortype_append(CONSTRAINT_OT_move_to_index);
diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c
index c61965b3e23..ec72ff11683 100644
--- a/source/blender/editors/object/object_relations.c
+++ b/source/blender/editors/object/object_relations.c
@@ -570,6 +570,8 @@ void ED_object_parent_clear(Object *ob, const int type)
/* clear parenting relationship completely */
ob->parent = NULL;
+ ob->partype = PAROBJECT;
+ ob->parsubstr[0] = 0;
break;
}
case CLEAR_PARENT_KEEP_TRANSFORM: {
@@ -2723,24 +2725,53 @@ void OBJECT_OT_make_single_user(wmOperatorType *ot)
/** \name Drop Named Material on Object Operator
* \{ */
+char *ED_object_ot_drop_named_material_tooltip(bContext *C,
+ PointerRNA *properties,
+ const wmEvent *event)
+{
+ Object *ob = ED_view3d_give_object_under_cursor(C, event->mval);
+ if (ob == NULL) {
+ return BLI_strdup("");
+ }
+
+ char name[MAX_ID_NAME - 2];
+ RNA_string_get(properties, "name", name);
+
+ int active_mat_slot = max_ii(ob->actcol, 1);
+ Material *prev_mat = BKE_object_material_get(ob, active_mat_slot);
+
+ char *result;
+ if (prev_mat) {
+ const char *tooltip = TIP_("Drop %s on %s (slot %d, replacing %s)");
+ result = BLI_sprintfN(tooltip, name, ob->id.name + 2, active_mat_slot, prev_mat->id.name + 2);
+ }
+ else {
+ const char *tooltip = TIP_("Drop %s on %s (slot %d)");
+ result = BLI_sprintfN(tooltip, name, ob->id.name + 2, active_mat_slot);
+ }
+ return result;
+}
+
static int drop_named_material_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
Main *bmain = CTX_data_main(C);
- Base *base = ED_view3d_give_base_under_cursor(C, event->mval);
+ Object *ob = ED_view3d_give_object_under_cursor(C, event->mval);
Material *ma;
char name[MAX_ID_NAME - 2];
RNA_string_get(op->ptr, "name", name);
ma = (Material *)BKE_libblock_find_name(bmain, ID_MA, name);
- if (base == NULL || ma == NULL) {
+ if (ob == NULL || ma == NULL) {
return OPERATOR_CANCELLED;
}
- BKE_object_material_assign(CTX_data_main(C), base->object, ma, 1, BKE_MAT_ASSIGN_USERPREF);
+ const short active_mat_slot = ob->actcol;
+
+ BKE_object_material_assign(CTX_data_main(C), ob, ma, active_mat_slot, BKE_MAT_ASSIGN_USERPREF);
- DEG_id_tag_update(&base->object->id, ID_RECALC_TRANSFORM);
+ DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
- WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, base->object);
+ WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, ob);
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_LINKS, ma);
diff --git a/source/blender/editors/object/object_remesh.cc b/source/blender/editors/object/object_remesh.cc
index 82dbc9aaf38..d56cb3c7548 100644
--- a/source/blender/editors/object/object_remesh.cc
+++ b/source/blender/editors/object/object_remesh.cc
@@ -166,9 +166,10 @@ static int voxel_remesh_exec(bContext *C, wmOperator *op)
Mesh *mesh_fixed_poles = BKE_mesh_remesh_voxel_fix_poles(new_mesh);
BKE_id_free(nullptr, new_mesh);
new_mesh = mesh_fixed_poles;
- BKE_mesh_calc_normals(new_mesh);
}
+ BKE_mesh_calc_normals(new_mesh);
+
if (mesh->flag & ME_REMESH_REPROJECT_VOLUME || mesh->flag & ME_REMESH_REPROJECT_PAINT_MASK ||
mesh->flag & ME_REMESH_REPROJECT_SCULPT_FACE_SETS) {
BKE_mesh_runtime_clear_geometry(mesh);
diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c
index f64f95c5322..7a42c9d5d8b 100644
--- a/source/blender/editors/object/object_vgroup.c
+++ b/source/blender/editors/object/object_vgroup.c
@@ -1582,7 +1582,7 @@ static void vgroup_fix(
mag = normalize_v3(norm);
if (mag) { /* zeros fix */
d = -dot_v3v3(norm, coord);
- /* dist = (dot_v3v3(norm, m.co) + d); */ /* UNUSED */
+ // dist = (dot_v3v3(norm, m.co) + d); /* UNUSED */
moveCloserToDistanceFromPlane(
depsgraph, scene_eval, object_eval, me, i, norm, coord, d, distToBe, strength, cp);
}
diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c
index 5a629058c81..8afc5c583e0 100644
--- a/source/blender/editors/physics/particle_edit.c
+++ b/source/blender/editors/physics/particle_edit.c
@@ -2957,10 +2957,7 @@ static int remove_tagged_particles(Object *ob, ParticleSystem *psys, int mirror)
}
edit->points = new_points;
- if (edit->mirror_cache) {
- MEM_freeN(edit->mirror_cache);
- edit->mirror_cache = NULL;
- }
+ MEM_SAFE_FREE(edit->mirror_cache);
if (psys->child) {
MEM_freeN(psys->child);
@@ -3576,10 +3573,7 @@ static void PE_mirror_x(Depsgraph *depsgraph, Scene *scene, Object *ob, int tagg
}
edit->points = new_points;
- if (edit->mirror_cache) {
- MEM_freeN(edit->mirror_cache);
- edit->mirror_cache = NULL;
- }
+ MEM_SAFE_FREE(edit->mirror_cache);
edit->totpoint = psys->totpart = newtotpart;
@@ -4497,10 +4491,7 @@ static int brush_add(const bContext *C, PEData *data, short number)
}
edit->points = new_points;
- if (edit->mirror_cache) {
- MEM_freeN(edit->mirror_cache);
- edit->mirror_cache = NULL;
- }
+ MEM_SAFE_FREE(edit->mirror_cache);
/* create tree for interpolation */
if (pset->flag & PE_INTERPOLATE_ADDED && psys->totpart) {
@@ -4676,7 +4667,7 @@ typedef struct BrushEdit {
int lastmouse[2];
float zfac;
- /* optional cached view settings to avoid setting on every mousemove */
+ /** Optional cached view settings to avoid setting on every mouse-move. */
PEData data;
} BrushEdit;
diff --git a/source/blender/editors/physics/particle_edit_undo.c b/source/blender/editors/physics/particle_edit_undo.c
index 2c7b5c0de6a..601a8385a24 100644
--- a/source/blender/editors/physics/particle_edit_undo.c
+++ b/source/blender/editors/physics/particle_edit_undo.c
@@ -128,10 +128,7 @@ static void undoptcache_to_editcache(PTCacheUndo *undo, PTCacheEdit *edit)
if (edit->points) {
MEM_freeN(edit->points);
}
- if (edit->mirror_cache) {
- MEM_freeN(edit->mirror_cache);
- edit->mirror_cache = NULL;
- }
+ MEM_SAFE_FREE(edit->mirror_cache);
edit->points = MEM_dupallocN(undo->points);
edit->totpoint = undo->totpoint;
diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c
index d3307ebf274..749010a5ba3 100644
--- a/source/blender/editors/render/render_opengl.c
+++ b/source/blender/editors/render/render_opengl.c
@@ -768,7 +768,7 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op)
/* corrects render size with actual size, not every card supports non-power-of-two dimensions */
DRW_opengl_context_enable(); /* Off-screen creation needs to be done in DRW context. */
- ofs = GPU_offscreen_create(sizex, sizey, true, true, err_out);
+ ofs = GPU_offscreen_create(sizex, sizey, true, GPU_RGBA16F, err_out);
DRW_opengl_context_disable();
if (!ofs) {
diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c
index fe1e850dcba..95351de45f0 100644
--- a/source/blender/editors/render/render_preview.c
+++ b/source/blender/editors/render/render_preview.c
@@ -47,6 +47,7 @@
#include "DNA_collection_types.h"
#include "DNA_light_types.h"
#include "DNA_material_types.h"
+#include "DNA_mesh_types.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
@@ -265,6 +266,11 @@ static const char *preview_collection_name(const ePreviewType pr_type)
}
}
+static bool render_engine_supports_ray_visibility(const Scene *sce)
+{
+ return !STREQ(sce->r.engine, RE_engine_id_BLENDER_EEVEE);
+}
+
static void switch_preview_collection_visibilty(ViewLayer *view_layer, const ePreviewType pr_type)
{
/* Set appropriate layer as visible. */
@@ -273,37 +279,68 @@ static void switch_preview_collection_visibilty(ViewLayer *view_layer, const ePr
for (lc = lc->layer_collections.first; lc; lc = lc->next) {
if (STREQ(lc->collection->id.name + 2, collection_name)) {
- lc->collection->flag &= ~COLLECTION_RESTRICT_RENDER;
+ lc->collection->flag &= ~COLLECTION_HIDE_RENDER;
}
else {
- lc->collection->flag |= COLLECTION_RESTRICT_RENDER;
+ lc->collection->flag |= COLLECTION_HIDE_RENDER;
}
}
}
-static void switch_preview_floor_visibility(ViewLayer *view_layer,
+static const char *preview_floor_material_name(const Scene *scene,
+ const ePreviewRenderMethod pr_method)
+{
+ if (pr_method == PR_ICON_RENDER && render_engine_supports_ray_visibility(scene)) {
+ return "FloorHidden";
+ }
+ return "Floor";
+}
+
+static void switch_preview_floor_material(Main *pr_main,
+ Mesh *me,
+ const Scene *scene,
+ const ePreviewRenderMethod pr_method)
+{
+ if (me->totcol == 0) {
+ return;
+ }
+
+ const char *material_name = preview_floor_material_name(scene, pr_method);
+ Material *mat = BLI_findstring(&pr_main->materials, material_name, offsetof(ID, name) + 2);
+ if (mat) {
+ me->mat[0] = mat;
+ }
+}
+
+static void switch_preview_floor_visibility(Main *pr_main,
+ const Scene *scene,
+ ViewLayer *view_layer,
const ePreviewRenderMethod pr_method)
{
/* Hide floor for icon renders. */
LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
if (STREQ(base->object->id.name + 2, "Floor")) {
+ base->object->visibility_flag &= ~OB_HIDE_RENDER;
if (pr_method == PR_ICON_RENDER) {
- base->object->restrictflag |= OB_RESTRICT_RENDER;
+ if (!render_engine_supports_ray_visibility(scene)) {
+ base->object->visibility_flag |= OB_HIDE_RENDER;
+ }
}
- else {
- base->object->restrictflag &= ~OB_RESTRICT_RENDER;
+ if (base->object->type == OB_MESH) {
+ switch_preview_floor_material(pr_main, base->object->data, scene, pr_method);
}
}
}
}
-static void set_preview_visibility(Scene *scene,
+static void set_preview_visibility(Main *pr_main,
+ Scene *scene,
ViewLayer *view_layer,
const ePreviewType pr_type,
const ePreviewRenderMethod pr_method)
{
switch_preview_collection_visibilty(view_layer, pr_type);
- switch_preview_floor_visibility(view_layer, pr_method);
+ switch_preview_floor_visibility(pr_main, scene, view_layer, pr_method);
BKE_layer_collection_sync(scene, view_layer);
}
@@ -357,10 +394,31 @@ static ID *duplicate_ids(ID *id, const bool allow_failure)
}
}
-static World *preview_get_world(Main *pr_main)
+static const char *preview_world_name(const Scene *sce,
+ const ID_Type id_type,
+ const ePreviewRenderMethod pr_method)
+{
+ /* When rendering material icons the floor will not be shown in the output. Cycles will use a
+ * material trick to show the floor in the reflections, but hide the floor for camera rays. For
+ * Eevee we use a transparent world that has a projected grid.
+ *
+ * In the future when Eevee supports vulkan raytracing we can re-evaluate and perhaps remove this
+ * approximation.
+ */
+ if (id_type == ID_MA && pr_method == PR_ICON_RENDER &&
+ !render_engine_supports_ray_visibility(sce)) {
+ return "WorldFloor";
+ }
+ return "World";
+}
+
+static World *preview_get_world(Main *pr_main,
+ const Scene *sce,
+ const ID_Type id_type,
+ const ePreviewRenderMethod pr_method)
{
World *result = NULL;
- const char *world_name = "World";
+ const char *world_name = preview_world_name(sce, id_type, pr_method);
result = BLI_findstring(&pr_main->worlds, world_name, offsetof(ID, name) + 2);
/* No world found return first world. */
@@ -380,9 +438,13 @@ static void preview_sync_exposure(World *dst, const World *src)
dst->range = src->range;
}
-static World *preview_prepare_world(Main *pr_main, const World *world)
+static World *preview_prepare_world(Main *pr_main,
+ const Scene *sce,
+ const World *world,
+ const ID_Type id_type,
+ const ePreviewRenderMethod pr_method)
{
- World *result = preview_get_world(pr_main);
+ World *result = preview_get_world(pr_main, sce, id_type, pr_method);
if (world) {
preview_sync_exposure(result, world);
}
@@ -436,7 +498,7 @@ static Scene *preview_prepare_scene(
sce->r.cfra = scene->r.cfra;
/* Setup the world. */
- sce->world = preview_prepare_world(pr_main, scene->world);
+ sce->world = preview_prepare_world(pr_main, sce, scene->world, id_type, sp->pr_method);
if (id_type == ID_TE) {
/* Texture is not actually rendered with engine, just set dummy value. */
@@ -458,7 +520,7 @@ static Scene *preview_prepare_scene(
/* Use current scene world to light sphere. */
sce->world = preview_get_localized_world(sp, scene->world);
}
- else if (sce->world) {
+ else if (sce->world && sp->pr_method != PR_ICON_RENDER) {
/* Use a default world color. Using the current
* scene world can be slow if it has big textures. */
sce->world->use_nodes = false;
@@ -472,7 +534,7 @@ static Scene *preview_prepare_scene(
sp->pr_main == G_pr_main_grease_pencil) ?
MA_SPHERE_A :
mat->pr_type;
- set_preview_visibility(sce, view_layer, preview_type, sp->pr_method);
+ set_preview_visibility(pr_main, sce, view_layer, preview_type, sp->pr_method);
if (sp->pr_method != PR_ICON_RENDER) {
if (mat->nodetree && sp->pr_method == PR_NODE_RENDER) {
@@ -536,7 +598,7 @@ static Scene *preview_prepare_scene(
BLI_addtail(&pr_main->lights, la);
}
- set_preview_visibility(sce, view_layer, MA_LAMP, sp->pr_method);
+ set_preview_visibility(pr_main, sce, view_layer, MA_LAMP, sp->pr_method);
if (sce->world) {
/* Only use lighting from the light. */
@@ -571,7 +633,7 @@ static Scene *preview_prepare_scene(
BLI_addtail(&pr_main->worlds, wrld);
}
- set_preview_visibility(sce, view_layer, MA_SKY, sp->pr_method);
+ set_preview_visibility(pr_main, sce, view_layer, MA_SKY, sp->pr_method);
sce->world = wrld;
if (wrld && wrld->nodetree && sp->pr_method == PR_NODE_RENDER) {
diff --git a/source/blender/editors/render/render_update.c b/source/blender/editors/render/render_update.c
index fb9d11feb63..8bc2281db73 100644
--- a/source/blender/editors/render/render_update.c
+++ b/source/blender/editors/render/render_update.c
@@ -23,6 +23,7 @@
#include <stdlib.h>
#include <string.h>
+#include "DNA_cachefile_types.h"
#include "DNA_light_types.h"
#include "DNA_material_types.h"
#include "DNA_node_types.h"
@@ -63,7 +64,9 @@
#include <stdio.h>
-/***************************** Render Engines ********************************/
+/* -------------------------------------------------------------------- */
+/** \name Render Engines
+ * \{ */
/* Update 3D viewport render or draw engine on changes to the scene or view settings. */
void ED_render_view3d_update(Depsgraph *depsgraph,
@@ -204,6 +207,19 @@ void ED_render_engine_changed(Main *bmain, const bool update_scene_data)
ntreeCompositUpdateRLayers(scene->nodetree);
}
}
+
+ /* Update #CacheFiles to ensure that procedurals are properly taken into account. */
+ LISTBASE_FOREACH (CacheFile *, cachefile, &bmain->cachefiles) {
+ /* Only update cache-files which are set to use a render procedural.
+ * We do not use #BKE_cachefile_uses_render_procedural here as we need to update regardless of
+ * the current engine or its settings. */
+ if (cachefile->use_render_procedural) {
+ DEG_id_tag_update(&cachefile->id, ID_RECALC_COPY_ON_WRITE);
+ /* Rebuild relations so that modifiers are reconnected to or disconnected from the
+ * cache-file. */
+ DEG_relations_tag_update(bmain);
+ }
+ }
}
void ED_render_view_layer_changed(Main *bmain, bScreen *screen)
@@ -213,10 +229,16 @@ void ED_render_view_layer_changed(Main *bmain, bScreen *screen)
}
}
-/***************************** Updates ***********************************
- * ED_render_id_flush_update gets called from DEG_id_tag_update, to do *
- * editor level updates when the ID changes. when these ID blocks are in *
- * the dependency graph, we can get rid of the manual dependency checks. */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Updates
+ *
+ * #ED_render_id_flush_update gets called from #DEG_id_tag_update,
+ * to do editor level updates when the ID changes.
+ * When these ID blocks are in the dependency graph,
+ * we can get rid of the manual dependency checks.
+ * \{ */
static void material_changed(Main *UNUSED(bmain), Material *ma)
{
@@ -322,3 +344,5 @@ void ED_render_id_flush_update(const DEGEditorUpdateContext *update_ctx, ID *id)
break;
}
}
+
+/** \} */
diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c
index c351ade9954..e08a4e946f6 100644
--- a/source/blender/editors/screen/area.c
+++ b/source/blender/editors/screen/area.c
@@ -834,9 +834,8 @@ void ED_area_status_text(ScrArea *area, const char *str)
BLI_strncpy(region->headerstr, str, UI_MAX_DRAW_STR);
BLI_str_rstrip(region->headerstr);
}
- else if (region->headerstr) {
- MEM_freeN(region->headerstr);
- region->headerstr = NULL;
+ else {
+ MEM_SAFE_FREE(region->headerstr);
}
ED_region_tag_redraw(region);
}
@@ -859,9 +858,8 @@ void ED_workspace_status_text(bContext *C, const char *str)
}
BLI_strncpy(workspace->status_text, str, UI_MAX_DRAW_STR);
}
- else if (workspace->status_text) {
- MEM_freeN(workspace->status_text);
- workspace->status_text = NULL;
+ else {
+ MEM_SAFE_FREE(workspace->status_text);
}
/* Redraw status bar. */
diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c
index 8123d8bb104..b0181de96a0 100644
--- a/source/blender/editors/screen/screen_context.c
+++ b/source/blender/editors/screen/screen_context.c
@@ -113,7 +113,7 @@ const char *screen_context_dir[] = {
"active_editable_fcurve",
"selected_editable_keyframes",
"ui_list",
- "asset_library",
+ "asset_library_ref",
NULL,
};
@@ -1031,7 +1031,7 @@ static eContextResult screen_ctx_asset_library(const bContext *C, bContextDataRe
{
WorkSpace *workspace = CTX_wm_workspace(C);
CTX_data_pointer_set(
- result, &workspace->id, &RNA_AssetLibraryReference, &workspace->asset_library);
+ result, &workspace->id, &RNA_AssetLibraryReference, &workspace->asset_library_ref);
return CTX_RESULT_OK;
}
@@ -1118,7 +1118,7 @@ static void ensure_ed_screen_context_functions(void)
register_context_function("selected_visible_fcurves", screen_ctx_selected_visible_fcurves);
register_context_function("active_editable_fcurve", screen_ctx_active_editable_fcurve);
register_context_function("selected_editable_keyframes", screen_ctx_selected_editable_keyframes);
- register_context_function("asset_library", screen_ctx_asset_library);
+ register_context_function("asset_library_ref", screen_ctx_asset_library);
register_context_function("ui_list", screen_ctx_ui_list);
}
diff --git a/source/blender/editors/screen/screen_draw.c b/source/blender/editors/screen/screen_draw.c
index dca464bbf22..ab50e327de3 100644
--- a/source/blender/editors/screen/screen_draw.c
+++ b/source/blender/editors/screen/screen_draw.c
@@ -451,7 +451,7 @@ static void screen_preview_draw(const bScreen *screen, int size_x, int size_y)
void ED_screen_preview_render(const bScreen *screen, int size_x, int size_y, uint *r_rect)
{
char err_out[256] = "unknown";
- GPUOffScreen *offscreen = GPU_offscreen_create(size_x, size_y, true, false, err_out);
+ GPUOffScreen *offscreen = GPU_offscreen_create(size_x, size_y, true, GPU_RGBA8, err_out);
GPU_offscreen_bind(offscreen, true);
GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f);
diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c
index 2a81fcfde8f..1c068fdd6e4 100644
--- a/source/blender/editors/screen/screen_edit.c
+++ b/source/blender/editors/screen/screen_edit.c
@@ -230,7 +230,7 @@ bScreen *screen_add(Main *bmain, const char *name, const rcti *rect)
void screen_data_copy(bScreen *to, bScreen *from)
{
/* free contents of 'to', is from blenkernel screen.c */
- BKE_screen_free(to);
+ BKE_screen_free_data(to);
to->flag = from->flag;
@@ -722,10 +722,7 @@ void ED_region_exit(bContext *C, ARegion *region)
WM_event_modal_handler_region_replace(win, region, NULL);
WM_draw_region_free(region, true);
- if (region->headerstr) {
- MEM_freeN(region->headerstr);
- region->headerstr = NULL;
- }
+ MEM_SAFE_FREE(region->headerstr);
if (region->regiontimer) {
WM_event_remove_timer(wm, win, region->regiontimer);
@@ -1653,10 +1650,7 @@ void ED_refresh_viewport_fps(bContext *C)
}
else {
/* playback stopped or shouldn't be running */
- if (scene->fps_info) {
- MEM_freeN(scene->fps_info);
- }
- scene->fps_info = NULL;
+ MEM_SAFE_FREE(scene->fps_info);
}
}
diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c
index 8d7d742e44b..cff3ecfbbd3 100644
--- a/source/blender/editors/screen/screen_ops.c
+++ b/source/blender/editors/screen/screen_ops.c
@@ -381,7 +381,7 @@ bool ED_operator_console_active(bContext *C)
static bool ed_object_hidden(const Object *ob)
{
/* if hidden but in edit mode, we still display, can happen with animation */
- return ((ob->restrictflag & OB_RESTRICT_VIEWPORT) && !(ob->mode & OB_MODE_EDIT));
+ return ((ob->visibility_flag & OB_HIDE_VIEWPORT) && !(ob->mode & OB_MODE_EDIT));
}
bool ED_operator_object_active(bContext *C)
@@ -403,7 +403,7 @@ bool ED_operator_object_active_editable_ex(bContext *C, const Object *ob)
}
if (ed_object_hidden(ob)) {
- CTX_wm_operator_poll_msg_set(C, "Cannot edit hidden obect");
+ CTX_wm_operator_poll_msg_set(C, "Cannot edit hidden object");
return false;
}
@@ -1023,10 +1023,7 @@ AZone *ED_area_azones_update(ScrArea *area, const int xy[2])
static void actionzone_exit(wmOperator *op)
{
- if (op->customdata) {
- MEM_freeN(op->customdata);
- }
- op->customdata = NULL;
+ MEM_SAFE_FREE(op->customdata);
G.moving &= ~G_TRANSFORM_WM;
}
@@ -1308,10 +1305,7 @@ static bool area_swap_init(wmOperator *op, const wmEvent *event)
static void area_swap_exit(bContext *C, wmOperator *op)
{
WM_cursor_modal_restore(CTX_wm_window(C));
- if (op->customdata) {
- MEM_freeN(op->customdata);
- }
- op->customdata = NULL;
+ MEM_SAFE_FREE(op->customdata);
}
static void area_swap_cancel(bContext *C, wmOperator *op)
@@ -1892,10 +1886,7 @@ static void area_move_apply(bContext *C, wmOperator *op)
static void area_move_exit(bContext *C, wmOperator *op)
{
- if (op->customdata) {
- MEM_freeN(op->customdata);
- }
- op->customdata = NULL;
+ MEM_SAFE_FREE(op->customdata);
/* this makes sure aligned edges will result in aligned grabbing */
BKE_screen_remove_double_scrverts(CTX_wm_screen(C));
@@ -2906,7 +2897,7 @@ static void areas_do_frame_follow(bContext *C, bool middle)
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
/* do follow here if editor type supports it */
- if ((screen_ctx->redraws_flag & TIME_FOLLOW)) {
+ if (screen_ctx->redraws_flag & TIME_FOLLOW) {
if ((region->regiontype == RGN_TYPE_WINDOW &&
ELEM(area->spacetype, SPACE_SEQ, SPACE_GRAPH, SPACE_ACTION, SPACE_NLA)) ||
(area->spacetype == SPACE_CLIP && region->regiontype == RGN_TYPE_PREVIEW)) {
@@ -3057,8 +3048,7 @@ static int keyframe_jump_exec(bContext *C, wmOperator *op)
float cfra = (float)(CFRA);
/* init binarytree-list for getting keyframes */
- DLRBT_Tree keys;
- BLI_dlrbTree_init(&keys);
+ struct AnimKeylist *keylist = ED_keylist_create();
/* seed up dummy dopesheet context with flags to perform necessary filtering */
if ((scene->flag & SCE_KEYS_NO_SELONLY) == 0) {
@@ -3067,14 +3057,14 @@ static int keyframe_jump_exec(bContext *C, wmOperator *op)
}
/* populate tree with keyframe nodes */
- scene_to_keylist(&ads, scene, &keys, 0);
+ scene_to_keylist(&ads, scene, keylist, 0);
if (ob) {
- ob_to_keylist(&ads, ob, &keys, 0);
+ ob_to_keylist(&ads, ob, keylist, 0);
if (ob->type == OB_GPENCIL) {
const bool active = !(scene->flag & SCE_KEYS_NO_SELONLY);
- gpencil_to_keylist(&ads, ob->data, &keys, active);
+ gpencil_to_keylist(&ads, ob->data, keylist, active);
}
}
@@ -3082,17 +3072,17 @@ static int keyframe_jump_exec(bContext *C, wmOperator *op)
Mask *mask = CTX_data_edit_mask(C);
if (mask) {
MaskLayer *masklay = BKE_mask_layer_active(mask);
- mask_to_keylist(&ads, masklay, &keys);
+ mask_to_keylist(&ads, masklay, keylist);
}
}
/* find matching keyframe in the right direction */
- ActKeyColumn *ak;
+ const ActKeyColumn *ak;
if (next) {
- ak = (ActKeyColumn *)BLI_dlrbTree_search_next(&keys, compare_ak_cfraPtr, &cfra);
+ ak = ED_keylist_find_next(keylist, cfra);
}
else {
- ak = (ActKeyColumn *)BLI_dlrbTree_search_prev(&keys, compare_ak_cfraPtr, &cfra);
+ ak = ED_keylist_find_prev(keylist, cfra);
}
while ((ak != NULL) && (done == false)) {
@@ -3113,7 +3103,7 @@ static int keyframe_jump_exec(bContext *C, wmOperator *op)
}
/* free temp stuff */
- BLI_dlrbTree_free(&keys);
+ ED_keylist_free(keylist);
/* any success? */
if (done == false) {
@@ -4488,10 +4478,8 @@ static bool match_region_with_redraws(const ScrArea *area,
return false;
}
-static void screen_animation_region_tag_redraw(ScrArea *area,
- ARegion *region,
- const Scene *scene,
- eScreen_Redraws_Flag redraws)
+static void screen_animation_region_tag_redraw(
+ bContext *C, ScrArea *area, ARegion *region, const Scene *scene, eScreen_Redraws_Flag redraws)
{
/* Do follow time here if editor type supports it */
if ((redraws & TIME_FOLLOW) &&
@@ -4515,10 +4503,29 @@ static void screen_animation_region_tag_redraw(ScrArea *area,
* We do need to redraw when this area is in full screen as no other areas
* will be tagged for redrawing. */
if (region->regiontype == RGN_TYPE_WINDOW && !area->full) {
- if (ELEM(area->spacetype, SPACE_GRAPH, SPACE_NLA, SPACE_ACTION)) {
+ if (ELEM(area->spacetype, SPACE_NLA, SPACE_ACTION)) {
return;
}
+ /* Drivers Editor needs a full redraw on playback for graph_draw_driver_debug().
+ * This will make it slower than regular graph editor during playback, but drawing this in
+ * graph_main_region_draw_overlay() is not feasible because it requires animation filtering
+ * which has significant overhead which needs to be avoided in the overlay which is redrawn on
+ * every UI interaction. */
+ if (area->spacetype == SPACE_GRAPH) {
+ const SpaceGraph *sipo = area->spacedata.first;
+ if (sipo->mode != SIPO_MODE_DRIVERS) {
+ return;
+ }
+ bAnimContext ac;
+ if (ANIM_animdata_get_context(C, &ac) == false) {
+ return;
+ }
+ if (ac.datatype != ANIMCONT_DRIVERS) {
+ return;
+ }
+ }
+
if (area->spacetype == SPACE_SEQ) {
const SpaceSeq *sseq = area->spacedata.first;
if (!ED_space_sequencer_has_playback_animation(sseq, scene)) {
@@ -4712,7 +4719,7 @@ static int screen_animation_step_invoke(bContext *C, wmOperator *UNUSED(op), con
}
if (redraw) {
- screen_animation_region_tag_redraw(area, region, scene, sad->redraws);
+ screen_animation_region_tag_redraw(C, area, region, scene, sad->redraws);
}
}
}
@@ -5695,10 +5702,7 @@ static void keymap_modal_set(wmKeyConfig *keyconf)
WM_modalkeymap_assign(keymap, "SCREEN_OT_area_move");
}
-static bool blend_file_drop_poll(bContext *UNUSED(C),
- wmDrag *drag,
- const wmEvent *UNUSED(event),
- const char **UNUSED(r_tooltip))
+static bool blend_file_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event))
{
if (drag->type == WM_DRAG_PATH) {
if (drag->icon == ICON_FILE_BLEND) {
@@ -5728,8 +5732,9 @@ void ED_keymap_screen(wmKeyConfig *keyconf)
/* dropbox for entire window */
ListBase *lb = WM_dropboxmap_find("Window", 0, 0);
- WM_dropbox_add(lb, "WM_OT_drop_blend_file", blend_file_drop_poll, blend_file_drop_copy, NULL);
- WM_dropbox_add(lb, "UI_OT_drop_color", UI_drop_color_poll, UI_drop_color_copy, NULL);
+ WM_dropbox_add(
+ lb, "WM_OT_drop_blend_file", blend_file_drop_poll, blend_file_drop_copy, NULL, NULL);
+ WM_dropbox_add(lb, "UI_OT_drop_color", UI_drop_color_poll, UI_drop_color_copy, NULL, NULL);
keymap_modal_set(keyconf);
}
diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c
index 23b90171a1d..a35e248a78c 100644
--- a/source/blender/editors/sculpt_paint/paint_image_2d.c
+++ b/source/blender/editors/sculpt_paint/paint_image_2d.c
@@ -837,10 +837,7 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s,
if (diameter != cache->lastdiameter || (mask_rotation != cache->last_mask_rotation) ||
renew_maxmask) {
- if (cache->tex_mask) {
- MEM_freeN(cache->tex_mask);
- cache->tex_mask = NULL;
- }
+ MEM_SAFE_FREE(cache->tex_mask);
brush_painter_2d_tex_mapping(s,
tile->canvas,
@@ -862,10 +859,7 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s,
}
/* curve mask can only change if the size changes */
- if (cache->curve_mask) {
- MEM_freeN(cache->curve_mask);
- cache->curve_mask = NULL;
- }
+ MEM_SAFE_FREE(cache->curve_mask);
cache->curve_mask = brush_painter_curve_mask_new(painter, diameter, size, pos);
diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c
index a8ad6ab1b74..a58b1947b0c 100644
--- a/source/blender/editors/sculpt_paint/paint_image_proj.c
+++ b/source/blender/editors/sculpt_paint/paint_image_proj.c
@@ -4512,7 +4512,7 @@ static void project_paint_begin(const bContext *C,
ps->buckets_x = (int)(ps->screen_width / (((float)diameter) / PROJ_BUCKET_BRUSH_DIV));
ps->buckets_y = (int)(ps->screen_height / (((float)diameter) / PROJ_BUCKET_BRUSH_DIV));
- /* printf("\tscreenspace bucket division x:%d y:%d\n", ps->buckets_x, ps->buckets_y); */
+ // printf("\tscreenspace bucket division x:%d y:%d\n", ps->buckets_x, ps->buckets_y);
if (ps->buckets_x > PROJ_BUCKET_RECT_MAX || ps->buckets_y > PROJ_BUCKET_RECT_MAX) {
reset_threads = true;
@@ -5194,8 +5194,8 @@ static void do_projectpaint_thread(TaskPool *__restrict UNUSED(pool), void *ph_v
softenArena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), "paint soften arena");
}
- /* printf("brush bounds %d %d %d %d\n",
- * bucketMin[0], bucketMin[1], bucketMax[0], bucketMax[1]); */
+ // printf("brush bounds %d %d %d %d\n",
+ // bucketMin[0], bucketMin[1], bucketMax[0], bucketMax[1]);
while (project_bucket_iter_next(ps, &bucket_index, &bucket_bounds, pos)) {
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index 83388c1aef2..7bde864e73f 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -6560,10 +6560,7 @@ static void sculpt_update_tex(const Scene *scene, Sculpt *sd, SculptSession *ss)
Brush *brush = BKE_paint_brush(&sd->paint);
const int radius = BKE_brush_size_get(scene, brush);
- if (ss->texcache) {
- MEM_freeN(ss->texcache);
- ss->texcache = NULL;
- }
+ MEM_SAFE_FREE(ss->texcache);
if (ss->tex_pool) {
BKE_image_pool_free(ss->tex_pool);
@@ -7887,6 +7884,9 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f
sculpt_update_cache_invariants(C, sd, ss, op, mouse);
+ SculptCursorGeometryInfo sgi;
+ SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
+
SCULPT_undo_push_begin(ob, sculpt_tool_name(sd));
return true;
diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c
index 87e0ea7f6a9..ae6dcbdbff4 100644
--- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c
+++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c
@@ -94,15 +94,9 @@ void SCULPT_pbvh_clear(Object *ob)
ss->pbvh = NULL;
}
- if (ss->pmap) {
- MEM_freeN(ss->pmap);
- ss->pmap = NULL;
- }
+ MEM_SAFE_FREE(ss->pmap);
- if (ss->pmap_mem) {
- MEM_freeN(ss->pmap_mem);
- ss->pmap_mem = NULL;
- }
+ MEM_SAFE_FREE(ss->pmap_mem);
BKE_object_free_derived_caches(ob);
diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c
index eabbfe43e03..38165b7622f 100644
--- a/source/blender/editors/sculpt_paint/sculpt_smooth.c
+++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c
@@ -88,7 +88,7 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3],
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
/* Do not modify corner vertices. */
- if (neighbor_count <= 2) {
+ if (neighbor_count <= 2 && is_boundary) {
copy_v3_v3(result, SCULPT_vertex_co_get(ss, index));
return;
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_uv.c b/source/blender/editors/sculpt_paint/sculpt_uv.c
index 771e0e1e47b..e5ca5e4defd 100644
--- a/source/blender/editors/sculpt_paint/sculpt_uv.c
+++ b/source/blender/editors/sculpt_paint/sculpt_uv.c
@@ -190,7 +190,7 @@ static void HC_relaxation_iteration_uv(BMEditMesh *em,
/* This is supposed to happen only if "Pin Edges" is on,
* since we have initialization on stroke start.
* If ever uv brushes get their own mode we should check for toolsettings option too. */
- if ((sculptdata->uv[i].flag & MARK_BOUNDARY)) {
+ if (sculptdata->uv[i].flag & MARK_BOUNDARY) {
continue;
}
@@ -268,7 +268,7 @@ static void laplacian_relaxation_iteration_uv(BMEditMesh *em,
/* This is supposed to happen only if "Pin Edges" is on,
* since we have initialization on stroke start.
* If ever uv brushes get their own mode we should check for toolsettings option too. */
- if ((sculptdata->uv[i].flag & MARK_BOUNDARY)) {
+ if (sculptdata->uv[i].flag & MARK_BOUNDARY) {
continue;
}
diff --git a/source/blender/editors/space_action/action_data.c b/source/blender/editors/space_action/action_data.c
index d69c7ab8d48..717d87c4972 100644
--- a/source/blender/editors/space_action/action_data.c
+++ b/source/blender/editors/space_action/action_data.c
@@ -599,16 +599,13 @@ void ED_animedit_unlink_action(
id_fake_user_clear(&act->id);
}
- /* If in Tweak Mode, don't unlink. Instead, this
- * becomes a shortcut to exit Tweak Mode instead
- */
+ /* If in Tweak Mode, don't unlink. Instead, this becomes a shortcut to exit Tweak Mode. */
if ((adt) && (adt->flag & ADT_NLA_EDIT_ON)) {
- /* Exit Tweak Mode */
BKE_nla_tweakmode_exit(adt);
- /* Flush this to the Action Editor (if that's where this change was initiated) */
- if (area->spacetype == SPACE_ACTION) {
- actedit_change_action(C, NULL);
+ Scene *scene = CTX_data_scene(C);
+ if (scene != NULL) {
+ scene->flag &= ~SCE_NLA_EDIT_ON;
}
}
else {
@@ -660,6 +657,9 @@ static int action_unlink_exec(bContext *C, wmOperator *op)
ED_animedit_unlink_action(C, NULL, adt, adt->action, op->reports, force_delete);
}
+ /* Unlink is also abused to exit NLA tweak mode. */
+ WM_main_add_notifier(NC_ANIMATION | ND_NLA_ACTCHANGE, NULL);
+
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/space_action/action_draw.c b/source/blender/editors/space_action/action_draw.c
index ce07b9c5fad..a3bdcd2adf5 100644
--- a/source/blender/editors/space_action/action_draw.c
+++ b/source/blender/editors/space_action/action_draw.c
@@ -144,12 +144,14 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *region
uchar col1[4], col2[4];
uchar col1a[4], col2a[4];
uchar col1b[4], col2b[4];
+ uchar col_summary[4];
const bool show_group_colors = U.animation_flag & USER_ANIM_SHOW_CHANNEL_GROUP_COLORS;
/* get theme colors */
UI_GetThemeColor4ubv(TH_SHADE2, col2);
UI_GetThemeColor4ubv(TH_HILITE, col1);
+ UI_GetThemeColor4ubv(TH_ANIM_ACTIVE, col_summary);
UI_GetThemeColor4ubv(TH_GROUP, col2a);
UI_GetThemeColor4ubv(TH_GROUP_ACTIVE, col1a);
@@ -244,7 +246,10 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *region
else if (ac->datatype == ANIMCONT_GPENCIL) {
uchar *color;
uchar gpl_col[4];
- if ((show_group_colors) && (ale->type == ANIMTYPE_GPLAYER)) {
+ if (ale->type == ANIMTYPE_SUMMARY) {
+ color = col_summary;
+ }
+ else if ((show_group_colors) && (ale->type == ANIMTYPE_GPLAYER)) {
bGPDlayer *gpl = (bGPDlayer *)ale->data;
rgb_float_to_uchar(gpl_col, gpl->color);
gpl_col[3] = col1[3];
@@ -265,7 +270,13 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *region
else if (ac->datatype == ANIMCONT_MASK) {
/* TODO: this is a copy of gpencil. */
/* frames less than one get less saturated background */
- uchar *color = sel ? col1 : col2;
+ uchar *color;
+ if (ale->type == ANIMTYPE_SUMMARY) {
+ color = col_summary;
+ }
+ else {
+ color = sel ? col1 : col2;
+ }
immUniformColor4ubv(color);
immRectf(pos, 0.0f, ymin, v2d->cur.xmin, ymax);
@@ -302,6 +313,8 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *region
ymax = ACHANNEL_FIRST_TOP(ac);
+ struct AnimKeylistDrawList *draw_list = ED_keylist_draw_list_create();
+
for (ale = anim_data.first; ale; ale = ale->next, ymax -= ACHANNEL_STEP(ac)) {
float ymin = ymax - ACHANNEL_HEIGHT(ac);
float ycenter = (ymin + ymax) / 2.0f;
@@ -316,34 +329,41 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *region
/* draw 'keyframes' for each specific datatype */
switch (ale->datatype) {
case ALE_ALL:
- draw_summary_channel(v2d, ale->data, ycenter, ac->yscale_fac, action_flag);
+ draw_summary_channel(draw_list, ale->data, ycenter, ac->yscale_fac, action_flag);
break;
case ALE_SCE:
- draw_scene_channel(v2d, ads, ale->key_data, ycenter, ac->yscale_fac, action_flag);
+ draw_scene_channel(
+ draw_list, ads, ale->key_data, ycenter, ac->yscale_fac, action_flag);
break;
case ALE_OB:
- draw_object_channel(v2d, ads, ale->key_data, ycenter, ac->yscale_fac, action_flag);
+ draw_object_channel(
+ draw_list, ads, ale->key_data, ycenter, ac->yscale_fac, action_flag);
break;
case ALE_ACT:
- draw_action_channel(v2d, adt, ale->key_data, ycenter, ac->yscale_fac, action_flag);
+ draw_action_channel(
+ draw_list, adt, ale->key_data, ycenter, ac->yscale_fac, action_flag);
break;
case ALE_GROUP:
- draw_agroup_channel(v2d, adt, ale->data, ycenter, ac->yscale_fac, action_flag);
+ draw_agroup_channel(draw_list, adt, ale->data, ycenter, ac->yscale_fac, action_flag);
break;
case ALE_FCURVE:
- draw_fcurve_channel(v2d, adt, ale->key_data, ycenter, ac->yscale_fac, action_flag);
+ draw_fcurve_channel(
+ draw_list, adt, ale->key_data, ycenter, ac->yscale_fac, action_flag);
break;
case ALE_GPFRAME:
- draw_gpl_channel(v2d, ads, ale->data, ycenter, ac->yscale_fac, action_flag);
+ draw_gpl_channel(draw_list, ads, ale->data, ycenter, ac->yscale_fac, action_flag);
break;
case ALE_MASKLAY:
- draw_masklay_channel(v2d, ads, ale->data, ycenter, ac->yscale_fac, action_flag);
+ draw_masklay_channel(draw_list, ads, ale->data, ycenter, ac->yscale_fac, action_flag);
break;
}
}
}
}
+ ED_keylist_draw_list_flush(draw_list, v2d);
+ ED_keylist_draw_list_free(draw_list);
+
/* free temporary channels used for drawing */
ANIM_animdata_freelist(&anim_data);
}
diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c
index 59d2063ea84..9dcfc626a50 100644
--- a/source/blender/editors/space_action/action_select.c
+++ b/source/blender/editors/space_action/action_select.c
@@ -93,7 +93,7 @@ static bAnimListElem *actkeys_find_list_element_at_position(bAnimContext *ac,
}
static void actkeys_list_element_to_keylist(bAnimContext *ac,
- DLRBT_Tree *anim_keys,
+ struct AnimKeylist *keylist,
bAnimListElem *ale)
{
AnimData *adt = ANIM_nla_mapping_get(ac, ale);
@@ -107,44 +107,44 @@ static void actkeys_list_element_to_keylist(bAnimContext *ac,
switch (ale->datatype) {
case ALE_SCE: {
Scene *scene = (Scene *)ale->key_data;
- scene_to_keylist(ads, scene, anim_keys, 0);
+ scene_to_keylist(ads, scene, keylist, 0);
break;
}
case ALE_OB: {
Object *ob = (Object *)ale->key_data;
- ob_to_keylist(ads, ob, anim_keys, 0);
+ ob_to_keylist(ads, ob, keylist, 0);
break;
}
case ALE_ACT: {
bAction *act = (bAction *)ale->key_data;
- action_to_keylist(adt, act, anim_keys, 0);
+ action_to_keylist(adt, act, keylist, 0);
break;
}
case ALE_FCURVE: {
FCurve *fcu = (FCurve *)ale->key_data;
- fcurve_to_keylist(adt, fcu, anim_keys, 0);
+ fcurve_to_keylist(adt, fcu, keylist, 0);
break;
}
}
}
else if (ale->type == ANIMTYPE_SUMMARY) {
/* dopesheet summary covers everything */
- summary_to_keylist(ac, anim_keys, 0);
+ summary_to_keylist(ac, keylist, 0);
}
else if (ale->type == ANIMTYPE_GROUP) {
/* TODO: why don't we just give groups key_data too? */
bActionGroup *agrp = (bActionGroup *)ale->data;
- agroup_to_keylist(adt, agrp, anim_keys, 0);
+ agroup_to_keylist(adt, agrp, keylist, 0);
}
else if (ale->type == ANIMTYPE_GPLAYER) {
/* TODO: why don't we just give gplayers key_data too? */
bGPDlayer *gpl = (bGPDlayer *)ale->data;
- gpl_to_keylist(ads, gpl, anim_keys);
+ gpl_to_keylist(ads, gpl, keylist);
}
else if (ale->type == ANIMTYPE_MASKLAYER) {
/* TODO: why don't we just give masklayers key_data too? */
MaskLayer *masklay = (MaskLayer *)ale->data;
- mask_to_keylist(ads, masklay, anim_keys);
+ mask_to_keylist(ads, masklay, keylist);
}
}
@@ -160,9 +160,8 @@ static void actkeys_find_key_in_list_element(bAnimContext *ac,
View2D *v2d = &ac->region->v2d;
- DLRBT_Tree anim_keys;
- BLI_dlrbTree_init(&anim_keys);
- actkeys_list_element_to_keylist(ac, &anim_keys, ale);
+ struct AnimKeylist *keylist = ED_keylist_create();
+ actkeys_list_element_to_keylist(ac, keylist, ale);
AnimData *adt = ANIM_nla_mapping_get(ac, ale);
@@ -171,25 +170,23 @@ static void actkeys_find_key_in_list_element(bAnimContext *ac,
/* half-size (for either side), but rounded up to nearest int (for easier targeting) */
key_hsize = roundf(key_hsize / 2.0f);
- float xmin = UI_view2d_region_to_view_x(v2d, region_x - (int)key_hsize);
- float xmax = UI_view2d_region_to_view_x(v2d, region_x + (int)key_hsize);
-
- for (ActKeyColumn *ak = anim_keys.root; ak; ak = (ak->cfra < xmin) ? ak->right : ak->left) {
- if (IN_RANGE(ak->cfra, xmin, xmax)) {
- /* set the frame to use, and apply inverse-correction for NLA-mapping
- * so that the frame will get selected by the selection functions without
- * requiring to map each frame once again...
- */
- *r_selx = BKE_nla_tweakedit_remap(adt, ak->cfra, NLATIME_CONVERT_UNMAP);
- *r_frame = ak->cfra;
- *r_found = true;
- *r_is_selected = (ak->sel & SELECT) != 0;
- break;
- }
+ const Range2f range = {UI_view2d_region_to_view_x(v2d, region_x - (int)key_hsize),
+ UI_view2d_region_to_view_x(v2d, region_x + (int)key_hsize)};
+ const ActKeyColumn *ak = ED_keylist_find_any_between(keylist, range);
+ if (ak) {
+
+ /* set the frame to use, and apply inverse-correction for NLA-mapping
+ * so that the frame will get selected by the selection functions without
+ * requiring to map each frame once again...
+ */
+ *r_selx = BKE_nla_tweakedit_remap(adt, ak->cfra, NLATIME_CONVERT_UNMAP);
+ *r_frame = ak->cfra;
+ *r_found = true;
+ *r_is_selected = (ak->sel & SELECT) != 0;
}
/* cleanup temporary lists */
- BLI_dlrbTree_free(&anim_keys);
+ ED_keylist_free(keylist);
}
static void actkeys_find_key_at_position(bAnimContext *ac,
diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c
index 57a7fe894b0..b04291b7ab4 100644
--- a/source/blender/editors/space_buttons/space_buttons.c
+++ b/source/blender/editors/space_buttons/space_buttons.c
@@ -811,6 +811,9 @@ static void buttons_area_listener(const wmSpaceTypeListenerParams *params)
break;
case NC_ANIMATION:
switch (wmn->data) {
+ case ND_NLA_ACTCHANGE:
+ ED_area_tag_redraw(area);
+ break;
case ND_KEYFRAME:
if (ELEM(wmn->action, NA_EDITED, NA_ADDED, NA_REMOVED)) {
ED_area_tag_redraw(area);
diff --git a/source/blender/editors/space_clip/clip_editor.c b/source/blender/editors/space_clip/clip_editor.c
index 67b4fd61d38..834ef847069 100644
--- a/source/blender/editors/space_clip/clip_editor.c
+++ b/source/blender/editors/space_clip/clip_editor.c
@@ -1037,6 +1037,7 @@ static void prefetch_freejob(void *pjv)
if (clip_local != NULL) {
BKE_libblock_free_datablock(&clip_local->id, 0);
BKE_libblock_free_data(&clip_local->id, false);
+ BLI_assert(!clip_local->id.py_instance); /* Or call #BKE_libblock_free_data_py. */
MEM_freeN(clip_local);
}
diff --git a/source/blender/editors/space_clip/clip_utils.c b/source/blender/editors/space_clip/clip_utils.c
index 7194e78e940..23dd290e13f 100644
--- a/source/blender/editors/space_clip/clip_utils.c
+++ b/source/blender/editors/space_clip/clip_utils.c
@@ -492,7 +492,7 @@ static bool mask_has_selection(const bContext *C)
}
LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) {
- if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+ if (mask_layer->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
LISTBASE_FOREACH (MaskSpline *, spline, &mask_layer->splines) {
diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c
index aef3385f2dc..e2fbb4a5a59 100644
--- a/source/blender/editors/space_clip/space_clip.c
+++ b/source/blender/editors/space_clip/space_clip.c
@@ -605,10 +605,7 @@ static int /*eContextResult*/ clip_context(const bContext *C,
}
/* dropboxes */
-static bool clip_drop_poll(bContext *UNUSED(C),
- wmDrag *drag,
- const wmEvent *UNUSED(event),
- const char **UNUSED(r_tooltip))
+static bool clip_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event))
{
if (drag->type == WM_DRAG_PATH) {
/* rule might not work? */
@@ -639,7 +636,7 @@ static void clip_dropboxes(void)
{
ListBase *lb = WM_dropboxmap_find("Clip", SPACE_CLIP, 0);
- WM_dropbox_add(lb, "CLIP_OT_open", clip_drop_poll, clip_drop_copy, NULL);
+ WM_dropbox_add(lb, "CLIP_OT_open", clip_drop_poll, clip_drop_copy, NULL, NULL);
}
static void clip_refresh(const bContext *C, ScrArea *area)
diff --git a/source/blender/editors/space_clip/tracking_ops_solve.c b/source/blender/editors/space_clip/tracking_ops_solve.c
index 96504651e44..58f9b307ef9 100644
--- a/source/blender/editors/space_clip/tracking_ops_solve.c
+++ b/source/blender/editors/space_clip/tracking_ops_solve.c
@@ -299,10 +299,7 @@ static int clear_solution_exec(bContext *C, wmOperator *UNUSED(op))
track->flag &= ~TRACK_HAS_BUNDLE;
}
- if (reconstruction->cameras != NULL) {
- MEM_freeN(reconstruction->cameras);
- reconstruction->cameras = NULL;
- }
+ MEM_SAFE_FREE(reconstruction->cameras);
reconstruction->camnr = 0;
reconstruction->flag &= ~TRACKING_RECONSTRUCTED;
diff --git a/source/blender/editors/space_console/space_console.c b/source/blender/editors/space_console/space_console.c
index 3029eed1017..47d15efb6ca 100644
--- a/source/blender/editors/space_console/space_console.c
+++ b/source/blender/editors/space_console/space_console.c
@@ -158,10 +158,7 @@ static void console_cursor(wmWindow *win, ScrArea *UNUSED(area), ARegion *region
/* ************* dropboxes ************* */
-static bool id_drop_poll(bContext *UNUSED(C),
- wmDrag *drag,
- const wmEvent *UNUSED(event),
- const char **UNUSED(tooltip))
+static bool id_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event))
{
return WM_drag_get_local_ID(drag, 0) != NULL;
}
@@ -176,10 +173,7 @@ static void id_drop_copy(wmDrag *drag, wmDropBox *drop)
MEM_freeN(text);
}
-static bool path_drop_poll(bContext *UNUSED(C),
- wmDrag *drag,
- const wmEvent *UNUSED(event),
- const char **UNUSED(tooltip))
+static bool path_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event))
{
return (drag->type == WM_DRAG_PATH);
}
@@ -196,8 +190,8 @@ static void console_dropboxes(void)
{
ListBase *lb = WM_dropboxmap_find("Console", SPACE_CONSOLE, RGN_TYPE_WINDOW);
- WM_dropbox_add(lb, "CONSOLE_OT_insert", id_drop_poll, id_drop_copy, NULL);
- WM_dropbox_add(lb, "CONSOLE_OT_insert", path_drop_poll, path_drop_copy, NULL);
+ WM_dropbox_add(lb, "CONSOLE_OT_insert", id_drop_poll, id_drop_copy, NULL, NULL);
+ WM_dropbox_add(lb, "CONSOLE_OT_insert", path_drop_poll, path_drop_copy, NULL, NULL);
}
/* ************* end drop *********** */
diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c
index 37a56816677..9a46579780e 100644
--- a/source/blender/editors/space_file/file_draw.c
+++ b/source/blender/editors/space_file/file_draw.c
@@ -82,6 +82,9 @@ void ED_file_path_button(bScreen *screen,
PointerRNA params_rna_ptr;
uiBut *but;
+ BLI_assert_msg(params != NULL,
+ "File select parameters not set. The caller is expected to check this.");
+
RNA_pointer_create(&screen->id, &RNA_FileSelectParams, params, &params_rna_ptr);
/* callbacks for operator check functions */
@@ -1103,7 +1106,7 @@ bool file_draw_hint_if_invalid(const SpaceFile *sfile, const ARegion *region)
return false;
}
/* Check if the library exists. */
- if ((asset_params->asset_library.type == ASSET_LIBRARY_LOCAL) ||
+ if ((asset_params->asset_library_ref.type == ASSET_LIBRARY_LOCAL) ||
filelist_is_dir(sfile->files, asset_params->base_params.dir)) {
return false;
}
diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c
index 616e7fe51db..2f1acd2ca4d 100644
--- a/source/blender/editors/space_file/file_ops.c
+++ b/source/blender/editors/space_file/file_ops.c
@@ -546,6 +546,9 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
const bool fill = RNA_boolean_get(op->ptr, "fill");
const bool do_diropen = RNA_boolean_get(op->ptr, "open");
const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all");
+ const bool only_activate_if_selected = RNA_boolean_get(op->ptr, "only_activate_if_selected");
+ /* Used so right mouse clicks can do both, activate and spawn the context menu. */
+ const bool pass_through = RNA_boolean_get(op->ptr, "pass_through");
if (region->regiontype != RGN_TYPE_WINDOW) {
return OPERATOR_CANCELLED;
@@ -563,8 +566,13 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
int numfiles = filelist_files_ensure(sfile->files);
if ((idx >= 0) && (idx < numfiles)) {
+ const bool is_selected = filelist_entry_select_index_get(sfile->files, idx, CHECK_ALL) &
+ FILE_SEL_SELECTED;
+ if (only_activate_if_selected && is_selected) {
+ /* Don't deselect other items. */
+ }
/* single select, deselect all selected first */
- if (!extend) {
+ else if (!extend) {
file_select_deselect_all(sfile, FILE_SEL_SELECTED);
}
}
@@ -593,7 +601,7 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
WM_event_add_mousemove(CTX_wm_window(C)); /* for directory changes */
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
- return OPERATOR_FINISHED;
+ return pass_through ? (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH) : OPERATOR_FINISHED;
}
void FILE_OT_select(wmOperatorType *ot)
@@ -628,6 +636,20 @@ void FILE_OT_select(wmOperatorType *ot)
"Deselect On Nothing",
"Deselect all when nothing under the cursor");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(ot->srna,
+ "only_activate_if_selected",
+ false,
+ "Only Activate if Selected",
+ "Do not change selection if the item under the cursor is already "
+ "selected, only activate it");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(ot->srna,
+ "pass_through",
+ false,
+ "Pass Through",
+ "Even on successful execution, pass the event on so other operators can "
+ "execute on it as well");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
}
/** \} */
@@ -1366,7 +1388,9 @@ int file_highlight_set(SpaceFile *sfile, ARegion *region, int mx, int my)
FileSelectParams *params;
int numfiles, origfile;
- if (sfile == NULL || sfile->files == NULL) {
+ /* In case blender starts where the mouse is over a File browser,
+ * this operator can be invoked when the `sfile` or `sfile->layout` isn't initialized yet. */
+ if (sfile == NULL || sfile->files == NULL || sfile->layout == NULL) {
return 0;
}
@@ -2516,7 +2540,7 @@ void file_directory_enter_handle(bContext *C, void *UNUSED(arg_unused), void *UN
/* don't do for now because it selects entire text instead of
* placing cursor at the end */
- /* UI_textbutton_activate_but(C, but); */
+ // UI_textbutton_activate_but(C, but);
}
#if defined(WIN32)
else if (!can_create_dir(params->dir)) {
@@ -2750,20 +2774,6 @@ static void file_rename_state_activate(SpaceFile *sfile, int file_idx, bool requ
}
}
-static int file_rename_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event))
-{
- ScrArea *area = CTX_wm_area(C);
- SpaceFile *sfile = (SpaceFile *)CTX_wm_space_data(C);
- FileSelectParams *params = ED_fileselect_get_active_params(sfile);
-
- if (params) {
- file_rename_state_activate(sfile, params->active_file, true);
- ED_area_tag_redraw(area);
- }
-
- return OPERATOR_FINISHED;
-}
-
static int file_rename_exec(bContext *C, wmOperator *UNUSED(op))
{
ScrArea *area = CTX_wm_area(C);
@@ -2771,7 +2781,7 @@ static int file_rename_exec(bContext *C, wmOperator *UNUSED(op))
FileSelectParams *params = ED_fileselect_get_active_params(sfile);
if (params) {
- file_rename_state_activate(sfile, params->highlight_file, false);
+ file_rename_state_activate(sfile, params->active_file, false);
ED_area_tag_redraw(area);
}
@@ -2786,7 +2796,6 @@ void FILE_OT_rename(struct wmOperatorType *ot)
ot->idname = "FILE_OT_rename";
/* api callbacks */
- ot->invoke = file_rename_invoke;
ot->exec = file_rename_exec;
/* File browsing only operator (not asset browsing). */
ot->poll = ED_operator_file_browsing_active;
@@ -2830,20 +2839,10 @@ static bool file_delete_single(const FileSelectParams *params,
FileDirEntry *file,
const char **r_error_message)
{
- if (file->typeflag & FILE_TYPE_ASSET) {
- ID *id = filelist_file_get_id(file);
- if (!id) {
- *r_error_message = "File is not a local data-block asset.";
- return false;
- }
- ED_asset_clear_id(id);
- }
- else {
- char str[FILE_MAX];
- BLI_join_dirfile(str, sizeof(str), params->dir, file->relpath);
- if (BLI_delete_soft(str, r_error_message) != 0 || BLI_exists(str)) {
- return false;
- }
+ char str[FILE_MAX];
+ BLI_join_dirfile(str, sizeof(str), params->dir, file->relpath);
+ if (BLI_delete_soft(str, r_error_message) != 0 || BLI_exists(str)) {
+ return false;
}
return true;
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index 630c9aed157..f8ae4be9471 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.c
@@ -379,12 +379,13 @@ enum {
FLF_ASSETS_ONLY = 1 << 4,
};
+struct FileListReadJob;
typedef struct FileList {
FileDirEntryArr filelist;
eFileSelectType type;
/* The library this list was created for. Stored here so we know when to re-read. */
- AssetLibraryReference *asset_library;
+ AssetLibraryReference *asset_library_ref;
short flags;
@@ -415,8 +416,7 @@ typedef struct FileList {
bool (*check_dir_fn)(struct FileList *, char *, const bool);
/* Fill filelist (to be called by read job). */
- void (*read_job_fn)(
- Main *, struct FileList *, const char *, short *, short *, float *, ThreadMutex *);
+ void (*read_job_fn)(struct FileListReadJob *, short *, short *, float *);
/* Filter an entry of current filelist. */
bool (*filter_fn)(struct FileListInternEntry *, const char *, FileListFilter *);
@@ -459,34 +459,22 @@ enum {
static ImBuf *gSpecialFileImages[SPECIAL_IMG_MAX];
-static void filelist_readjob_main(Main *current_main,
- FileList *filelist,
- const char *main_name,
+static void filelist_readjob_main(struct FileListReadJob *job_params,
short *stop,
short *do_update,
- float *progress,
- ThreadMutex *lock);
-static void filelist_readjob_lib(Main *current_main,
- FileList *filelist,
- const char *main_name,
+ float *progress);
+static void filelist_readjob_lib(struct FileListReadJob *job_params,
short *stop,
short *do_update,
- float *progress,
- ThreadMutex *lock);
-static void filelist_readjob_dir(Main *current_main,
- FileList *filelist,
- const char *main_name,
+ float *progress);
+static void filelist_readjob_dir(struct FileListReadJob *job_params,
short *stop,
short *do_update,
- float *progress,
- ThreadMutex *lock);
-static void filelist_readjob_main_assets(Main *current_main,
- FileList *filelist,
- const char *main_name,
+ float *progress);
+static void filelist_readjob_main_assets(struct FileListReadJob *job_params,
short *stop,
short *do_update,
- float *progress,
- ThreadMutex *lock);
+ float *progress);
/* helper, could probably go in BKE actually? */
static int groupname_to_code(const char *group);
@@ -1065,28 +1053,28 @@ static bool filelist_compare_asset_libraries(const AssetLibraryReference *librar
}
/**
- * \param asset_library: May be NULL to unset the library.
+ * \param asset_library_ref: May be NULL to unset the library.
*/
-void filelist_setlibrary(FileList *filelist, const AssetLibraryReference *asset_library)
+void filelist_setlibrary(FileList *filelist, const AssetLibraryReference *asset_library_ref)
{
/* Unset if needed. */
- if (!asset_library) {
- if (filelist->asset_library) {
- MEM_SAFE_FREE(filelist->asset_library);
+ if (!asset_library_ref) {
+ if (filelist->asset_library_ref) {
+ MEM_SAFE_FREE(filelist->asset_library_ref);
filelist->flags |= FL_FORCE_RESET;
}
return;
}
- if (!filelist->asset_library) {
- filelist->asset_library = MEM_mallocN(sizeof(*filelist->asset_library),
- "filelist asset library");
- *filelist->asset_library = *asset_library;
+ if (!filelist->asset_library_ref) {
+ filelist->asset_library_ref = MEM_mallocN(sizeof(*filelist->asset_library_ref),
+ "filelist asset library");
+ *filelist->asset_library_ref = *asset_library_ref;
filelist->flags |= FL_FORCE_RESET;
}
- else if (!filelist_compare_asset_libraries(filelist->asset_library, asset_library)) {
- *filelist->asset_library = *asset_library;
+ else if (!filelist_compare_asset_libraries(filelist->asset_library_ref, asset_library_ref)) {
+ *filelist->asset_library_ref = *asset_library_ref;
filelist->flags |= FL_FORCE_RESET;
}
}
@@ -1791,7 +1779,7 @@ void filelist_free(struct FileList *filelist)
filelist->selection_state = NULL;
}
- MEM_SAFE_FREE(filelist->asset_library);
+ MEM_SAFE_FREE(filelist->asset_library_ref);
memset(&filelist->filter_data, 0, sizeof(filelist->filter_data));
@@ -1867,7 +1855,7 @@ bool filelist_is_dir(struct FileList *filelist, const char *path)
*/
void filelist_setdir(struct FileList *filelist, char *r_dir)
{
- const bool allow_invalid = filelist->asset_library != NULL;
+ const bool allow_invalid = filelist->asset_library_ref != NULL;
BLI_assert(strlen(r_dir) < FILE_MAX_LIBEXTRA);
BLI_path_normalize_dir(BKE_main_blendfile_path_from_global(), r_dir);
@@ -3133,14 +3121,29 @@ static void filelist_readjob_main_recursive(Main *bmain, FileList *filelist)
}
#endif
+typedef struct FileListReadJob {
+ ThreadMutex lock;
+ char main_name[FILE_MAX];
+ Main *current_main;
+ struct FileList *filelist;
+
+ /** Shallow copy of #filelist for thread-safe access.
+ *
+ * The job system calls #filelist_readjob_update which moves any read file from #tmp_filelist
+ * into #filelist in a thread-safe way.
+ *
+ * NOTE: #tmp_filelist is freed in #filelist_readjob_free, so any copied pointers need to be set
+ * to NULL to avoid double-freeing them. */
+ struct FileList *tmp_filelist;
+} FileListReadJob;
+
static void filelist_readjob_do(const bool do_lib,
- FileList *filelist,
- const char *main_name,
+ FileListReadJob *job_params,
const short *stop,
short *do_update,
- float *progress,
- ThreadMutex *lock)
+ float *progress)
{
+ FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */
ListBase entries = {0};
BLI_Stack *todo_dirs;
TodoDir *td_dir;
@@ -3164,7 +3167,7 @@ static void filelist_readjob_do(const bool do_lib,
BLI_strncpy(dir, filelist->filelist.root, sizeof(dir));
BLI_strncpy(filter_glob, filelist->filter_data.filter_glob, sizeof(filter_glob));
- BLI_path_normalize_dir(main_name, dir);
+ BLI_path_normalize_dir(job_params->main_name, dir);
td_dir->dir = BLI_strdup(dir);
while (!BLI_stack_is_empty(todo_dirs) && !(*stop)) {
@@ -3199,7 +3202,7 @@ static void filelist_readjob_do(const bool do_lib,
if (!nbr_entries) {
is_lib = false;
nbr_entries = filelist_readjob_list_dir(
- subdir, &entries, filter_glob, do_lib, main_name, skip_currpar);
+ subdir, &entries, filter_glob, do_lib, job_params->main_name, skip_currpar);
}
for (entry = entries.first; entry; entry = entry->next) {
@@ -3226,7 +3229,7 @@ static void filelist_readjob_do(const bool do_lib,
else {
/* We have a directory we want to list, add it to todo list! */
BLI_join_dirfile(dir, sizeof(dir), root, entry->relpath);
- BLI_path_normalize_dir(main_name, dir);
+ BLI_path_normalize_dir(job_params->main_name, dir);
td_dir = BLI_stack_push_r(todo_dirs);
td_dir->level = recursion_level + 1;
td_dir->dir = BLI_strdup(dir);
@@ -3236,14 +3239,14 @@ static void filelist_readjob_do(const bool do_lib,
}
if (nbr_entries) {
- BLI_mutex_lock(lock);
+ BLI_mutex_lock(&job_params->lock);
*do_update = true;
BLI_movelisttolist(&filelist->filelist.entries, &entries);
filelist->filelist.nbr_entries += nbr_entries;
- BLI_mutex_unlock(lock);
+ BLI_mutex_unlock(&job_params->lock);
}
nbr_done_dirs++;
@@ -3261,51 +3264,40 @@ static void filelist_readjob_do(const bool do_lib,
BLI_stack_free(todo_dirs);
}
-static void filelist_readjob_dir(Main *UNUSED(current_main),
- FileList *filelist,
- const char *main_name,
+static void filelist_readjob_dir(FileListReadJob *job_params,
short *stop,
short *do_update,
- float *progress,
- ThreadMutex *lock)
+ float *progress)
{
- filelist_readjob_do(false, filelist, main_name, stop, do_update, progress, lock);
+ filelist_readjob_do(false, job_params, stop, do_update, progress);
}
-static void filelist_readjob_lib(Main *UNUSED(current_main),
- FileList *filelist,
- const char *main_name,
+static void filelist_readjob_lib(FileListReadJob *job_params,
short *stop,
short *do_update,
- float *progress,
- ThreadMutex *lock)
+ float *progress)
{
- filelist_readjob_do(true, filelist, main_name, stop, do_update, progress, lock);
+ filelist_readjob_do(true, job_params, stop, do_update, progress);
}
-static void filelist_readjob_main(Main *current_main,
- FileList *filelist,
- const char *main_name,
+static void filelist_readjob_main(FileListReadJob *job_params,
short *stop,
short *do_update,
- float *progress,
- ThreadMutex *lock)
+ float *progress)
{
/* TODO! */
- filelist_readjob_dir(current_main, filelist, main_name, stop, do_update, progress, lock);
+ filelist_readjob_dir(job_params, stop, do_update, progress);
}
/**
* \warning Acts on main, so NOT thread-safe!
*/
-static void filelist_readjob_main_assets(Main *current_main,
- FileList *filelist,
- const char *UNUSED(main_name),
+static void filelist_readjob_main_assets(FileListReadJob *job_params,
short *UNUSED(stop),
short *do_update,
- float *UNUSED(progress),
- ThreadMutex *UNUSED(lock))
+ float *UNUSED(progress))
{
+ FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */
BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) &&
(filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET));
@@ -3317,7 +3309,7 @@ static void filelist_readjob_main_assets(Main *current_main,
ID *id_iter;
int nbr_entries = 0;
- FOREACH_MAIN_ID_BEGIN (current_main, id_iter) {
+ FOREACH_MAIN_ID_BEGIN (job_params->current_main, id_iter) {
if (!id_iter->asset_data) {
continue;
}
@@ -3349,15 +3341,6 @@ static void filelist_readjob_main_assets(Main *current_main,
}
}
-typedef struct FileListReadJob {
- ThreadMutex lock;
- char main_name[FILE_MAX];
- Main *current_main;
- struct FileList *filelist;
- /** XXX We may use a simpler struct here... just a linked list and root path? */
- struct FileList *tmp_filelist;
-} FileListReadJob;
-
static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update, float *progress)
{
FileListReadJob *flrj = flrjv;
@@ -3381,17 +3364,11 @@ static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update
flrj->tmp_filelist->libfiledata = NULL;
memset(&flrj->tmp_filelist->filelist_cache, 0, sizeof(flrj->tmp_filelist->filelist_cache));
flrj->tmp_filelist->selection_state = NULL;
- flrj->tmp_filelist->asset_library = NULL;
+ flrj->tmp_filelist->asset_library_ref = NULL;
BLI_mutex_unlock(&flrj->lock);
- flrj->tmp_filelist->read_job_fn(flrj->current_main,
- flrj->tmp_filelist,
- flrj->main_name,
- stop,
- do_update,
- progress,
- &flrj->lock);
+ flrj->tmp_filelist->read_job_fn(flrj, stop, do_update, progress);
}
static void filelist_readjob_update(void *flrjv)
diff --git a/source/blender/editors/space_file/filelist.h b/source/blender/editors/space_file/filelist.h
index 6915e853681..d67cd89200b 100644
--- a/source/blender/editors/space_file/filelist.h
+++ b/source/blender/editors/space_file/filelist.h
@@ -73,7 +73,7 @@ void filelist_setfilter_options(struct FileList *filelist,
const char *filter_search);
void filelist_filter(struct FileList *filelist);
void filelist_setlibrary(struct FileList *filelist,
- const struct AssetLibraryReference *asset_library);
+ const struct AssetLibraryReference *asset_library_ref);
void filelist_init_icons(void);
void filelist_free_icons(void);
diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c
index 89142b6669b..4ab7014cf82 100644
--- a/source/blender/editors/space_file/filesel.c
+++ b/source/blender/editors/space_file/filesel.c
@@ -118,8 +118,8 @@ static void fileselect_ensure_updated_asset_params(SpaceFile *sfile)
asset_params = sfile->asset_params = MEM_callocN(sizeof(*asset_params),
"FileAssetSelectParams");
asset_params->base_params.details_flags = U_default.file_space_data.details_flags;
- asset_params->asset_library.type = ASSET_LIBRARY_LOCAL;
- asset_params->asset_library.custom_library_index = -1;
+ asset_params->asset_library_ref.type = ASSET_LIBRARY_LOCAL;
+ asset_params->asset_library_ref.custom_library_index = -1;
asset_params->import_type = FILE_ASSET_IMPORT_APPEND;
}
@@ -415,7 +415,7 @@ FileAssetSelectParams *ED_fileselect_get_asset_params(const SpaceFile *sfile)
static void fileselect_refresh_asset_params(FileAssetSelectParams *asset_params)
{
- AssetLibraryReference *library = &asset_params->asset_library;
+ AssetLibraryReference *library = &asset_params->asset_library_ref;
FileSelectParams *base_params = &asset_params->base_params;
bUserAssetLibrary *user_library = NULL;
@@ -863,20 +863,8 @@ FileAttributeColumnType file_attribute_column_type_find_isect(const View2D *v2d,
float file_string_width(const char *str)
{
const uiStyle *style = UI_style_get();
- float width;
-
UI_fontstyle_set(&style->widget);
- if (style->widget.kerning == 1) { /* for BLF_width */
- BLF_enable(style->widget.uifont_id, BLF_KERNING_DEFAULT);
- }
-
- width = BLF_width(style->widget.uifont_id, str, BLF_DRAW_STR_DUMMY_MAX);
-
- if (style->widget.kerning == 1) {
- BLF_disable(style->widget.uifont_id, BLF_KERNING_DEFAULT);
- }
-
- return width;
+ return BLF_width(style->widget.uifont_id, str, BLF_DRAW_STR_DUMMY_MAX);
}
float file_font_pointsize(void)
diff --git a/source/blender/editors/space_file/fsmenu.c b/source/blender/editors/space_file/fsmenu.c
index 2d1151c8f4d..776bb0b3bb7 100644
--- a/source/blender/editors/space_file/fsmenu.c
+++ b/source/blender/editors/space_file/fsmenu.c
@@ -969,7 +969,7 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks)
BLI_join_dirfile(name, sizeof(name), xdg_runtime_dir, "gvfs/");
const uint dir_len = BLI_filelist_dir_contents(name, &dir);
for (uint i = 0; i < dir_len; i++) {
- if ((dir[i].type & S_IFDIR)) {
+ if (dir[i].type & S_IFDIR) {
const char *dirname = dir[i].relname;
if (dirname[0] != '.') {
/* Dir names contain a lot of unwanted text.
diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c
index 46cc96ba0d4..7deaa2fec60 100644
--- a/source/blender/editors/space_file/space_file.c
+++ b/source/blender/editors/space_file/space_file.c
@@ -1,4 +1,4 @@
-/*
+/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
@@ -176,10 +176,7 @@ static void file_free(SpaceLink *sl)
MEM_SAFE_FREE(sfile->asset_params);
MEM_SAFE_FREE(sfile->runtime);
- if (sfile->layout) {
- MEM_freeN(sfile->layout);
- sfile->layout = NULL;
- }
+ MEM_SAFE_FREE(sfile->layout);
}
/* spacetype; init callback, area size changes, screen set, etc */
@@ -337,11 +334,17 @@ static void file_refresh(const bContext *C, ScrArea *area)
sfile->files = filelist_new(params->type);
params->highlight_file = -1; /* added this so it opens nicer (ton) */
}
+
+ if (!U.experimental.use_extended_asset_browser && ED_fileselect_is_asset_browser(sfile)) {
+ /* Only poses supported as non-experimental right now. */
+ params->filter_id = FILTER_ID_AC;
+ }
+
filelist_settype(sfile->files, params->type);
filelist_setdir(sfile->files, params->dir);
filelist_setrecursion(sfile->files, params->recursion_level);
filelist_setsorting(sfile->files, params->sort, params->flag & FILE_SORT_INVERT);
- filelist_setlibrary(sfile->files, asset_params ? &asset_params->asset_library : NULL);
+ filelist_setlibrary(sfile->files, asset_params ? &asset_params->asset_library_ref : NULL);
filelist_setfilter_options(
sfile->files,
(params->flag & FILE_FILTER) != 0,
@@ -578,6 +581,16 @@ static void file_main_region_message_subscribe(const wmRegionMessageSubscribePar
/* All properties for this space type. */
WM_msg_subscribe_rna(mbus, &ptr, NULL, &msg_sub_value_area_tag_refresh, __func__);
}
+
+ /* Experimental Asset Browser features option. */
+ {
+ PointerRNA ptr;
+ RNA_pointer_create(NULL, &RNA_PreferencesExperimental, &U.experimental, &ptr);
+ PropertyRNA *prop = RNA_struct_find_property(&ptr, "use_extended_asset_browser");
+
+ /* All properties for this space type. */
+ WM_msg_subscribe_rna(mbus, &ptr, prop, &msg_sub_value_area_tag_refresh, __func__);
+ }
}
static bool file_main_region_needs_refresh_before_draw(SpaceFile *sfile)
@@ -815,10 +828,7 @@ static void file_ui_region_listener(const wmRegionListenerParams *listener_param
}
}
-static bool filepath_drop_poll(bContext *C,
- wmDrag *drag,
- const wmEvent *UNUSED(event),
- const char **UNUSED(r_tooltip))
+static bool filepath_drop_poll(bContext *C, wmDrag *drag, const wmEvent *UNUSED(event))
{
if (drag->type == WM_DRAG_PATH) {
SpaceFile *sfile = CTX_wm_space_file(C);
@@ -839,7 +849,7 @@ static void file_dropboxes(void)
{
ListBase *lb = WM_dropboxmap_find("Window", SPACE_EMPTY, RGN_TYPE_WINDOW);
- WM_dropbox_add(lb, "FILE_OT_filepath_drop", filepath_drop_poll, filepath_drop_copy, NULL);
+ WM_dropbox_add(lb, "FILE_OT_filepath_drop", filepath_drop_poll, filepath_drop_copy, NULL, NULL);
}
static int file_space_subtype_get(ScrArea *area)
@@ -858,18 +868,12 @@ static void file_space_subtype_item_extend(bContext *UNUSED(C),
EnumPropertyItem **item,
int *totitem)
{
- if (U.experimental.use_asset_browser) {
- RNA_enum_items_add(item, totitem, rna_enum_space_file_browse_mode_items);
- }
- else {
- RNA_enum_items_add_value(
- item, totitem, rna_enum_space_file_browse_mode_items, FILE_BROWSE_MODE_FILES);
- }
+ RNA_enum_items_add(item, totitem, rna_enum_space_file_browse_mode_items);
}
static const char *file_context_dir[] = {
"active_file",
- "asset_library",
+ "asset_library_ref",
"id",
NULL,
};
@@ -903,14 +907,14 @@ static int /*eContextResult*/ file_context(const bContext *C,
CTX_data_pointer_set(result, &screen->id, &RNA_FileSelectEntry, file);
return CTX_RESULT_OK;
}
- if (CTX_data_equals(member, "asset_library")) {
+ if (CTX_data_equals(member, "asset_library_ref")) {
FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile);
if (!asset_params) {
return CTX_RESULT_NO_DATA;
}
CTX_data_pointer_set(
- result, &screen->id, &RNA_AssetLibraryReference, &asset_params->asset_library);
+ result, &screen->id, &RNA_AssetLibraryReference, &asset_params->asset_library_ref);
return CTX_RESULT_OK;
}
if (CTX_data_equals(member, "id")) {
diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c
index ec5f443e2dc..f4c4b6cafcd 100644
--- a/source/blender/editors/space_graph/graph_buttons.c
+++ b/source/blender/editors/space_graph/graph_buttons.c
@@ -364,7 +364,7 @@ static void graph_panel_key_properties(const bContext *C, Panel *panel)
}
block = uiLayoutGetBlock(layout);
- /* UI_block_func_handle_set(block, do_graph_region_buttons, NULL); */
+ // UI_block_func_handle_set(block, do_graph_region_buttons, NULL);
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c
index dad354ba8ee..29c1452b988 100644
--- a/source/blender/editors/space_image/image_ops.c
+++ b/source/blender/editors/space_image/image_ops.c
@@ -1289,8 +1289,10 @@ static Image *image_open_single(Main *bmain,
}
if ((range->length > 1) && (ima->source == IMA_SRC_FILE)) {
- if (range->udim_tiles.first && range->offset == 1001) {
+ if (range->udim_tiles.first) {
ima->source = IMA_SRC_TILED;
+ ImageTile *first_tile = ima->tiles.first;
+ first_tile->tile_number = range->offset;
LISTBASE_FOREACH (LinkData *, node, &range->udim_tiles) {
BKE_image_add_tile(ima, POINTER_AS_INT(node->data), NULL);
}
@@ -1806,10 +1808,13 @@ static int image_save_options_init(Main *bmain,
}
/* append UDIM numbering if not present */
- if (ima->source == IMA_SRC_TILED &&
- (BLI_path_sequence_decode(ima->filepath, NULL, NULL, NULL) != 1001)) {
+ if (ima->source == IMA_SRC_TILED) {
+ char udim[6];
+ ImageTile *tile = ima->tiles.first;
+ BLI_snprintf(udim, sizeof(udim), ".%d", tile->tile_number);
+
int len = strlen(opts->filepath);
- STR_CONCAT(opts->filepath, len, ".1001");
+ STR_CONCAT(opts->filepath, len, udim);
}
}
@@ -2508,10 +2513,7 @@ static ImageNewData *image_new_init(bContext *C, wmOperator *op)
static void image_new_free(wmOperator *op)
{
- if (op->customdata) {
- MEM_freeN(op->customdata);
- op->customdata = NULL;
- }
+ MEM_SAFE_FREE(op->customdata);
}
static int image_new_exec(bContext *C, wmOperator *op)
@@ -3871,9 +3873,9 @@ static void tile_fill_init(PointerRNA *ptr, Image *ima, ImageTile *tile)
/* Acquire ibuf to get the default values.
* If the specified tile has no ibuf, try acquiring the main tile instead
- * (unless the specified tile already was the main tile). */
+ * (unless the specified tile already was the first tile). */
ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL);
- if (ibuf == NULL && (tile != NULL) && (tile->tile_number != 1001)) {
+ if (ibuf == NULL && (tile != NULL) && (tile != ima->tiles.first)) {
ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
}
@@ -3925,7 +3927,7 @@ static int tile_add_exec(bContext *C, wmOperator *op)
Image *ima = CTX_data_edit_image(C);
int start_tile = RNA_int_get(op->ptr, "number");
- int end_tile = start_tile + RNA_int_get(op->ptr, "count");
+ int end_tile = start_tile + RNA_int_get(op->ptr, "count") - 1;
if (start_tile < 1001 || end_tile > IMA_UDIM_MAX) {
BKE_report(op->reports, RPT_ERROR, "Invalid UDIM index range was specified");
@@ -3935,26 +3937,30 @@ static int tile_add_exec(bContext *C, wmOperator *op)
bool fill_tile = RNA_boolean_get(op->ptr, "fill");
char *label = RNA_string_get_alloc(op->ptr, "label", NULL, 0);
- bool created_tile = false;
- for (int tile_number = start_tile; tile_number < end_tile; tile_number++) {
+ /* BKE_image_add_tile assumes a pre-sorted list of tiles. */
+ BKE_image_sort_tiles(ima);
+
+ ImageTile *last_tile_created = NULL;
+ for (int tile_number = start_tile; tile_number <= end_tile; tile_number++) {
ImageTile *tile = BKE_image_add_tile(ima, tile_number, label);
if (tile != NULL) {
- ima->active_tile_index = BLI_findindex(&ima->tiles, tile);
-
if (fill_tile) {
do_fill_tile(op->ptr, ima, tile);
}
- created_tile = true;
+ last_tile_created = tile;
}
}
MEM_freeN(label);
- if (!created_tile) {
+ if (!last_tile_created) {
+ BKE_report(op->reports, RPT_WARNING, "No UDIM tiles were created");
return OPERATOR_CANCELLED;
}
+ ima->active_tile_index = BLI_findindex(&ima->tiles, last_tile_created);
+
WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL);
return OPERATOR_FINISHED;
}
@@ -4043,7 +4049,7 @@ static bool tile_remove_poll(bContext *C)
{
Image *ima = CTX_data_edit_image(C);
- return (ima != NULL && ima->source == IMA_SRC_TILED && ima->active_tile_index != 0);
+ return (ima != NULL && ima->source == IMA_SRC_TILED && !BLI_listbase_is_single(&ima->tiles));
}
static int tile_remove_exec(bContext *C, wmOperator *UNUSED(op))
diff --git a/source/blender/editors/space_image/image_sequence.c b/source/blender/editors/space_image/image_sequence.c
index 02546e3e3b3..288b3d94b1d 100644
--- a/source/blender/editors/space_image/image_sequence.c
+++ b/source/blender/editors/space_image/image_sequence.c
@@ -124,7 +124,7 @@ static int image_cmp_frame(const void *a, const void *b)
*
* udim_tiles may get filled even if the result ultimately is false!
*/
-static int image_get_udim(char *filepath, ListBase *udim_tiles)
+static bool image_get_udim(char *filepath, ListBase *udim_tiles, int *udim_start, int *udim_range)
{
char filename[FILE_MAX], dirname[FILE_MAXDIR];
BLI_split_dirfile(filepath, dirname, filename, sizeof(dirname), sizeof(filename));
@@ -133,12 +133,12 @@ static int image_get_udim(char *filepath, ListBase *udim_tiles)
char base_head[FILE_MAX], base_tail[FILE_MAX];
int id = BLI_path_sequence_decode(filename, base_head, base_tail, &digits);
- if (id < 1001 || id >= IMA_UDIM_MAX) {
- return 0;
+ if (id < 1001 || id > IMA_UDIM_MAX) {
+ return false;
}
bool is_udim = true;
- bool has_primary = false;
+ int min_udim = IMA_UDIM_MAX + 1;
int max_udim = 0;
struct direntry *dir;
@@ -155,26 +155,27 @@ static int image_get_udim(char *filepath, ListBase *udim_tiles)
continue;
}
- if (id < 1001 || id >= IMA_UDIM_MAX) {
+ if (id < 1001 || id > IMA_UDIM_MAX) {
is_udim = false;
break;
}
- if (id == 1001) {
- has_primary = true;
- }
BLI_addtail(udim_tiles, BLI_genericNodeN(POINTER_FROM_INT(id)));
+ min_udim = min_ii(min_udim, id);
max_udim = max_ii(max_udim, id);
}
BLI_filelist_free(dir, totfile);
- if (is_udim && has_primary) {
+ if (is_udim && min_udim <= IMA_UDIM_MAX) {
char primary_filename[FILE_MAX];
- BLI_path_sequence_encode(primary_filename, base_head, base_tail, digits, 1001);
+ BLI_path_sequence_encode(primary_filename, base_head, base_tail, digits, min_udim);
BLI_join_dirfile(filepath, FILE_MAX, dirname, primary_filename);
- return max_udim - 1000;
+
+ *udim_start = min_udim;
+ *udim_range = max_udim - min_udim + 1;
+ return true;
}
- return 0;
+ return false;
}
/**
@@ -185,11 +186,12 @@ static void image_detect_frame_range(ImageFrameRange *range, const bool detect_u
{
/* UDIM */
if (detect_udim) {
- int len_udim = image_get_udim(range->filepath, &range->udim_tiles);
+ int udim_start, udim_range;
+ bool result = image_get_udim(range->filepath, &range->udim_tiles, &udim_start, &udim_range);
- if (len_udim > 0) {
- range->offset = 1001;
- range->length = len_udim;
+ if (result) {
+ range->offset = udim_start;
+ range->length = udim_range;
return;
}
}
diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c
index 5a03b4f6ef0..4107fd619aa 100644
--- a/source/blender/editors/space_image/space_image.c
+++ b/source/blender/editors/space_image/space_image.c
@@ -253,10 +253,7 @@ static void image_keymap(struct wmKeyConfig *keyconf)
}
/* dropboxes */
-static bool image_drop_poll(bContext *C,
- wmDrag *drag,
- const wmEvent *event,
- const char **UNUSED(r_tooltip))
+static bool image_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
{
ScrArea *area = CTX_wm_area(C);
if (ED_region_overlap_isect_any_xy(area, &event->x)) {
@@ -282,7 +279,7 @@ static void image_dropboxes(void)
{
ListBase *lb = WM_dropboxmap_find("Image", SPACE_IMAGE, 0);
- WM_dropbox_add(lb, "IMAGE_OT_open", image_drop_poll, image_drop_copy, NULL);
+ WM_dropbox_add(lb, "IMAGE_OT_open", image_drop_poll, image_drop_copy, NULL, NULL);
}
/**
diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c
index d7671a372c6..e749e1a7947 100644
--- a/source/blender/editors/space_info/info_stats.c
+++ b/source/blender/editors/space_info/info_stats.c
@@ -466,10 +466,7 @@ static void stats_update(Depsgraph *depsgraph,
void ED_info_stats_clear(wmWindowManager *wm, ViewLayer *view_layer)
{
- if (view_layer->stats) {
- MEM_freeN(view_layer->stats);
- view_layer->stats = NULL;
- }
+ MEM_SAFE_FREE(view_layer->stats);
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
ViewLayer *view_layer_test = WM_window_get_active_view_layer(win);
@@ -764,6 +761,7 @@ void ED_info_draw_stats(
FRAMES,
STROKES,
POINTS,
+ LIGHTS,
MAX_LABELS_COUNT
};
char labels[MAX_LABELS_COUNT][64];
@@ -779,6 +777,7 @@ void ED_info_draw_stats(
STRNCPY(labels[FRAMES], IFACE_("Frames"));
STRNCPY(labels[STROKES], IFACE_("Strokes"));
STRNCPY(labels[POINTS], IFACE_("Points"));
+ STRNCPY(labels[LIGHTS], IFACE_("Lights"));
int longest_label = 0;
int i;
@@ -832,6 +831,9 @@ void ED_info_draw_stats(
stats_row(col1, labels[FACES], col2, stats_fmt.totfacesculpt, stats_fmt.totface, y, height);
}
}
+ else if ((ob) && (ob->type == OB_LAMP)) {
+ stats_row(col1, labels[LIGHTS], col2, stats_fmt.totlampsel, stats_fmt.totlamp, y, height);
+ }
else {
stats_row(col1, labels[VERTS], col2, stats_fmt.totvert, NULL, y, height);
stats_row(col1, labels[EDGES], col2, stats_fmt.totedge, NULL, y, height);
diff --git a/source/blender/editors/space_nla/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c
index 0498964c549..1b87a8c6b9d 100644
--- a/source/blender/editors/space_nla/nla_channels.c
+++ b/source/blender/editors/space_nla/nla_channels.c
@@ -562,7 +562,8 @@ void NLA_OT_action_pushdown(wmOperatorType *ot)
static bool nla_action_unlink_poll(bContext *C)
{
if (ED_operator_nla_active(C)) {
- return nla_panel_context(C, NULL, NULL, NULL);
+ PointerRNA adt_ptr;
+ return (nla_panel_context(C, &adt_ptr, NULL, NULL) && (adt_ptr.data != NULL));
}
/* something failed... */
diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c
index c96047da0c8..c1b308d213f 100644
--- a/source/blender/editors/space_nla/nla_draw.c
+++ b/source/blender/editors/space_nla/nla_draw.c
@@ -35,6 +35,7 @@
#include "BLI_blenlib.h"
#include "BLI_dlrbTree.h"
+#include "BLI_range.h"
#include "BLI_utildefines.h"
#include "BKE_context.h"
@@ -95,12 +96,16 @@ void nla_action_get_color(AnimData *adt, bAction *act, float color[4])
static void nla_action_draw_keyframes(
View2D *v2d, AnimData *adt, bAction *act, float y, float ymin, float ymax)
{
+ if (act == NULL) {
+ return;
+ }
+
/* get a list of the keyframes with NLA-scaling applied */
- DLRBT_Tree keys;
- BLI_dlrbTree_init(&keys);
- action_to_keylist(adt, act, &keys, 0);
+ struct AnimKeylist *keylist = ED_keylist_create();
+ action_to_keylist(adt, act, keylist, 0);
- if (ELEM(NULL, act, keys.first)) {
+ if (ED_keylist_is_empty(keylist)) {
+ ED_keylist_free(keylist);
return;
}
@@ -122,25 +127,29 @@ static void nla_action_draw_keyframes(
/* - draw a rect from the first to the last frame (no extra overlaps for now)
* that is slightly stumpier than the track background (hardcoded 2-units here)
*/
- float f1 = ((ActKeyColumn *)keys.first)->cfra;
- float f2 = ((ActKeyColumn *)keys.last)->cfra;
- immRectf(pos_id, f1, ymin + 2, f2, ymax - 2);
+ Range2f frame_range;
+ ED_keylist_frame_range(keylist, &frame_range);
+ immRectf(pos_id, frame_range.min, ymin + 2, frame_range.max, ymax - 2);
immUnbindProgram();
/* Count keys before drawing. */
/* NOTE: It's safe to cast #DLRBT_Tree, as it's designed to degrade down to a #ListBase. */
- uint key_len = BLI_listbase_count((ListBase *)&keys);
+ const ListBase *keys = ED_keylist_listbase(keylist);
+ uint key_len = BLI_listbase_count(keys);
if (key_len > 0) {
format = immVertexFormat();
- pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- uint size_id = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
- uint color_id = GPU_vertformat_attr_add(
+ KeyframeShaderBindings sh_bindings;
+ sh_bindings.pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ sh_bindings.size_id = GPU_vertformat_attr_add(
+ format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+ sh_bindings.color_id = GPU_vertformat_attr_add(
format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
- uint outline_color_id = GPU_vertformat_attr_add(
+ sh_bindings.outline_color_id = GPU_vertformat_attr_add(
format, "outlineColor", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
- uint flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT);
+ sh_bindings.flags_id = GPU_vertformat_attr_add(
+ format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT);
GPU_program_point_size(true);
immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND);
@@ -151,7 +160,7 @@ static void nla_action_draw_keyframes(
/* - disregard the selection status of keyframes so they draw a certain way
* - size is 6.0f which is smaller than the editable keyframes, so that there is a distinction
*/
- LISTBASE_FOREACH (ActKeyColumn *, ak, &keys) {
+ LISTBASE_FOREACH (const ActKeyColumn *, ak, keys) {
draw_keyframe_shape(ak->cfra,
y,
6.0f,
@@ -159,11 +168,7 @@ static void nla_action_draw_keyframes(
ak->key_type,
KEYFRAME_SHAPE_FRAME,
1.0f,
- pos_id,
- size_id,
- color_id,
- outline_color_id,
- flags_id,
+ &sh_bindings,
KEYFRAME_HANDLE_NONE,
KEYFRAME_EXTREME_NONE);
}
@@ -174,7 +179,7 @@ static void nla_action_draw_keyframes(
}
/* free icons */
- BLI_dlrbTree_free(&keys);
+ ED_keylist_free(keylist);
}
/* Strip Markers ------------------------ */
diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c
index 56efcd8571f..c75b874833a 100644
--- a/source/blender/editors/space_nla/nla_edit.c
+++ b/source/blender/editors/space_nla/nla_edit.c
@@ -241,9 +241,7 @@ bool nlaedit_disable_tweakmode(bAnimContext *ac, bool do_solo)
ANIM_animdata_update(ac, &anim_data);
ANIM_animdata_freelist(&anim_data);
- /* if we managed to enter tweak-mode on at least one AnimData block,
- * set the flag for this in the active scene and send notifiers
- */
+ /* Clear the tweak-mode flag in the active scene and send notifiers. */
if (ac->scene) {
/* clear editing flag */
ac->scene->flag &= ~SCE_NLA_EDIT_ON;
diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc
index 95eb1ccc025..0f7a911e3ce 100644
--- a/source/blender/editors/space_node/drawnode.cc
+++ b/source/blender/editors/space_node/drawnode.cc
@@ -3596,8 +3596,26 @@ static void std_node_socket_draw(
break;
}
case SOCK_TEXTURE: {
- uiTemplateID(
- layout, C, ptr, "default_value", "texture.new", nullptr, nullptr, 0, ICON_NONE, nullptr);
+ if (text[0] == '\0') {
+ uiTemplateID(layout,
+ C,
+ ptr,
+ "default_value",
+ "texture.new",
+ nullptr,
+ nullptr,
+ 0,
+ ICON_NONE,
+ nullptr);
+ }
+ else {
+ /* 0.3 split ratio is inconsistent, but use it here because the "New" button is large. */
+ uiLayout *row = uiLayoutSplit(layout, 0.3f, false);
+ uiItemL(row, text, 0);
+ uiTemplateID(
+ row, C, ptr, "default_value", "texture.new", nullptr, nullptr, 0, ICON_NONE, nullptr);
+ }
+
break;
}
case SOCK_MATERIAL: {
diff --git a/source/blender/editors/space_node/node_select.cc b/source/blender/editors/space_node/node_select.cc
index 6c07c41f451..a1068f29624 100644
--- a/source/blender/editors/space_node/node_select.cc
+++ b/source/blender/editors/space_node/node_select.cc
@@ -664,7 +664,8 @@ void NODE_OT_select(wmOperatorType *ot)
/* properties */
WM_operator_properties_generic_select(ot);
- RNA_def_boolean(ot->srna, "extend", false, "Extend", "");
+ prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
RNA_def_boolean(ot->srna, "socket_select", false, "Socket Select", "");
prop = RNA_def_boolean(ot->srna,
"deselect_all",
diff --git a/source/blender/editors/space_node/space_node.c b/source/blender/editors/space_node/space_node.c
index ff848a7bb95..956fb3aa867 100644
--- a/source/blender/editors/space_node/space_node.c
+++ b/source/blender/editors/space_node/space_node.c
@@ -664,42 +664,29 @@ static void node_main_region_draw(const bContext *C, ARegion *region)
/* ************* dropboxes ************* */
-static bool node_group_drop_poll(bContext *UNUSED(C),
- wmDrag *drag,
- const wmEvent *UNUSED(event),
- const char **UNUSED(r_tooltip))
+static bool node_group_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event))
{
return WM_drag_is_ID_type(drag, ID_NT);
}
-static bool node_object_drop_poll(bContext *UNUSED(C),
- wmDrag *drag,
- const wmEvent *UNUSED(event),
- const char **UNUSED(r_tooltip))
+static bool node_object_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event))
{
return WM_drag_is_ID_type(drag, ID_OB);
}
static bool node_collection_drop_poll(bContext *UNUSED(C),
wmDrag *drag,
- const wmEvent *UNUSED(event),
- const char **UNUSED(r_tooltip))
+ const wmEvent *UNUSED(event))
{
return WM_drag_is_ID_type(drag, ID_GR);
}
-static bool node_texture_drop_poll(bContext *UNUSED(C),
- wmDrag *drag,
- const wmEvent *UNUSED(event),
- const char **UNUSED(r_tooltip))
+static bool node_texture_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event))
{
return WM_drag_is_ID_type(drag, ID_TE);
}
-static bool node_ima_drop_poll(bContext *UNUSED(C),
- wmDrag *drag,
- const wmEvent *UNUSED(event),
- const char **UNUSED(r_tooltip))
+static bool node_ima_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event))
{
if (drag->type == WM_DRAG_PATH) {
/* rule might not work? */
@@ -708,10 +695,7 @@ static bool node_ima_drop_poll(bContext *UNUSED(C),
return WM_drag_is_ID_type(drag, ID_IM);
}
-static bool node_mask_drop_poll(bContext *UNUSED(C),
- wmDrag *drag,
- const wmEvent *UNUSED(event),
- const char **UNUSED(r_tooltip))
+static bool node_mask_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event))
{
return WM_drag_is_ID_type(drag, ID_MSK);
}
@@ -753,32 +737,38 @@ static void node_dropboxes(void)
"NODE_OT_add_object",
node_object_drop_poll,
node_id_drop_copy,
- WM_drag_free_imported_drag_ID);
+ WM_drag_free_imported_drag_ID,
+ NULL);
WM_dropbox_add(lb,
"NODE_OT_add_collection",
node_collection_drop_poll,
node_id_drop_copy,
- WM_drag_free_imported_drag_ID);
+ WM_drag_free_imported_drag_ID,
+ NULL);
WM_dropbox_add(lb,
"NODE_OT_add_texture",
node_texture_drop_poll,
node_id_drop_copy,
- WM_drag_free_imported_drag_ID);
+ WM_drag_free_imported_drag_ID,
+ NULL);
WM_dropbox_add(lb,
"NODE_OT_add_group",
node_group_drop_poll,
node_group_drop_copy,
- WM_drag_free_imported_drag_ID);
+ WM_drag_free_imported_drag_ID,
+ NULL);
WM_dropbox_add(lb,
"NODE_OT_add_file",
node_ima_drop_poll,
node_id_path_drop_copy,
- WM_drag_free_imported_drag_ID);
+ WM_drag_free_imported_drag_ID,
+ NULL);
WM_dropbox_add(lb,
"NODE_OT_add_mask",
node_mask_drop_poll,
node_id_drop_copy,
- WM_drag_free_imported_drag_ID);
+ WM_drag_free_imported_drag_ID,
+ NULL);
}
/* ************* end drop *********** */
diff --git a/source/blender/editors/space_outliner/outliner_collections.c b/source/blender/editors/space_outliner/outliner_collections.c
index 6538f5709b7..1ec1afe86fc 100644
--- a/source/blender/editors/space_outliner/outliner_collections.c
+++ b/source/blender/editors/space_outliner/outliner_collections.c
@@ -1263,22 +1263,22 @@ static bool collection_flag_poll(bContext *C, bool clear, int flag)
static bool collection_enable_poll(bContext *C)
{
- return collection_flag_poll(C, true, COLLECTION_RESTRICT_VIEWPORT);
+ return collection_flag_poll(C, true, COLLECTION_HIDE_VIEWPORT);
}
static bool collection_disable_poll(bContext *C)
{
- return collection_flag_poll(C, false, COLLECTION_RESTRICT_VIEWPORT);
+ return collection_flag_poll(C, false, COLLECTION_HIDE_VIEWPORT);
}
static bool collection_enable_render_poll(bContext *C)
{
- return collection_flag_poll(C, true, COLLECTION_RESTRICT_RENDER);
+ return collection_flag_poll(C, true, COLLECTION_HIDE_RENDER);
}
static bool collection_disable_render_poll(bContext *C)
{
- return collection_flag_poll(C, false, COLLECTION_RESTRICT_RENDER);
+ return collection_flag_poll(C, false, COLLECTION_HIDE_RENDER);
}
static int collection_flag_exec(bContext *C, wmOperator *op)
@@ -1288,7 +1288,7 @@ static int collection_flag_exec(bContext *C, wmOperator *op)
SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
const bool is_render = strstr(op->idname, "render");
const bool clear = strstr(op->idname, "show") || strstr(op->idname, "enable");
- int flag = is_render ? COLLECTION_RESTRICT_RENDER : COLLECTION_RESTRICT_VIEWPORT;
+ int flag = is_render ? COLLECTION_HIDE_RENDER : COLLECTION_HIDE_VIEWPORT;
struct CollectionEditData data = {
.scene = scene,
.space_outliner = space_outliner,
diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.c b/source/blender/editors/space_outliner/outliner_dragdrop.c
index 86aab86db10..a82f516b125 100644
--- a/source/blender/editors/space_outliner/outliner_dragdrop.c
+++ b/source/blender/editors/space_outliner/outliner_dragdrop.c
@@ -31,6 +31,7 @@
#include "DNA_space_types.h"
#include "BLI_listbase.h"
+#include "BLI_string.h"
#include "BLT_translation.h"
@@ -316,10 +317,7 @@ static bool allow_parenting_without_modifier_key(SpaceOutliner *space_outliner)
}
}
-static bool parent_drop_poll(bContext *C,
- wmDrag *drag,
- const wmEvent *event,
- const char **UNUSED(r_tooltip))
+static bool parent_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
{
SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
@@ -455,10 +453,7 @@ void OUTLINER_OT_parent_drop(wmOperatorType *ot)
/* ******************** Parent Clear Operator *********************** */
-static bool parent_clear_poll(bContext *C,
- wmDrag *drag,
- const wmEvent *event,
- const char **UNUSED(r_tooltip))
+static bool parent_clear_poll(bContext *C, wmDrag *drag, const wmEvent *event)
{
SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
@@ -541,10 +536,7 @@ void OUTLINER_OT_parent_clear(wmOperatorType *ot)
/* ******************** Scene Drop Operator *********************** */
-static bool scene_drop_poll(bContext *C,
- wmDrag *drag,
- const wmEvent *event,
- const char **UNUSED(r_tooltip))
+static bool scene_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
{
/* Ensure item under cursor is valid drop target */
Object *ob = (Object *)WM_drag_get_local_ID(drag, ID_OB);
@@ -609,10 +601,7 @@ void OUTLINER_OT_scene_drop(wmOperatorType *ot)
/* ******************** Material Drop Operator *********************** */
-static bool material_drop_poll(bContext *C,
- wmDrag *drag,
- const wmEvent *event,
- const char **UNUSED(r_tooltip))
+static bool material_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
{
/* Ensure item under cursor is valid drop target */
Material *ma = (Material *)WM_drag_get_local_ID(drag, ID_MA);
@@ -833,10 +822,7 @@ static bool datastack_drop_are_types_valid(StackDropData *drop_data)
return true;
}
-static bool datastack_drop_poll(bContext *C,
- wmDrag *drag,
- const wmEvent *event,
- const char **r_tooltip)
+static bool datastack_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
{
if (drag->type != WM_DRAG_DATASTACK) {
return false;
@@ -873,33 +859,41 @@ static bool datastack_drop_poll(bContext *C,
break;
}
+ if (changed) {
+ ED_region_tag_redraw_no_rebuild(region);
+ }
+
+ return true;
+}
+
+static char *datastack_drop_tooltip(bContext *UNUSED(C),
+ wmDrag *drag,
+ const wmEvent *UNUSED(event),
+ struct wmDropBox *UNUSED(drop))
+{
+ StackDropData *drop_data = drag->poin;
switch (drop_data->drop_action) {
case DATA_STACK_DROP_REORDER:
- *r_tooltip = TIP_("Reorder");
+ return BLI_strdup(TIP_("Reorder"));
break;
case DATA_STACK_DROP_COPY:
if (drop_data->pchan_parent) {
- *r_tooltip = TIP_("Copy to bone");
+ return BLI_strdup(TIP_("Copy to bone"));
}
else {
- *r_tooltip = TIP_("Copy to object");
+ return BLI_strdup(TIP_("Copy to object"));
}
break;
case DATA_STACK_DROP_LINK:
if (drop_data->pchan_parent) {
- *r_tooltip = TIP_("Link all to bone");
+ return BLI_strdup(TIP_("Link all to bone"));
}
else {
- *r_tooltip = TIP_("Link all to object");
+ return BLI_strdup(TIP_("Link all to object"));
}
break;
}
-
- if (changed) {
- ED_region_tag_redraw_no_rebuild(region);
- }
-
- return true;
+ return NULL;
}
static void datastack_drop_link(bContext *C, StackDropData *drop_data)
@@ -1110,10 +1104,6 @@ static bool collection_drop_init(bContext *C,
if (ID_IS_LINKED(to_collection)) {
return false;
}
- /* Currently this should not be allowed (might be supported in the future though...). */
- if (ID_IS_OVERRIDE_LIBRARY(to_collection)) {
- return false;
- }
/* Get drag datablocks. */
if (drag->type != WM_DRAG_ID) {
@@ -1137,6 +1127,11 @@ static bool collection_drop_init(bContext *C,
from_collection = NULL;
}
+ /* Currently this should not be allowed, cannot edit items in an override of a Collection. */
+ if (from_collection != NULL && ID_IS_OVERRIDE_LIBRARY(from_collection)) {
+ return false;
+ }
+
/* Get collections. */
if (GS(id->name) == ID_GR) {
if (id == &to_collection->id) {
@@ -1147,6 +1142,12 @@ static bool collection_drop_init(bContext *C,
insert_type = TE_INSERT_INTO;
}
+ /* Currently this should not be allowed, cannot edit items in an override of a Collection. */
+ if (ID_IS_OVERRIDE_LIBRARY(to_collection) &&
+ !ELEM(insert_type, TE_INSERT_AFTER, TE_INSERT_BEFORE)) {
+ return false;
+ }
+
data->from = from_collection;
data->to = to_collection;
data->te = te;
@@ -1155,10 +1156,7 @@ static bool collection_drop_init(bContext *C,
return true;
}
-static bool collection_drop_poll(bContext *C,
- wmDrag *drag,
- const wmEvent *event,
- const char **r_tooltip)
+static bool collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
{
SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
ARegion *region = CTX_wm_region(C);
@@ -1172,45 +1170,20 @@ static bool collection_drop_poll(bContext *C,
if (!data.from || event->ctrl) {
tselem->flag |= TSE_DRAG_INTO;
changed = true;
- *r_tooltip = TIP_("Link inside Collection");
}
else {
switch (data.insert_type) {
case TE_INSERT_BEFORE:
tselem->flag |= TSE_DRAG_BEFORE;
changed = true;
- if (te->prev && outliner_is_collection_tree_element(te->prev)) {
- *r_tooltip = TIP_("Move between collections");
- }
- else {
- *r_tooltip = TIP_("Move before collection");
- }
break;
case TE_INSERT_AFTER:
tselem->flag |= TSE_DRAG_AFTER;
changed = true;
- if (te->next && outliner_is_collection_tree_element(te->next)) {
- *r_tooltip = TIP_("Move between collections");
- }
- else {
- *r_tooltip = TIP_("Move after collection");
- }
break;
case TE_INSERT_INTO: {
tselem->flag |= TSE_DRAG_INTO;
changed = true;
-
- /* Check the type of the drag IDs to avoid the incorrect "Shift to parent"
- * for collections. Checking the type of the first ID works fine here since
- * all drag IDs are the same type. */
- wmDragID *drag_id = (wmDragID *)drag->ids.first;
- const bool is_object = (GS(drag_id->id->name) == ID_OB);
- if (is_object) {
- *r_tooltip = TIP_("Move inside collection (Ctrl to link, Shift to parent)");
- }
- else {
- *r_tooltip = TIP_("Move inside collection (Ctrl to link)");
- }
break;
}
}
@@ -1226,6 +1199,52 @@ static bool collection_drop_poll(bContext *C,
return false;
}
+static char *collection_drop_tooltip(bContext *C,
+ wmDrag *drag,
+ const wmEvent *event,
+ wmDropBox *UNUSED(drop))
+{
+ CollectionDrop data;
+ if (!event->shift && collection_drop_init(C, drag, event, &data)) {
+ TreeElement *te = data.te;
+ if (!data.from || event->ctrl) {
+ return BLI_strdup(TIP_("Link inside Collection"));
+ }
+ switch (data.insert_type) {
+ case TE_INSERT_BEFORE:
+ if (te->prev && outliner_is_collection_tree_element(te->prev)) {
+ return BLI_strdup(TIP_("Move between collections"));
+ }
+ else {
+ return BLI_strdup(TIP_("Move before collection"));
+ }
+ break;
+ case TE_INSERT_AFTER:
+ if (te->next && outliner_is_collection_tree_element(te->next)) {
+ return BLI_strdup(TIP_("Move between collections"));
+ }
+ else {
+ return BLI_strdup(TIP_("Move after collection"));
+ }
+ break;
+ case TE_INSERT_INTO: {
+
+ /* Check the type of the drag IDs to avoid the incorrect "Shift to parent"
+ * for collections. Checking the type of the first ID works fine here since
+ * all drag IDs are the same type. */
+ wmDragID *drag_id = (wmDragID *)drag->ids.first;
+ const bool is_object = (GS(drag_id->id->name) == ID_OB);
+ if (is_object) {
+ return BLI_strdup(TIP_("Move inside collection (Ctrl to link, Shift to parent)"));
+ }
+ return BLI_strdup(TIP_("Move inside collection (Ctrl to link)"));
+ break;
+ }
+ }
+ }
+ return NULL;
+}
+
static int collection_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
{
Main *bmain = CTX_data_main(C);
@@ -1499,10 +1518,16 @@ void outliner_dropboxes(void)
{
ListBase *lb = WM_dropboxmap_find("Outliner", SPACE_OUTLINER, RGN_TYPE_WINDOW);
- WM_dropbox_add(lb, "OUTLINER_OT_parent_drop", parent_drop_poll, NULL, NULL);
- WM_dropbox_add(lb, "OUTLINER_OT_parent_clear", parent_clear_poll, NULL, NULL);
- WM_dropbox_add(lb, "OUTLINER_OT_scene_drop", scene_drop_poll, NULL, NULL);
- WM_dropbox_add(lb, "OUTLINER_OT_material_drop", material_drop_poll, NULL, NULL);
- WM_dropbox_add(lb, "OUTLINER_OT_datastack_drop", datastack_drop_poll, NULL, NULL);
- WM_dropbox_add(lb, "OUTLINER_OT_collection_drop", collection_drop_poll, NULL, NULL);
+ WM_dropbox_add(lb, "OUTLINER_OT_parent_drop", parent_drop_poll, NULL, NULL, NULL);
+ WM_dropbox_add(lb, "OUTLINER_OT_parent_clear", parent_clear_poll, NULL, NULL, NULL);
+ WM_dropbox_add(lb, "OUTLINER_OT_scene_drop", scene_drop_poll, NULL, NULL, NULL);
+ WM_dropbox_add(lb, "OUTLINER_OT_material_drop", material_drop_poll, NULL, NULL, NULL);
+ WM_dropbox_add(
+ lb, "OUTLINER_OT_datastack_drop", datastack_drop_poll, NULL, NULL, datastack_drop_tooltip);
+ WM_dropbox_add(lb,
+ "OUTLINER_OT_collection_drop",
+ collection_drop_poll,
+ NULL,
+ NULL,
+ collection_drop_tooltip);
}
diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c
index 5be6c69363e..738db28a2b6 100644
--- a/source/blender/editors/space_outliner/outliner_edit.c
+++ b/source/blender/editors/space_outliner/outliner_edit.c
@@ -1773,7 +1773,7 @@ static void tree_element_to_path(TreeElement *te,
char buf[128], *name;
temnext = (TreeElement *)(ld->next->data);
- /* tsenext = TREESTORE(temnext); */ /* UNUSED */
+ // tsenext = TREESTORE(temnext); /* UNUSED */
nextptr = &temnext->rnaptr;
name = RNA_struct_name_get_alloc(nextptr, buf, sizeof(buf), NULL);
diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c
index aaa52f6b649..898e66e7a39 100644
--- a/source/blender/editors/space_outliner/outliner_select.c
+++ b/source/blender/editors/space_outliner/outliner_select.c
@@ -1682,7 +1682,8 @@ void OUTLINER_OT_item_activate(wmOperatorType *ot)
ot->flag |= OPTYPE_REGISTER | OPTYPE_UNDO;
PropertyRNA *prop;
- RNA_def_boolean(ot->srna, "extend", true, "Extend", "Extend selection for activation");
+ prop = RNA_def_boolean(ot->srna, "extend", true, "Extend", "Extend selection for activation");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
prop = RNA_def_boolean(
ot->srna, "extend_range", false, "Extend Range", "Select a range from active element");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c
index 3edb12c5503..d88ae82cc9a 100644
--- a/source/blender/editors/space_outliner/outliner_tools.c
+++ b/source/blender/editors/space_outliner/outliner_tools.c
@@ -92,6 +92,7 @@
#include "RNA_define.h"
#include "RNA_enum_types.h"
+#include "SEQ_relations.h"
#include "SEQ_sequencer.h"
#include "outliner_intern.h"
@@ -845,8 +846,20 @@ static void id_override_library_create_fn(bContext *C,
if (!ID_IS_LINKED(te->store_elem->id)) {
break;
}
+ /* If we'd need to override that aren't ID, but it is not overridable, abort. */
+ if (!ID_IS_OVERRIDABLE_LIBRARY(te->store_elem->id)) {
+ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+ BKE_reportf(reports,
+ RPT_WARNING,
+ "Could not create library override from data-block '%s', one of its parents "
+ "is not overridable ('%s')",
+ id_root->name,
+ te->store_elem->id->name);
+ return;
+ }
te->store_elem->id->tag |= LIB_TAG_DOIT;
}
+
success = BKE_lib_override_library_create(
bmain, CTX_data_scene(C), CTX_data_view_layer(C), id_root, id_reference, NULL);
}
@@ -1269,18 +1282,31 @@ static void ebone_fn(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem),
}
}
-static void sequence_fn(int event, TreeElement *te, TreeStoreElem *tselem, void *scene_ptr)
+static void sequence_fn(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem), void *scene_ptr)
{
Sequence *seq = (Sequence *)te->directdata;
- if (event == OL_DOP_SELECT) {
- Scene *scene = (Scene *)scene_ptr;
- Editing *ed = SEQ_editing_get(scene, false);
- if (BLI_findindex(ed->seqbasep, seq) != -1) {
+ Scene *scene = (Scene *)scene_ptr;
+ Editing *ed = SEQ_editing_get(scene, false);
+ if (BLI_findindex(ed->seqbasep, seq) != -1) {
+ if (event == OL_DOP_SELECT) {
ED_sequencer_select_sequence_single(scene, seq, true);
}
+ else if (event == OL_DOP_DESELECT) {
+ seq->flag &= ~SELECT;
+ }
+ else if (event == OL_DOP_HIDE) {
+ if (!(seq->flag & SEQ_MUTE)) {
+ seq->flag |= SEQ_MUTE;
+ SEQ_relations_invalidate_dependent(scene, seq);
+ }
+ }
+ else if (event == OL_DOP_UNHIDE) {
+ if (seq->flag & SEQ_MUTE) {
+ seq->flag &= ~SEQ_MUTE;
+ SEQ_relations_invalidate_dependent(scene, seq);
+ }
+ }
}
-
- (void)tselem;
}
static void gpencil_layer_fn(int event,
@@ -2697,16 +2723,6 @@ void OUTLINER_OT_modifier_operation(wmOperatorType *ot)
/** \name Data Menu Operator
* \{ */
-/* XXX: select linked is for RNA structs only. */
-static const EnumPropertyItem prop_data_op_types[] = {
- {OL_DOP_SELECT, "SELECT", 0, "Select", ""},
- {OL_DOP_DESELECT, "DESELECT", 0, "Deselect", ""},
- {OL_DOP_HIDE, "HIDE", 0, "Hide", ""},
- {OL_DOP_UNHIDE, "UNHIDE", 0, "Unhide", ""},
- {OL_DOP_SELECT_LINKED, "SELECT_LINKED", 0, "Select Linked", ""},
- {0, NULL, 0, NULL, NULL},
-};
-
static int outliner_data_operation_exec(bContext *C, wmOperator *op)
{
SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
@@ -2750,6 +2766,8 @@ static int outliner_data_operation_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
outliner_do_data_operation(
space_outliner, datalevel, event, &space_outliner->tree, sequence_fn, scene);
+ WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene);
+ ED_undo_push(C, "Sequencer operation");
break;
}
@@ -2777,6 +2795,42 @@ static int outliner_data_operation_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+/* Dynamically populate an enum of Keying Sets */
+static const EnumPropertyItem *outliner_data_op_sets_enum_item_fn(bContext *C,
+ PointerRNA *UNUSED(ptr),
+ PropertyRNA *UNUSED(prop),
+ bool *UNUSED(r_free))
+{
+ /* Check for invalid states. */
+ if (C == NULL) {
+ return DummyRNA_DEFAULT_items;
+ }
+
+ SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
+ if (space_outliner == NULL) {
+ return DummyRNA_DEFAULT_items;
+ }
+
+ static const EnumPropertyItem optype_sel_and_hide[] = {
+ {OL_DOP_SELECT, "SELECT", 0, "Select", ""},
+ {OL_DOP_DESELECT, "DESELECT", 0, "Deselect", ""},
+ {OL_DOP_HIDE, "HIDE", 0, "Hide", ""},
+ {OL_DOP_UNHIDE, "UNHIDE", 0, "Unhide", ""},
+ {0, NULL, 0, NULL, NULL}};
+
+ static const EnumPropertyItem optype_sel_linked[] = {
+ {OL_DOP_SELECT_LINKED, "SELECT_LINKED", 0, "Select Linked", ""}, {0, NULL, 0, NULL, NULL}};
+
+ TreeElement *te = get_target_element(space_outliner);
+ TreeStoreElem *tselem = TREESTORE(te);
+
+ if (tselem->type == TSE_RNA_STRUCT) {
+ return optype_sel_linked;
+ }
+
+ return optype_sel_and_hide;
+}
+
void OUTLINER_OT_data_operation(wmOperatorType *ot)
{
/* identifiers */
@@ -2790,7 +2844,8 @@ void OUTLINER_OT_data_operation(wmOperatorType *ot)
ot->flag = 0;
- ot->prop = RNA_def_enum(ot->srna, "type", prop_data_op_types, 0, "Data Operation", "");
+ ot->prop = RNA_def_enum(ot->srna, "type", DummyRNA_DEFAULT_items, 0, "Data Operation", "");
+ RNA_def_enum_funcs(ot->prop, outliner_data_op_sets_enum_item_fn);
}
/** \} */
diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c
index b6b3d1841d2..ff8cbdb1a59 100644
--- a/source/blender/editors/space_sequencer/sequencer_add.c
+++ b/source/blender/editors/space_sequencer/sequencer_add.c
@@ -159,7 +159,7 @@ static void sequencer_generic_props__internal(wmOperatorType *ot, int flag)
"set_view_transform",
true,
"Set View Transform",
- "Set appropriate view transform based on media colorspace");
+ "Set appropriate view transform based on media color space");
}
}
@@ -565,10 +565,7 @@ static void sequencer_add_init(bContext *UNUSED(C), wmOperator *op)
static void sequencer_add_cancel(bContext *UNUSED(C), wmOperator *op)
{
- if (op->customdata) {
- MEM_freeN(op->customdata);
- }
- op->customdata = NULL;
+ MEM_SAFE_FREE(op->customdata);
}
static bool sequencer_add_draw_check_fn(PointerRNA *UNUSED(ptr),
@@ -646,15 +643,17 @@ static void sequencer_add_movie_multiple_strips(bContext *C,
BLI_strncpy(load_data->name, file_only, sizeof(load_data->name));
Sequence *seq_movie = NULL;
Sequence *seq_sound = NULL;
+ double video_start_offset;
+
load_data->channel++;
- seq_movie = SEQ_add_movie_strip(bmain, scene, ed->seqbasep, load_data);
+ seq_movie = SEQ_add_movie_strip(bmain, scene, ed->seqbasep, load_data, &video_start_offset);
load_data->channel--;
if (seq_movie == NULL) {
BKE_reportf(op->reports, RPT_ERROR, "File '%s' could not be loaded", load_data->path);
}
else {
if (RNA_boolean_get(op->ptr, "sound")) {
- seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data);
+ seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, video_start_offset);
}
load_data->start_frame += seq_movie->enddisp - seq_movie->startdisp;
seq_load_apply_generic_options(C, op, seq_sound);
@@ -673,8 +672,10 @@ static bool sequencer_add_movie_single_strip(bContext *C, wmOperator *op, SeqLoa
Sequence *seq_movie = NULL;
Sequence *seq_sound = NULL;
+ double video_start_offset;
+
load_data->channel++;
- seq_movie = SEQ_add_movie_strip(bmain, scene, ed->seqbasep, load_data);
+ seq_movie = SEQ_add_movie_strip(bmain, scene, ed->seqbasep, load_data, &video_start_offset);
load_data->channel--;
if (seq_movie == NULL) {
@@ -682,7 +683,7 @@ static bool sequencer_add_movie_single_strip(bContext *C, wmOperator *op, SeqLoa
return false;
}
if (RNA_boolean_get(op->ptr, "sound")) {
- seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data);
+ seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, video_start_offset);
}
seq_load_apply_generic_options(C, op, seq_sound);
seq_load_apply_generic_options(C, op, seq_movie);
@@ -710,13 +711,13 @@ static int sequencer_add_movie_strip_exec(bContext *C, wmOperator *op)
}
else {
if (!sequencer_add_movie_single_strip(C, op, &load_data)) {
+ sequencer_add_cancel(C, op);
return OPERATOR_CANCELLED;
}
}
- if (op->customdata) {
- MEM_freeN(op->customdata);
- }
+ /* Free custom data. */
+ sequencer_add_cancel(C, op);
DEG_relations_tag_update(bmain);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
@@ -825,7 +826,7 @@ static void sequencer_add_sound_multiple_strips(bContext *C,
RNA_string_get(&itemptr, "name", file_only);
BLI_join_dirfile(load_data->path, sizeof(load_data->path), dir_only, file_only);
BLI_strncpy(load_data->name, file_only, sizeof(load_data->name));
- Sequence *seq = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data);
+ Sequence *seq = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, 0.0f);
if (seq == NULL) {
BKE_reportf(op->reports, RPT_ERROR, "File '%s' could not be loaded", load_data->path);
}
@@ -843,7 +844,7 @@ static bool sequencer_add_sound_single_strip(bContext *C, wmOperator *op, SeqLoa
Scene *scene = CTX_data_scene(C);
Editing *ed = SEQ_editing_get(scene, true);
- Sequence *seq = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data);
+ Sequence *seq = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, 0.0f);
if (seq == NULL) {
BKE_reportf(op->reports, RPT_ERROR, "File '%s' could not be loaded", load_data->path);
return false;
@@ -1043,6 +1044,7 @@ static int sequencer_add_image_strip_exec(bContext *C, wmOperator *op)
load_data.image.len = sequencer_add_image_strip_calculate_length(
op, load_data.start_frame, &minframe, &numdigits);
if (load_data.image.len == 0) {
+ sequencer_add_cancel(C, op);
return OPERATOR_CANCELLED;
}
@@ -1065,9 +1067,8 @@ static int sequencer_add_image_strip_exec(bContext *C, wmOperator *op)
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
- if (op->customdata) {
- MEM_freeN(op->customdata);
- }
+ /* Free custom data. */
+ sequencer_add_cancel(C, op);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c
index 3f8dea8b533..b3c39e2fa6f 100644
--- a/source/blender/editors/space_sequencer/sequencer_draw.c
+++ b/source/blender/editors/space_sequencer/sequencer_draw.c
@@ -228,9 +228,93 @@ void color3ubv_from_seq(Scene *curscene, Sequence *seq, uchar col[3])
}
}
+typedef struct WaveVizData {
+ float pos[2];
+ float rms_pos;
+ bool clip;
+ bool end;
+} WaveVizData;
+
+static int get_section_len(WaveVizData *start, WaveVizData *end)
+{
+ int len = 0;
+ while (start != end) {
+ len++;
+ if (start->end) {
+ return len;
+ }
+ start++;
+ }
+ return len;
+}
+
+static void draw_waveform(WaveVizData *iter, WaveVizData *end, GPUPrimType prim_type, bool use_rms)
+{
+ int strip_len = get_section_len(iter, end);
+ if (strip_len > 1) {
+ GPU_blend(GPU_BLEND_ALPHA);
+ GPUVertFormat *format = immVertexFormat();
+ uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+
+ immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
+ immBegin(prim_type, strip_len);
+
+ while (iter != end) {
+ if (iter->clip) {
+ immAttr4f(col, 1.0f, 0.0f, 0.0f, 0.5f);
+ }
+ else if (use_rms) {
+ immAttr4f(col, 1.0f, 1.0f, 1.0f, 0.8f);
+ }
+ else {
+ immAttr4f(col, 1.0f, 1.0f, 1.0f, 0.5f);
+ }
+
+ if (use_rms) {
+ immVertex2f(pos, iter->pos[0], iter->rms_pos);
+ }
+ else {
+ immVertex2f(pos, iter->pos[0], iter->pos[1]);
+ }
+
+ if (iter->end) {
+ /* End of line. */
+ iter++;
+ strip_len = get_section_len(iter, end);
+ if (strip_len != 0) {
+ immEnd();
+ immUnbindProgram();
+ immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
+ immBegin(prim_type, strip_len);
+ }
+ }
+ else {
+ iter++;
+ }
+ }
+ immEnd();
+ immUnbindProgram();
+
+ GPU_blend(GPU_BLEND_NONE);
+ }
+}
+
+static float clamp_frame_coord_to_pixel(float frame_coord,
+ float pixel_frac,
+ float frames_per_pixel)
+{
+ float cur_pixel = (frame_coord / frames_per_pixel);
+ float new_pixel = (int)(frame_coord / frames_per_pixel) + pixel_frac;
+ if (cur_pixel > new_pixel) {
+ new_pixel += 1.0f;
+ }
+ return new_pixel * frames_per_pixel;
+}
+
/**
* \param x1, x2, y1, y2: The starting and end X value to draw the wave, same for y1 and y2.
- * \param stepsize: The width of a pixel.
+ * \param frames_per_pixel: The amount of pixels a whole frame takes up (x-axis direction).
*/
static void draw_seq_waveform_overlay(View2D *v2d,
const bContext *C,
@@ -241,29 +325,34 @@ static void draw_seq_waveform_overlay(View2D *v2d,
float y1,
float x2,
float y2,
- float stepsize)
+ float frames_per_pixel)
{
- /* Offset x1 and x2 values, to match view min/max, if strip is out of bounds. */
- int x1_offset = max_ff(v2d->cur.xmin, x1);
- int x2_offset = min_ff(v2d->cur.xmax + 1.0f, x2);
-
if (seq->sound && ((sseq->flag & SEQ_ALL_WAVEFORMS) || (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM))) {
- int length = floor((x2_offset - x1_offset) / stepsize) + 1;
- float ymid = (y1 + y2) / 2.0f;
- float yscale = (y2 - y1) / 2.0f;
- float samplestep;
- float startsample, endsample;
- float volume = seq->volume;
- float value1, value2;
- bSound *sound = seq->sound;
- SoundWaveform *waveform;
-
- if (length < 2) {
+ /* Make sure that the start drawing position is aligned to the pixels on the screen to avoid
+ * flickering when moving around the strip.
+ * To do this we figure out the fractional offset in pixel space by checking where the
+ * window starts.
+ * We then append this pixel offset to our strip start coordinate to ensure we are aligned to
+ * the screen pixel grid. */
+ float pixel_frac = v2d->cur.xmin / frames_per_pixel - floor(v2d->cur.xmin / frames_per_pixel);
+ float x1_adj = clamp_frame_coord_to_pixel(x1, pixel_frac, frames_per_pixel);
+
+ /* Offset x1 and x2 values, to match view min/max, if strip is out of bounds. */
+ float x1_offset = max_ff(v2d->cur.xmin, x1_adj);
+ float x2_offset = min_ff(v2d->cur.xmax, x2);
+
+ /* Calculate how long the strip that is in view is in pixels. */
+ int pix_strip_len = round((x2_offset - x1_offset) / frames_per_pixel);
+
+ if (pix_strip_len < 2) {
return;
}
+ bSound *sound = seq->sound;
+
BLI_spin_lock(sound->spinlock);
if (!sound->waveform) {
+ /* Load the waveform data if it hasn't been loaded and cached already. */
if (!(sound->tags & SOUND_TAGS_WAVEFORM_LOADING)) {
/* Prevent sounds from reloading. */
sound->tags |= SOUND_TAGS_WAVEFORM_LOADING;
@@ -277,87 +366,187 @@ static void draw_seq_waveform_overlay(View2D *v2d,
}
BLI_spin_unlock(sound->spinlock);
- waveform = sound->waveform;
+ SoundWaveform *waveform = sound->waveform;
/* Waveform could not be built. */
if (waveform->length == 0) {
return;
}
- startsample = floor((seq->startofs + seq->anim_startofs) / FPS *
- SOUND_WAVE_SAMPLES_PER_SECOND);
- endsample = ceil((seq->startofs + seq->anim_startofs + seq->enddisp - seq->startdisp) / FPS *
- SOUND_WAVE_SAMPLES_PER_SECOND);
- samplestep = (endsample - startsample) * stepsize / (x2 - x1);
+ /* F-curve lookup is quite expensive, so do this after precondition. */
+ FCurve *fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "volume", 0, NULL);
- length = min_ii(
- floor((waveform->length - startsample) / samplestep - (x1_offset - x1) / stepsize),
- length);
+ WaveVizData *tri_strip_arr = MEM_callocN(sizeof(*tri_strip_arr) * pix_strip_len * 2,
+ "tri_strip");
+ WaveVizData *line_strip_arr = MEM_callocN(sizeof(*line_strip_arr) * pix_strip_len,
+ "line_strip");
- if (length < 2) {
- return;
- }
+ WaveVizData *tri_strip_iter = tri_strip_arr;
+ WaveVizData *line_strip_iter = line_strip_arr;
- /* F-curve lookup is quite expensive, so do this after precondition. */
- FCurve *fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "volume", 0, NULL);
+ /* The y coordinate for the middle of the strip. */
+ float y_mid = (y1 + y2) / 2.0f;
+ /* The length from the middle of the strip to the top/bottom. */
+ float y_scale = (y2 - y1) / 2.0f;
+ float volume = seq->volume;
- GPU_blend(GPU_BLEND_ALPHA);
- GPUVertFormat *format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
- immBegin(GPU_PRIM_TRI_STRIP, length * 2);
+ /* Value to keep track if the previous item to be drawn was a line strip. */
+ int8_t was_line_strip = -1; /* -1 == no previous value. */
- for (int i = 0; i < length; i++) {
- float sampleoffset = startsample + ((x1_offset - x1) / stepsize + i) * samplestep;
- int p = sampleoffset;
+ float samples_per_frame = SOUND_WAVE_SAMPLES_PER_SECOND / FPS;
- value1 = waveform->data[p * 3];
- value2 = waveform->data[p * 3 + 1];
+ /* How many samples do we have for each pixel? */
+ float samples_per_pix = samples_per_frame * frames_per_pixel;
- if (samplestep > 1.0f) {
- for (int j = p + 1; (j < waveform->length) && (j < p + samplestep); j++) {
- if (value1 > waveform->data[j * 3]) {
- value1 = waveform->data[j * 3];
- }
+ float strip_start_offset = seq->startofs + seq->anim_startofs;
+ float start_sample = 0;
- if (value2 < waveform->data[j * 3 + 1]) {
- value2 = waveform->data[j * 3 + 1];
- }
- }
+ if (strip_start_offset != 0) {
+ /* If start offset is not zero, we need to make sure that we pick the same start sample as if
+ * we simply scrolled the start of the strip off-screen. Otherwise we will get flickering
+ * when changing start offset as the pixel alignment will not be the same for the drawn
+ * samples. */
+ strip_start_offset = clamp_frame_coord_to_pixel(
+ x1 - strip_start_offset, pixel_frac, frames_per_pixel);
+ start_sample = fabsf(strip_start_offset - x1_adj) * samples_per_frame;
+ }
+
+ start_sample += seq->sound->offset_time * SOUND_WAVE_SAMPLES_PER_SECOND;
+ /* If we scrolled the start off-screen, then the start sample should be at the first visible
+ * sample. */
+ start_sample += (x1_offset - x1_adj) * samples_per_frame;
+
+ for (int i = 0; i < pix_strip_len; i++) {
+ float sample_offset = start_sample + i * samples_per_pix;
+ int p = sample_offset;
+
+ if (p >= waveform->length) {
+ break;
}
- else if (p + 1 < waveform->length) {
+
+ float value_min = waveform->data[p * 3];
+ float value_max = waveform->data[p * 3 + 1];
+ float rms = waveform->data[p * 3 + 2];
+
+ if (p + 1 < waveform->length) {
/* Use simple linear interpolation. */
- float f = sampleoffset - p;
- value1 = (1.0f - f) * value1 + f * waveform->data[p * 3 + 3];
- value2 = (1.0f - f) * value2 + f * waveform->data[p * 3 + 4];
+ float f = sample_offset - p;
+ value_min = (1.0f - f) * value_min + f * waveform->data[p * 3 + 3];
+ value_max = (1.0f - f) * value_max + f * waveform->data[p * 3 + 4];
+ rms = (1.0f - f) * rms + f * waveform->data[p * 3 + 5];
+ if (samples_per_pix > 1.0f) {
+ /* We need to sum up the values we skip over until the next step. */
+ float next_pos = sample_offset + samples_per_pix;
+ int end_idx = next_pos;
+
+ for (int j = p + 1; (j < waveform->length) && (j < end_idx); j++) {
+ value_min = min_ff(value_min, waveform->data[j * 3]);
+ value_max = max_ff(value_max, waveform->data[j * 3 + 1]);
+ rms = max_ff(rms, waveform->data[j * 3 + 2]);
+ }
+ }
}
if (fcu && !BKE_fcurve_is_empty(fcu)) {
- float evaltime = x1_offset + (i * stepsize);
+ float evaltime = x1_offset + (i * frames_per_pixel);
volume = evaluate_fcurve(fcu, evaltime);
CLAMP_MIN(volume, 0.0f);
}
- value1 *= volume;
- value2 *= volume;
- if (value2 > 1 || value1 < -1) {
- immAttr4f(col, 1.0f, 0.0f, 0.0f, 0.5f);
+ value_min *= volume;
+ value_max *= volume;
+ rms *= volume;
+
+ bool clipping = false;
+
+ if (value_max > 1 || value_min < -1) {
+ clipping = true;
- CLAMP_MAX(value2, 1.0f);
- CLAMP_MIN(value1, -1.0f);
+ CLAMP_MAX(value_max, 1.0f);
+ CLAMP_MIN(value_min, -1.0f);
}
- else {
- immAttr4f(col, 1.0f, 1.0f, 1.0f, 0.5f);
+
+ bool is_line_strip = (value_max - value_min < 0.05f);
+
+ if (was_line_strip != -1 && is_line_strip != was_line_strip) {
+ /* If the previously added strip type isn't the same as the current one,
+ * add transition areas so they transition smoothly between each other. */
+ if (is_line_strip) {
+ /* This will be a line strip, end the tri strip. */
+ tri_strip_iter->pos[0] = x1_offset + i * frames_per_pixel;
+ tri_strip_iter->pos[1] = y_mid + value_min * y_scale;
+ tri_strip_iter->clip = clipping;
+ tri_strip_iter->rms_pos = tri_strip_iter->pos[1];
+ tri_strip_iter->end = true;
+
+ /* End of section. */
+ tri_strip_iter++;
+
+ /* Check if we are at the end.
+ * If so, skip one point line. */
+ if (i + 1 == pix_strip_len) {
+ continue;
+ }
+ }
+ else {
+ /* This will be a tri strip. */
+ line_strip_iter--;
+ tri_strip_iter->pos[0] = line_strip_iter->pos[0];
+ tri_strip_iter->pos[1] = line_strip_iter->pos[1];
+ tri_strip_iter->clip = line_strip_iter->clip;
+ tri_strip_iter->rms_pos = line_strip_iter->pos[1];
+ tri_strip_iter++;
+
+ /* Check if line had only one point. */
+ line_strip_iter--;
+ if (line_strip_iter < line_strip_arr || line_strip_iter->end) {
+ /* Only one point, skip it. */
+ line_strip_iter++;
+ }
+ else {
+ /* End of section. */
+ line_strip_iter++;
+ line_strip_iter->end = true;
+ line_strip_iter++;
+ }
+ }
}
- immVertex2f(pos, x1_offset + i * stepsize, ymid + value1 * yscale);
- immVertex2f(pos, x1_offset + i * stepsize, ymid + value2 * yscale);
+ was_line_strip = is_line_strip;
+
+ if (is_line_strip) {
+ line_strip_iter->pos[0] = x1_offset + i * frames_per_pixel;
+ line_strip_iter->pos[1] = y_mid + value_min * y_scale;
+ line_strip_iter->clip = clipping;
+ line_strip_iter++;
+ }
+ else {
+ tri_strip_iter->pos[0] = x1_offset + i * frames_per_pixel;
+ tri_strip_iter->pos[1] = y_mid + value_min * y_scale;
+ tri_strip_iter->clip = clipping;
+ tri_strip_iter->rms_pos = y_mid + max_ff(-rms, value_min) * y_scale;
+ tri_strip_iter++;
+
+ tri_strip_iter->pos[0] = x1_offset + i * frames_per_pixel;
+ tri_strip_iter->pos[1] = y_mid + value_max * y_scale;
+ tri_strip_iter->clip = clipping;
+ tri_strip_iter->rms_pos = y_mid + min_ff(rms, value_max) * y_scale;
+ tri_strip_iter++;
+ }
}
- immEnd();
- immUnbindProgram();
- GPU_blend(GPU_BLEND_NONE);
+ WaveVizData *tri_strip_end = tri_strip_iter;
+ WaveVizData *line_strip_end = line_strip_iter;
+
+ tri_strip_iter = tri_strip_arr;
+ line_strip_iter = line_strip_arr;
+
+ draw_waveform(line_strip_iter, line_strip_end, GPU_PRIM_LINE_STRIP, false);
+ draw_waveform(tri_strip_iter, tri_strip_end, GPU_PRIM_TRI_STRIP, false);
+ draw_waveform(tri_strip_iter, tri_strip_end, GPU_PRIM_TRI_STRIP, true);
+
+ MEM_freeN(tri_strip_arr);
+ MEM_freeN(line_strip_arr);
}
}
@@ -1125,7 +1314,7 @@ static void draw_seq_strip(const bContext *C,
}
else {
text_margin_y = y2;
- y_threshold = 1;
+ y_threshold = false;
}
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
@@ -1139,7 +1328,7 @@ static void draw_seq_strip(const bContext *C,
}
/* Draw strip offsets when flag is enabled or during "solo preview". */
- if ((sseq->flag & SEQ_SHOW_STRIP_OVERLAY)) {
+ if (sseq->flag & SEQ_SHOW_STRIP_OVERLAY) {
if (!is_single_image && (seq->startofs || seq->endofs) && pixely > 0) {
if ((sseq->draw_flag & SEQ_DRAW_OFFSET_EXT) || (seq == special_seq_update)) {
draw_sequence_extensions_overlay(scene, seq, pos, pixely);
diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c
index 4b26469aad3..afad8999e88 100644
--- a/source/blender/editors/space_sequencer/sequencer_edit.c
+++ b/source/blender/editors/space_sequencer/sequencer_edit.c
@@ -2943,7 +2943,7 @@ static int seq_cmp_time_startdisp_channel(const void *a, const void *b)
int seq_a_start = SEQ_transform_get_left_handle_frame(seq_a);
int seq_b_start = SEQ_transform_get_left_handle_frame(seq_b);
- /** If strips have the same start frame favor the one with a higher channel. **/
+ /* If strips have the same start frame favor the one with a higher channel. */
if (seq_a_start == seq_b_start) {
return seq_a->machine > seq_b->machine;
}
diff --git a/source/blender/editors/space_sequencer/sequencer_proxy.c b/source/blender/editors/space_sequencer/sequencer_proxy.c
index 2dcc2d389d9..16d14b5fa72 100644
--- a/source/blender/editors/space_sequencer/sequencer_proxy.c
+++ b/source/blender/editors/space_sequencer/sequencer_proxy.c
@@ -131,7 +131,7 @@ static int sequencer_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op))
file_list = BLI_gset_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, "file list");
LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
- if ((seq->flag & SELECT)) {
+ if (seq->flag & SELECT) {
ListBase queue = {NULL, NULL};
LinkData *link;
short stop = 0, do_update;
@@ -197,7 +197,7 @@ static int sequencer_enable_proxies_exec(bContext *C, wmOperator *op)
}
LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) {
- if ((seq->flag & SELECT)) {
+ if (seq->flag & SELECT) {
if (ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_IMAGE)) {
SEQ_proxy_set(seq, turnon);
if (seq->strip->proxy == NULL) {
diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c
index 5980bfe37cd..333edd0ed5f 100644
--- a/source/blender/editors/space_sequencer/sequencer_select.c
+++ b/source/blender/editors/space_sequencer/sequencer_select.c
@@ -999,7 +999,9 @@ void SEQUENCER_OT_select_linked_pick(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* Properties. */
- RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection");
+ PropertyRNA *prop;
+ prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
/** \} */
@@ -1227,7 +1229,9 @@ void SEQUENCER_OT_select_side_of_frame(wmOperatorType *ot)
ot->flag = OPTYPE_UNDO;
/* Properties. */
- RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection");
+ PropertyRNA *prop;
+ prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
ot->prop = RNA_def_enum(ot->srna, "side", sequencer_select_left_right_types, 0, "Side", "");
}
diff --git a/source/blender/editors/space_sequencer/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c
index 337ac2e0009..a0a9cdd96b1 100644
--- a/source/blender/editors/space_sequencer/sequencer_view.c
+++ b/source/blender/editors/space_sequencer/sequencer_view.c
@@ -275,7 +275,6 @@ static int sequencer_view_selected_exec(bContext *C, wmOperator *op)
View2D *v2d = UI_view2d_fromcontext(C);
ARegion *region = CTX_wm_region(C);
Editing *ed = SEQ_editing_get(scene, false);
- Sequence *last_seq = SEQ_select_active_get(scene);
Sequence *seq;
rctf cur_new = v2d->cur;
@@ -293,7 +292,7 @@ static int sequencer_view_selected_exec(bContext *C, wmOperator *op)
}
for (seq = ed->seqbasep->first; seq; seq = seq->next) {
- if ((seq->flag & SELECT) || (seq == last_seq)) {
+ if (seq->flag & SELECT) {
xmin = min_ii(xmin, seq->startdisp);
xmax = max_ii(xmax, seq->enddisp);
diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c
index 6de95f0995a..2a6e49edfb6 100644
--- a/source/blender/editors/space_sequencer/space_sequencer.c
+++ b/source/blender/editors/space_sequencer/space_sequencer.c
@@ -194,7 +194,7 @@ static void sequencer_free(SpaceLink *sl)
SpaceSeq *sseq = (SpaceSeq *)sl;
SequencerScopes *scopes = &sseq->scopes;
- /* XXX if (sseq->gpd) BKE_gpencil_free(sseq->gpd); */
+ /* XXX if (sseq->gpd) BKE_gpencil_free_data(sseq->gpd); */
if (scopes->zebra_ibuf) {
IMB_freeImBuf(scopes->zebra_ibuf);
@@ -364,10 +364,7 @@ static void sequencer_listener(const wmSpaceTypeListenerParams *params)
/* ************* dropboxes ************* */
-static bool image_drop_poll(bContext *C,
- wmDrag *drag,
- const wmEvent *event,
- const char **UNUSED(r_tooltip))
+static bool image_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
{
ARegion *region = CTX_wm_region(C);
Scene *scene = CTX_data_scene(C);
@@ -384,10 +381,7 @@ static bool image_drop_poll(bContext *C,
return 0;
}
-static bool movie_drop_poll(bContext *C,
- wmDrag *drag,
- const wmEvent *event,
- const char **UNUSED(r_tooltip))
+static bool movie_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
{
ARegion *region = CTX_wm_region(C);
Scene *scene = CTX_data_scene(C);
@@ -403,10 +397,7 @@ static bool movie_drop_poll(bContext *C,
return 0;
}
-static bool sound_drop_poll(bContext *C,
- wmDrag *drag,
- const wmEvent *event,
- const char **UNUSED(r_tooltip))
+static bool sound_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
{
ARegion *region = CTX_wm_region(C);
Scene *scene = CTX_data_scene(C);
@@ -448,9 +439,12 @@ static void sequencer_dropboxes(void)
{
ListBase *lb = WM_dropboxmap_find("Sequencer", SPACE_SEQ, RGN_TYPE_WINDOW);
- WM_dropbox_add(lb, "SEQUENCER_OT_image_strip_add", image_drop_poll, sequencer_drop_copy, NULL);
- WM_dropbox_add(lb, "SEQUENCER_OT_movie_strip_add", movie_drop_poll, sequencer_drop_copy, NULL);
- WM_dropbox_add(lb, "SEQUENCER_OT_sound_strip_add", sound_drop_poll, sequencer_drop_copy, NULL);
+ WM_dropbox_add(
+ lb, "SEQUENCER_OT_image_strip_add", image_drop_poll, sequencer_drop_copy, NULL, NULL);
+ WM_dropbox_add(
+ lb, "SEQUENCER_OT_movie_strip_add", movie_drop_poll, sequencer_drop_copy, NULL, NULL);
+ WM_dropbox_add(
+ lb, "SEQUENCER_OT_sound_strip_add", sound_drop_poll, sequencer_drop_copy, NULL, NULL);
}
/* ************* end drop *********** */
diff --git a/source/blender/editors/space_text/space_text.c b/source/blender/editors/space_text/space_text.c
index af783051661..89e92231657 100644
--- a/source/blender/editors/space_text/space_text.c
+++ b/source/blender/editors/space_text/space_text.c
@@ -312,10 +312,7 @@ static void text_cursor(wmWindow *win, ScrArea *area, ARegion *region)
/* ************* dropboxes ************* */
-static bool text_drop_poll(bContext *UNUSED(C),
- wmDrag *drag,
- const wmEvent *UNUSED(event),
- const char **UNUSED(r_tooltip))
+static bool text_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event))
{
if (drag->type == WM_DRAG_PATH) {
/* rule might not work? */
@@ -332,10 +329,7 @@ static void text_drop_copy(wmDrag *drag, wmDropBox *drop)
RNA_string_set(drop->ptr, "filepath", drag->path);
}
-static bool text_drop_paste_poll(bContext *UNUSED(C),
- wmDrag *drag,
- const wmEvent *UNUSED(event),
- const char **UNUSED(r_tooltip))
+static bool text_drop_paste_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event))
{
return (drag->type == WM_DRAG_ID);
}
@@ -356,8 +350,8 @@ static void text_dropboxes(void)
{
ListBase *lb = WM_dropboxmap_find("Text", SPACE_TEXT, RGN_TYPE_WINDOW);
- WM_dropbox_add(lb, "TEXT_OT_open", text_drop_poll, text_drop_copy, NULL);
- WM_dropbox_add(lb, "TEXT_OT_insert", text_drop_paste_poll, text_drop_paste, NULL);
+ WM_dropbox_add(lb, "TEXT_OT_open", text_drop_poll, text_drop_copy, NULL, NULL);
+ WM_dropbox_add(lb, "TEXT_OT_insert", text_drop_paste_poll, text_drop_paste, NULL, NULL);
}
/* ************* end drop *********** */
diff --git a/source/blender/editors/space_text/text_draw.c b/source/blender/editors/space_text/text_draw.c
index 1d8bc427212..b6ba95885e4 100644
--- a/source/blender/editors/space_text/text_draw.c
+++ b/source/blender/editors/space_text/text_draw.c
@@ -686,10 +686,7 @@ static void text_update_drawcache(SpaceText *st, ARegion *region)
}
}
else {
- if (drawcache->line_height) {
- MEM_freeN(drawcache->line_height);
- drawcache->line_height = NULL;
- }
+ MEM_SAFE_FREE(drawcache->line_height);
if (full_update || drawcache->update_flag) {
nlines = BLI_listbase_count(&txt->lines);
diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c
index b98dae0cd57..2b78ecb245d 100644
--- a/source/blender/editors/space_text/text_ops.c
+++ b/source/blender/editors/space_text/text_ops.c
@@ -236,10 +236,7 @@ void text_update_line_edited(TextLine *line)
}
/* we just free format here, and let it rebuild during draw */
- if (line->format) {
- MEM_freeN(line->format);
- line->format = NULL;
- }
+ MEM_SAFE_FREE(line->format);
}
void text_update_edited(Text *text)
diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index 54f10e259f9..72d0c11e192 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -53,6 +53,7 @@
#include "BKE_screen.h"
#include "BKE_workspace.h"
+#include "ED_object.h"
#include "ED_render.h"
#include "ED_screen.h"
#include "ED_space_api.h"
@@ -513,49 +514,49 @@ static bool view3d_drop_id_in_main_region_poll(bContext *C,
return WM_drag_is_ID_type(drag, id_type);
}
-static bool view3d_ob_drop_poll(bContext *C,
- wmDrag *drag,
- const wmEvent *event,
- const char **UNUSED(r_tooltip))
+static bool view3d_ob_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
{
return view3d_drop_id_in_main_region_poll(C, drag, event, ID_OB);
}
-static bool view3d_collection_drop_poll(bContext *C,
- wmDrag *drag,
- const wmEvent *event,
- const char **UNUSED(r_tooltip))
+static bool view3d_collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
{
return view3d_drop_id_in_main_region_poll(C, drag, event, ID_GR);
}
-static bool view3d_mat_drop_poll(bContext *C,
- wmDrag *drag,
- const wmEvent *event,
- const char **UNUSED(r_tooltip))
+static bool view3d_mat_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
{
return view3d_drop_id_in_main_region_poll(C, drag, event, ID_MA);
}
-static bool view3d_object_data_drop_poll(bContext *C,
- wmDrag *drag,
- const wmEvent *event,
- const char **r_tooltip)
+static char *view3d_mat_drop_tooltip(bContext *C,
+ wmDrag *drag,
+ const wmEvent *event,
+ struct wmDropBox *drop)
+{
+ const char *name = WM_drag_get_item_name(drag);
+ RNA_string_set(drop->ptr, "name", name);
+ return ED_object_ot_drop_named_material_tooltip(C, drop->ptr, event);
+}
+
+static bool view3d_object_data_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
{
ID_Type id_type = view3d_drop_id_in_main_region_poll_get_id_type(C, drag, event);
- if (id_type) {
- if (OB_DATA_SUPPORT_ID(id_type)) {
- *r_tooltip = TIP_("Create object instance from object-data");
- return true;
- }
+ if (id_type && OB_DATA_SUPPORT_ID(id_type)) {
+ return true;
}
return false;
}
-static bool view3d_ima_drop_poll(bContext *C,
- wmDrag *drag,
- const wmEvent *event,
- const char **UNUSED(r_tooltip))
+static char *view3d_object_data_drop_tooltip(bContext *UNUSED(C),
+ wmDrag *UNUSED(drag),
+ const wmEvent *UNUSED(event),
+ wmDropBox *UNUSED(drop))
+{
+ return BLI_strdup(TIP_("Create object instance from object-data"));
+}
+
+static bool view3d_ima_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
{
if (ED_region_overlap_isect_any_xy(CTX_wm_area(C), &event->x)) {
return false;
@@ -580,12 +581,9 @@ static bool view3d_ima_bg_is_camera_view(bContext *C)
return false;
}
-static bool view3d_ima_bg_drop_poll(bContext *C,
- wmDrag *drag,
- const wmEvent *event,
- const char **r_tooltip)
+static bool view3d_ima_bg_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
{
- if (!view3d_ima_drop_poll(C, drag, event, r_tooltip)) {
+ if (!view3d_ima_drop_poll(C, drag, event)) {
return false;
}
@@ -596,12 +594,9 @@ static bool view3d_ima_bg_drop_poll(bContext *C,
return view3d_ima_bg_is_camera_view(C);
}
-static bool view3d_ima_empty_drop_poll(bContext *C,
- wmDrag *drag,
- const wmEvent *event,
- const char **r_tooltip)
+static bool view3d_ima_empty_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
{
- if (!view3d_ima_drop_poll(C, drag, event, r_tooltip)) {
+ if (!view3d_ima_drop_poll(C, drag, event)) {
return false;
}
@@ -620,8 +615,7 @@ static bool view3d_ima_empty_drop_poll(bContext *C,
static bool view3d_volume_drop_poll(bContext *UNUSED(C),
wmDrag *drag,
- const wmEvent *UNUSED(event),
- const char **UNUSED(r_tooltip))
+ const wmEvent *UNUSED(event))
{
return (drag->type == WM_DRAG_PATH) && (drag->icon == ICON_FILE_VOLUME);
}
@@ -700,37 +694,44 @@ static void view3d_dropboxes(void)
"OBJECT_OT_add_named",
view3d_ob_drop_poll,
view3d_ob_drop_copy,
- WM_drag_free_imported_drag_ID);
+ WM_drag_free_imported_drag_ID,
+ NULL);
WM_dropbox_add(lb,
"OBJECT_OT_drop_named_material",
view3d_mat_drop_poll,
view3d_id_drop_copy,
- WM_drag_free_imported_drag_ID);
+ WM_drag_free_imported_drag_ID,
+ view3d_mat_drop_tooltip);
WM_dropbox_add(lb,
"VIEW3D_OT_background_image_add",
view3d_ima_bg_drop_poll,
view3d_id_path_drop_copy,
- WM_drag_free_imported_drag_ID);
+ WM_drag_free_imported_drag_ID,
+ NULL);
WM_dropbox_add(lb,
"OBJECT_OT_drop_named_image",
view3d_ima_empty_drop_poll,
view3d_id_path_drop_copy,
- WM_drag_free_imported_drag_ID);
+ WM_drag_free_imported_drag_ID,
+ NULL);
WM_dropbox_add(lb,
"OBJECT_OT_volume_import",
view3d_volume_drop_poll,
view3d_id_path_drop_copy,
- WM_drag_free_imported_drag_ID);
+ WM_drag_free_imported_drag_ID,
+ NULL);
WM_dropbox_add(lb,
"OBJECT_OT_collection_instance_add",
view3d_collection_drop_poll,
view3d_collection_drop_copy,
- WM_drag_free_imported_drag_ID);
+ WM_drag_free_imported_drag_ID,
+ NULL);
WM_dropbox_add(lb,
"OBJECT_OT_data_instance_add",
view3d_object_data_drop_poll,
view3d_id_drop_copy_with_type,
- WM_drag_free_imported_drag_ID);
+ WM_drag_free_imported_drag_ID,
+ view3d_object_data_drop_tooltip);
}
static void view3d_widgets(void)
diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c
index 3428a738dde..b79303551a1 100644
--- a/source/blender/editors/space_view3d/view3d_buttons.c
+++ b/source/blender/editors/space_view3d/view3d_buttons.c
@@ -1456,7 +1456,7 @@ static void v3d_transform_butsR(uiLayout *layout, PointerRNA *ptr)
colsub = uiLayoutColumn(split, true);
uiItemR(colsub, ptr, "location", 0, NULL, ICON_NONE);
colsub = uiLayoutColumn(split, true);
- uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE);
+ uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE_OR_STATUS);
uiItemL(colsub, "", ICON_NONE);
uiItemR(colsub,
ptr,
@@ -1472,7 +1472,7 @@ static void v3d_transform_butsR(uiLayout *layout, PointerRNA *ptr)
colsub = uiLayoutColumn(split, true);
uiItemR(colsub, ptr, "rotation_quaternion", 0, IFACE_("Rotation"), ICON_NONE);
colsub = uiLayoutColumn(split, true);
- uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE);
+ uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE_OR_STATUS);
uiItemR(colsub, ptr, "lock_rotations_4d", UI_ITEM_R_TOGGLE, IFACE_("4L"), ICON_NONE);
if (RNA_boolean_get(ptr, "lock_rotations_4d")) {
uiItemR(colsub,
@@ -1496,7 +1496,7 @@ static void v3d_transform_butsR(uiLayout *layout, PointerRNA *ptr)
colsub = uiLayoutColumn(split, true);
uiItemR(colsub, ptr, "rotation_axis_angle", 0, IFACE_("Rotation"), ICON_NONE);
colsub = uiLayoutColumn(split, true);
- uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE);
+ uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE_OR_STATUS);
uiItemR(colsub, ptr, "lock_rotations_4d", UI_ITEM_R_TOGGLE, IFACE_("4L"), ICON_NONE);
if (RNA_boolean_get(ptr, "lock_rotations_4d")) {
uiItemR(colsub,
@@ -1520,7 +1520,7 @@ static void v3d_transform_butsR(uiLayout *layout, PointerRNA *ptr)
colsub = uiLayoutColumn(split, true);
uiItemR(colsub, ptr, "rotation_euler", 0, IFACE_("Rotation"), ICON_NONE);
colsub = uiLayoutColumn(split, true);
- uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE);
+ uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE_OR_STATUS);
uiItemL(colsub, "", ICON_NONE);
uiItemR(colsub,
ptr,
@@ -1536,7 +1536,7 @@ static void v3d_transform_butsR(uiLayout *layout, PointerRNA *ptr)
colsub = uiLayoutColumn(split, true);
uiItemR(colsub, ptr, "scale", 0, NULL, ICON_NONE);
colsub = uiLayoutColumn(split, true);
- uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE);
+ uiLayoutSetEmboss(colsub, UI_EMBOSS_NONE_OR_STATUS);
uiItemL(colsub, "", ICON_NONE);
uiItemR(colsub,
ptr,
diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c
index d87c14b9844..ec99affe43b 100644
--- a/source/blender/editors/space_view3d/view3d_draw.c
+++ b/source/blender/editors/space_view3d/view3d_draw.c
@@ -1876,7 +1876,7 @@ ImBuf *ED_view3d_draw_offscreen_imbuf(Depsgraph *depsgraph,
if (own_ofs) {
/* bind */
- ofs = GPU_offscreen_create(sizex, sizey, true, false, err_out);
+ ofs = GPU_offscreen_create(sizex, sizey, true, GPU_RGBA8, err_out);
if (ofs == NULL) {
DRW_opengl_context_disable();
return NULL;
diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c
index 651ae8a3000..b055a0fe947 100644
--- a/source/blender/editors/space_view3d/view3d_edit.c
+++ b/source/blender/editors/space_view3d/view3d_edit.c
@@ -4917,10 +4917,7 @@ static int view3d_clipping_invoke(bContext *C, wmOperator *op, const wmEvent *ev
if (rv3d->rflag & RV3D_CLIPPING) {
rv3d->rflag &= ~RV3D_CLIPPING;
ED_region_tag_redraw(region);
- if (rv3d->clipbb) {
- MEM_freeN(rv3d->clipbb);
- }
- rv3d->clipbb = NULL;
+ MEM_SAFE_FREE(rv3d->clipbb);
return OPERATOR_FINISHED;
}
return WM_gesture_box_invoke(C, op, event);
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
index 49299d73337..edc34d0d883 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
@@ -299,7 +299,9 @@ static void view3d_ruler_item_project(RulerInfo *ruler_info, float r_co[3], cons
ED_view3d_win_to_3d_int(ruler_info->area->spacedata.first, ruler_info->region, r_co, xy, r_co);
}
-/* use for mousemove events */
+/**
+ * Use for mouse-move events.
+ */
static bool view3d_ruler_item_mousemove(struct Depsgraph *depsgraph,
RulerInfo *ruler_info,
RulerItem *ruler_item,
diff --git a/source/blender/editors/space_view3d/view3d_navigate_walk.c b/source/blender/editors/space_view3d/view3d_navigate_walk.c
index 09936b41a74..1ac241013ed 100644
--- a/source/blender/editors/space_view3d/view3d_navigate_walk.c
+++ b/source/blender/editors/space_view3d/view3d_navigate_walk.c
@@ -548,7 +548,7 @@ static bool initWalkInfo(bContext *C, WalkInfo *walk, wmOperator *op)
walk->teleport.duration = U.walk_navigation.teleport_time;
walk->mouse_speed = U.walk_navigation.mouse_speed;
- if ((U.walk_navigation.flag & USER_WALK_GRAVITY)) {
+ if (U.walk_navigation.flag & USER_WALK_GRAVITY) {
walk_navigation_mode_set(walk, WALK_MODE_GRAVITY);
}
else {
@@ -563,7 +563,7 @@ static bool initWalkInfo(bContext *C, WalkInfo *walk, wmOperator *op)
walk->gravity_state = WALK_GRAVITY_STATE_OFF;
- if ((walk->scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY)) {
+ if (walk->scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) {
walk->gravity = fabsf(walk->scene->physics_settings.gravity[2]);
}
else {
@@ -1199,11 +1199,11 @@ static int walkApply(bContext *C, WalkInfo *walk, bool is_confirm)
direction = 0;
- if ((walk->active_directions & WALK_BIT_FORWARD)) {
+ if (walk->active_directions & WALK_BIT_FORWARD) {
direction += 1;
}
- if ((walk->active_directions & WALK_BIT_BACKWARD)) {
+ if (walk->active_directions & WALK_BIT_BACKWARD) {
direction -= 1;
}
@@ -1223,11 +1223,11 @@ static int walkApply(bContext *C, WalkInfo *walk, bool is_confirm)
direction = 0;
- if ((walk->active_directions & WALK_BIT_LEFT)) {
+ if (walk->active_directions & WALK_BIT_LEFT) {
direction += 1;
}
- if ((walk->active_directions & WALK_BIT_RIGHT)) {
+ if (walk->active_directions & WALK_BIT_RIGHT) {
direction -= 1;
}
@@ -1245,11 +1245,11 @@ static int walkApply(bContext *C, WalkInfo *walk, bool is_confirm)
direction = 0;
- if ((walk->active_directions & WALK_BIT_UP)) {
+ if (walk->active_directions & WALK_BIT_UP) {
direction -= 1;
}
- if ((walk->active_directions & WALK_BIT_DOWN)) {
+ if (walk->active_directions & WALK_BIT_DOWN) {
direction = 1;
}
diff --git a/source/blender/editors/space_view3d/view3d_project.c b/source/blender/editors/space_view3d/view3d_project.c
index d926ea84e0f..88efc530484 100644
--- a/source/blender/editors/space_view3d/view3d_project.c
+++ b/source/blender/editors/space_view3d/view3d_project.c
@@ -843,7 +843,7 @@ bool ED_view3d_unproject_v3(
const int viewport[4] = {0, 0, region->winx, region->winy};
const float region_co[3] = {regionx, regiony, regionz};
- return GPU_matrix_unproject_3fv(region_co, rv3d->viewmat, rv3d->winmat, viewport, world);
+ return GPU_matrix_unproject_3fv(region_co, rv3d->viewinv, rv3d->winmat, viewport, world);
}
/** \} */
diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c
index ecf43c734e2..e3f97dd1c63 100644
--- a/source/blender/editors/space_view3d/view3d_select.c
+++ b/source/blender/editors/space_view3d/view3d_select.c
@@ -1589,9 +1589,12 @@ void VIEW3D_OT_select_menu(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_ENUM_NO_TRANSLATE);
ot->prop = prop;
- RNA_def_boolean(ot->srna, "extend", 0, "Extend", "");
- RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "");
- RNA_def_boolean(ot->srna, "toggle", 0, "Toggle", "");
+ prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend", "");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(ot->srna, "toggle", 0, "Toggle", "");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
static Base *object_mouse_select_menu(bContext *C,
@@ -1764,9 +1767,12 @@ void VIEW3D_OT_bone_select_menu(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_ENUM_NO_TRANSLATE);
ot->prop = prop;
- RNA_def_boolean(ot->srna, "extend", 0, "Extend", "");
- RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "");
- RNA_def_boolean(ot->srna, "toggle", 0, "Toggle", "");
+ prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend", "");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(ot->srna, "toggle", 0, "Toggle", "");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
static bool bone_mouse_select_menu(bContext *C,
const uint *buffer,
@@ -2150,7 +2156,7 @@ static Base *mouse_select_eval_buffer(ViewContext *vc,
for (a = 0; a < hits; a++) {
if (has_bones) {
/* skip non-bone objects */
- if ((buffer[4 * a + 3] & 0xFFFF0000)) {
+ if (buffer[4 * a + 3] & 0xFFFF0000) {
if (base->object->runtime.select_id == (buffer[(4 * a) + 3] & 0xFFFF)) {
basact = base;
}
@@ -2515,6 +2521,7 @@ static bool ed_object_select_pick(bContext *C,
}
/* also prevent making it active on mouse selection */
else if (BASE_SELECTABLE(v3d, basact)) {
+ const bool use_activate_selected_base = (oldbasact != basact) && (is_obedit == false);
if (extend) {
ED_object_base_select(basact, BA_SELECT);
}
@@ -2523,7 +2530,8 @@ static bool ed_object_select_pick(bContext *C,
}
else if (toggle) {
if (basact->flag & BASE_SELECTED) {
- if (basact == oldbasact) {
+ /* Keep selected if the base is to be activated. */
+ if (use_activate_selected_base == false) {
ED_object_base_select(basact, BA_DESELECT);
}
}
@@ -2539,7 +2547,7 @@ static bool ed_object_select_pick(bContext *C,
}
}
- if ((oldbasact != basact) && (is_obedit == false)) {
+ if (use_activate_selected_base) {
ED_object_base_activate(C, basact); /* adds notifier */
if ((scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) == 0) {
WM_toolsystem_update_from_context_view3d(C);
diff --git a/source/blender/editors/transform/CMakeLists.txt b/source/blender/editors/transform/CMakeLists.txt
index ad0a330f0f4..e9efed3cd61 100644
--- a/source/blender/editors/transform/CMakeLists.txt
+++ b/source/blender/editors/transform/CMakeLists.txt
@@ -101,6 +101,7 @@ set(SRC
transform_ops.c
transform_orientations.c
transform_snap.c
+ transform_snap_animation.c
transform_snap_object.c
transform_snap_sequencer.c
diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c
index efcf7d587e1..4069a72a8fc 100644
--- a/source/blender/editors/transform/transform.c
+++ b/source/blender/editors/transform/transform.c
@@ -78,6 +78,9 @@ static void initSnapSpatial(TransInfo *t, float r_snap[2]);
bool transdata_check_local_islands(TransInfo *t, short around)
{
+ if (t->options & (CTX_CURSOR | CTX_TEXTURE_SPACE)) {
+ return false;
+ }
return ((around == V3D_AROUND_LOCAL_ORIGINS) && (ELEM(t->obedit_type, OB_MESH, OB_GPENCIL)));
}
@@ -1828,7 +1831,7 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
if ((t->flag & T_EDIT) && t->obedit_type == OB_MESH) {
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- if ((((Mesh *)(tc->obedit->data))->flag & ME_AUTOSMOOTH)) {
+ if (((Mesh *)(tc->obedit->data))->flag & ME_AUTOSMOOTH) {
BMEditMesh *em = NULL; /* BKE_editmesh_from_object(t->obedit); */
bool do_skip = false;
diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h
index 1a61a594f37..549ad770ac6 100644
--- a/source/blender/editors/transform/transform.h
+++ b/source/blender/editors/transform/transform.h
@@ -582,7 +582,7 @@ typedef struct TransInfo {
short around;
/** space-type where transforming is. */
char spacetype;
- /** Avoid looking inside #TransDataContainer.obedit. */
+ /** Type of active object being edited. */
short obedit_type;
/** translation, to show for widget. */
@@ -779,7 +779,6 @@ void drawLine(TransInfo *t, const float center[3], const float dir[3], char axis
void applyTransObjects(TransInfo *t);
void restoreTransObjects(TransInfo *t);
-void recalcData(TransInfo *t);
void calculateCenter2D(TransInfo *t);
void calculateCenterLocal(TransInfo *t, const float center_global[3]);
diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c
index 00fd008151d..094ae080de0 100644
--- a/source/blender/editors/transform/transform_convert.c
+++ b/source/blender/editors/transform/transform_convert.c
@@ -861,10 +861,13 @@ bool constraints_list_needinv(TransInfo *t, ListBase *list)
/* The Action constraint only does this in the Before mode. */
bActionConstraint *data = (bActionConstraint *)con->data;
- if (ELEM(data->mix_mode, ACTCON_MIX_BEFORE) &&
+ if (ELEM(data->mix_mode, ACTCON_MIX_BEFORE, ACTCON_MIX_BEFORE_FULL) &&
ELEM(t->mode, TFM_ROTATION, TFM_TRANSLATION)) {
return true;
}
+ if (ELEM(data->mix_mode, ACTCON_MIX_BEFORE_SPLIT) && ELEM(t->mode, TFM_ROTATION)) {
+ return true;
+ }
}
else if (con->type == CONSTRAINT_TYPE_TRANSFORM) {
/* Transform constraint needs it for rotation at least (r.57309),
@@ -1069,7 +1072,8 @@ static void init_proportional_edit(TransInfo *t)
else if (convert_type == TC_MESH_UV && t->flag & T_PROP_CONNECTED) {
/* Already calculated by uv_set_connectivity_distance. */
}
- else if (convert_type == TC_CURVE_VERTS && t->obedit_type == OB_CURVE) {
+ else if (convert_type == TC_CURVE_VERTS) {
+ BLI_assert(t->obedit_type == OB_CURVE);
set_prop_dist(t, false);
}
else {
@@ -1368,7 +1372,6 @@ void createTransData(bContext *C, TransInfo *t)
switch (t->data_type) {
case TC_ACTION_DATA:
- t->obedit_type = -1;
createTransActionData(C, t);
break;
case TC_POSE:
@@ -1391,7 +1394,6 @@ void createTransData(bContext *C, TransInfo *t)
createTransCurveVerts(t);
break;
case TC_GRAPH_EDIT_DATA:
- t->obedit_type = -1;
createTransGraphEditData(C, t);
break;
case TC_GPENCIL:
@@ -1401,9 +1403,6 @@ void createTransData(bContext *C, TransInfo *t)
createTransLatticeVerts(t);
break;
case TC_MASKING_DATA:
- if (t->spacetype == SPACE_CLIP) {
- t->obedit_type = -1;
- }
createTransMaskingData(C, t);
break;
case TC_MBALL_VERTS:
@@ -1422,11 +1421,9 @@ void createTransData(bContext *C, TransInfo *t)
createTransUVs(C, t);
break;
case TC_NLA_DATA:
- t->obedit_type = -1;
createTransNlaData(C, t);
break;
case TC_NODE_DATA:
- t->obedit_type = -1;
createTransNodeData(t);
break;
case TC_OBJECT:
@@ -1470,12 +1467,10 @@ void createTransData(bContext *C, TransInfo *t)
createTransSculpt(C, t);
break;
case TC_SEQ_DATA:
- t->obedit_type = -1;
t->num.flag |= NUM_NO_FRACTION; /* sequencer has no use for floating point transform. */
createTransSeqData(t);
break;
case TC_TRACKING_DATA:
- t->obedit_type = -1;
createTransTrackingData(C, t);
break;
case TC_NONE:
@@ -1489,8 +1484,6 @@ void createTransData(bContext *C, TransInfo *t)
countAndCleanTransDataContainer(t);
init_proportional_edit(t);
-
- BLI_assert((!(t->flag & T_EDIT)) == (!(t->obedit_type != -1)));
}
/** \} */
@@ -1669,6 +1662,23 @@ void animrecord_check_state(TransInfo *t, struct Object *ob)
}
}
+void transform_convert_flush_handle2D(TransData *td, TransData2D *td2d, const float inv_unit_scale)
+{
+ /* If the handles are to be moved too
+ * (as side-effect of keyframes moving, to keep the general effect)
+ * offset them by the same amount so that the general angles are maintained
+ * (i.e. won't change while handles are free-to-roam and keyframes are snap-locked).
+ */
+ if ((td->flag & TD_MOVEHANDLE1) && td2d->h1) {
+ td2d->h1[0] = td2d->ih1[0] + td->loc[0] - td->iloc[0];
+ td2d->h1[1] = td2d->ih1[1] + (td->loc[1] - td->iloc[1]) * inv_unit_scale;
+ }
+ if ((td->flag & TD_MOVEHANDLE2) && td2d->h2) {
+ td2d->h2[0] = td2d->ih2[0] + td->loc[0] - td->iloc[0];
+ td2d->h2[1] = td2d->ih2[1] + (td->loc[1] - td->iloc[1]) * inv_unit_scale;
+ }
+}
+
/* called for updating while transform acts, once per redraw */
void recalcData(TransInfo *t)
{
diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h
index 971c23b8c69..fa34e2555d6 100644
--- a/source/blender/editors/transform/transform_convert.h
+++ b/source/blender/editors/transform/transform_convert.h
@@ -43,6 +43,10 @@ void sort_trans_data_dist(TransInfo *t);
void createTransData(struct bContext *C, TransInfo *t);
bool clipUVTransform(TransInfo *t, float vec[2], const bool resize);
void clipUVData(TransInfo *t);
+void transform_convert_flush_handle2D(TransData *td,
+ TransData2D *td2d,
+ const float inv_unit_scale);
+void recalcData(TransInfo *t);
/* transform_convert_mesh.c */
void transform_convert_mesh_customdatacorrect_init(TransInfo *t);
diff --git a/source/blender/editors/transform/transform_convert_action.c b/source/blender/editors/transform/transform_convert_action.c
index cfa14e21d0d..a5565b5fb88 100644
--- a/source/blender/editors/transform/transform_convert_action.c
+++ b/source/blender/editors/transform/transform_convert_action.c
@@ -45,6 +45,8 @@
#include "WM_types.h"
#include "transform.h"
+#include "transform_snap.h"
+
#include "transform_convert.h"
/* helper struct for gp-frame transforms */
@@ -140,19 +142,37 @@ static int count_masklayer_frames(MaskLayer *masklay, char side, float cfra, boo
}
/* This function assigns the information to transdata */
-static void TimeToTransData(TransData *td, float *time, AnimData *adt, float ypos)
+static void TimeToTransData(
+ TransData *td, TransData2D *td2d, BezTriple *bezt, AnimData *adt, float ypos)
{
- /* memory is calloc'ed, so that should zero everything nicely for us */
+ float *time = bezt->vec[1];
+
+ /* Setup #TransData2D. */
+ td2d->loc[0] = *time;
+ td2d->loc2d = time;
+ td2d->h1 = bezt->vec[0];
+ td2d->h2 = bezt->vec[2];
+ copy_v2_v2(td2d->ih1, td2d->h1);
+ copy_v2_v2(td2d->ih2, td2d->h2);
+
+ /* Setup #TransData. */
+ td->loc = time; /* Usually #td2d->loc is used here. But this is for when the original location is
+ not float[3]. */
td->val = time;
- td->ival = *(time);
-
+ td->ival = td->iloc[0] = *(time);
td->center[0] = td->ival;
td->center[1] = ypos;
- /* store the AnimData where this keyframe exists as a keyframe of the
- * active action as td->extra.
- */
+ /* Store the AnimData where this keyframe exists as a keyframe of the
+ * active action as #td->extra. */
td->extra = adt;
+
+ if (bezt->f2 & SELECT) {
+ td->flag |= TD_SELECTED;
+ }
+
+ /* Set flags to move handles as necessary. */
+ td->flag |= TD_MOVEHANDLE1 | TD_MOVEHANDLE2;
}
/* This function advances the address to which td points to, so it must return
@@ -185,19 +205,7 @@ static TransData *ActionFCurveToTransData(TransData *td,
* so can't use BEZT_ISSEL_ANY() macro */
/* only add if on the right 'side' of the current frame */
if (FrameOnMouseSide(side, bezt->vec[1][0], cfra)) {
- TimeToTransData(td, bezt->vec[1], adt, ypos);
-
- if (bezt->f2 & SELECT) {
- td->flag |= TD_SELECTED;
- }
-
- /* Set flags to move handles as necessary. */
- td->flag |= TD_MOVEHANDLE1 | TD_MOVEHANDLE2;
- td2d->h1 = bezt->vec[0];
- td2d->h2 = bezt->vec[2];
-
- copy_v2_v2(td2d->ih1, td2d->h1);
- copy_v2_v2(td2d->ih2, td2d->h2);
+ TimeToTransData(td, td2d, bezt, adt, ypos);
td++;
td2d++;
@@ -233,16 +241,15 @@ static int GPLayerToTransData(TransData *td,
for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
if (is_prop_edit || (gpf->flag & GP_FRAME_SELECT)) {
if (FrameOnMouseSide(side, (float)gpf->framenum, cfra)) {
- /* memory is calloc'ed, so that should zero everything nicely for us */
- td->val = &tfd->val;
- td->ival = (float)gpf->framenum;
+ tfd->val = (float)gpf->framenum;
+ tfd->sdata = &gpf->framenum;
+
+ td->val = td->loc = &tfd->val; /* XXX: It's not a 3d array. */
+ td->ival = td->iloc[0] = (float)gpf->framenum;
td->center[0] = td->ival;
td->center[1] = ypos;
- tfd->val = (float)gpf->framenum;
- tfd->sdata = &gpf->framenum;
-
/* Advance `td` now. */
td++;
tfd++;
@@ -598,6 +605,19 @@ void recalcData_actedit(TransInfo *t)
flushTransIntFrameActionData(t);
}
+ /* Flush 2d vector. */
+ TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
+ const short autosnap = getAnimEdit_SnapMode(t);
+ TransData *td;
+ TransData2D *td2d;
+ int i = 0;
+ for (td = tc->data, td2d = tc->data_2d; i < tc->data_len; i++, td++, td2d++) {
+ if ((autosnap != SACTSNAP_OFF) && (t->state != TRANS_CANCEL) && !(td->flag & TD_NOTIMESNAP)) {
+ transform_snap_anim_flush_data(t, td, autosnap, td->loc);
+ }
+ transform_convert_flush_handle2D(td, td2d, 1.0f);
+ }
+
if (ac.datatype != ANIMCONT_MASK) {
/* Get animdata blocks visible in editor,
* assuming that these will be the ones where things changed. */
diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c
index 1f1b1f8db97..5627a910ab4 100644
--- a/source/blender/editors/transform/transform_convert_armature.c
+++ b/source/blender/editors/transform/transform_convert_armature.c
@@ -131,12 +131,12 @@ static void autokeyframe_pose(
ListBase dsources = {NULL, NULL};
- /* add datasource override for the camera object */
+ /* Add data-source override for the camera object. */
ANIM_relative_keyingset_add_source(&dsources, id, &RNA_PoseBone, pchan);
/* only insert into active keyingset? */
if (IS_AUTOKEY_FLAG(scene, ONLYKEYINGSET) && (active_ks)) {
- /* run the active Keying Set on the current datasource */
+ /* Run the active Keying Set on the current data-source. */
ANIM_apply_keyingset(
C, &dsources, NULL, active_ks, MODIFYKEY_MODE_INSERT, anim_eval_context.eval_time);
}
@@ -1515,7 +1515,7 @@ int transform_convert_pose_transflags_update(Object *ob,
for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
bone = pchan->bone;
if (PBONE_VISIBLE(arm, bone)) {
- if ((bone->flag & BONE_SELECTED)) {
+ if (bone->flag & BONE_SELECTED) {
bone->flag |= BONE_TRANSFORM;
}
else {
diff --git a/source/blender/editors/transform/transform_convert_graph.c b/source/blender/editors/transform/transform_convert_graph.c
index 111f81ff87b..d22277f9d91 100644
--- a/source/blender/editors/transform/transform_convert_graph.c
+++ b/source/blender/editors/transform/transform_convert_graph.c
@@ -40,7 +40,12 @@
#include "UI_view2d.h"
#include "transform.h"
+#include "transform_snap.h"
+
#include "transform_convert.h"
+#include "transform_snap.h"
+
+#include "transform_mode.h"
typedef struct TransDataGraph {
float unit_scale;
@@ -656,14 +661,12 @@ static bool fcu_test_selected(FCurve *fcu)
*/
static void flushTransGraphData(TransInfo *t)
{
- SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first;
TransData *td;
TransData2D *td2d;
TransDataGraph *tdg;
- Scene *scene = t->scene;
- double secf = FPS;
int a;
+ const short autosnap = getAnimEdit_SnapMode(t);
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
/* flush to 2d vector from internally used 3d vector */
@@ -679,21 +682,8 @@ static void flushTransGraphData(TransInfo *t)
* - Only apply to keyframes (but never to handles).
* - Don't do this when canceling, or else these changes won't go away.
*/
- if ((t->state != TRANS_CANCEL) && (td->flag & TD_NOTIMESNAP) == 0) {
- switch (sipo->autosnap) {
- case SACTSNAP_FRAME: /* snap to nearest frame */
- td2d->loc[0] = floor((double)td2d->loc[0] + 0.5);
- break;
-
- case SACTSNAP_SECOND: /* snap to nearest second */
- td2d->loc[0] = floor(((double)td2d->loc[0] / secf) + 0.5) * secf;
- break;
-
- case SACTSNAP_MARKER: /* snap to nearest marker */
- td2d->loc[0] = (float)ED_markers_find_nearest_marker_time(&t->scene->markers,
- td2d->loc[0]);
- break;
- }
+ if ((autosnap != SACTSNAP_OFF) && (t->state != TRANS_CANCEL) && !(td->flag & TD_NOTIMESNAP)) {
+ transform_snap_anim_flush_data(t, td, autosnap, td->loc);
}
/* we need to unapply the nla-mapping from the time in some situations */
@@ -704,32 +694,6 @@ static void flushTransGraphData(TransInfo *t)
td2d->loc2d[0] = td2d->loc[0];
}
- /** Time-stepping auto-snapping modes don't get applied for Graph Editor transforms,
- * as these use the generic transform modes which don't account for this sort of thing.
- * These ones aren't affected by NLA mapping, so we do this after the conversion...
- *
- * \note We also have to apply to td->loc,
- * as that's what the handle-adjustment step below looks to,
- * otherwise we get "swimming handles".
- *
- * \note We don't do this when canceling transforms, or else these changes don't go away.
- */
- if ((t->state != TRANS_CANCEL) && (td->flag & TD_NOTIMESNAP) == 0 &&
- ELEM(sipo->autosnap, SACTSNAP_STEP, SACTSNAP_TSTEP)) {
- switch (sipo->autosnap) {
- case SACTSNAP_STEP: /* frame step */
- td2d->loc2d[0] = floor((double)td2d->loc[0] + 0.5);
- td->loc[0] = floor((double)td->loc[0] + 0.5);
- break;
-
- case SACTSNAP_TSTEP: /* second step */
- /* XXX: the handle behavior in this case is still not quite right... */
- td2d->loc[0] = floor(((double)td2d->loc[0] / secf) + 0.5) * secf;
- td->loc[0] = floor(((double)td->loc[0] / secf) + 0.5) * secf;
- break;
- }
- }
-
/* if int-values only, truncate to integers */
if (td->flag & TD_INTVALUES) {
td2d->loc2d[1] = floorf(td2d->loc[1] * inv_unit_scale - tdg->offset + 0.5f);
@@ -738,15 +702,7 @@ static void flushTransGraphData(TransInfo *t)
td2d->loc2d[1] = td2d->loc[1] * inv_unit_scale - tdg->offset;
}
- if ((td->flag & TD_MOVEHANDLE1) && td2d->h1) {
- td2d->h1[0] = td2d->ih1[0] + td->loc[0] - td->iloc[0];
- td2d->h1[1] = td2d->ih1[1] + (td->loc[1] - td->iloc[1]) * inv_unit_scale;
- }
-
- if ((td->flag & TD_MOVEHANDLE2) && td2d->h2) {
- td2d->h2[0] = td2d->ih2[0] + td->loc[0] - td->iloc[0];
- td2d->h2[1] = td2d->ih2[1] + (td->loc[1] - td->iloc[1]) * inv_unit_scale;
- }
+ transform_convert_flush_handle2D(td, td2d, inv_unit_scale);
}
}
diff --git a/source/blender/editors/transform/transform_convert_mask.c b/source/blender/editors/transform/transform_convert_mask.c
index 54df8270702..1a25cfd1efb 100644
--- a/source/blender/editors/transform/transform_convert_mask.c
+++ b/source/blender/editors/transform/transform_convert_mask.c
@@ -293,7 +293,7 @@ void createTransMaskingData(bContext *C, TransInfo *t)
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
MaskSpline *spline;
- if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+ if (masklay->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
@@ -351,7 +351,7 @@ void createTransMaskingData(bContext *C, TransInfo *t)
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
MaskSpline *spline;
- if (masklay->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) {
+ if (masklay->visibility_flag & (MASK_HIDE_VIEW | MASK_HIDE_SELECT)) {
continue;
}
diff --git a/source/blender/editors/transform/transform_convert_nla.c b/source/blender/editors/transform/transform_convert_nla.c
index b55005673d9..7e5b80c2453 100644
--- a/source/blender/editors/transform/transform_convert_nla.c
+++ b/source/blender/editors/transform/transform_convert_nla.c
@@ -41,7 +41,12 @@
#include "RNA_access.h"
#include "transform.h"
+#include "transform_snap.h"
+
#include "transform_convert.h"
+#include "transform_snap.h"
+
+#include "transform_mode.h"
/** Used for NLA transform (stored in #TransData.extra pointer). */
typedef struct TransDataNla {
@@ -289,21 +294,30 @@ void createTransNlaData(bContext *C, TransInfo *t)
void recalcData_nla(TransInfo *t)
{
SpaceNla *snla = (SpaceNla *)t->area->spacedata.first;
- Scene *scene = t->scene;
- double secf = FPS;
- int i;
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
- TransDataNla *tdn = tc->custom.type.data;
+
+ /* handle auto-snapping
+ * NOTE: only do this when transform is still running, or we can't restore
+ */
+ if (t->state != TRANS_CANCEL) {
+ const short autosnap = getAnimEdit_SnapMode(t);
+ if (autosnap != SACTSNAP_OFF) {
+ TransData *td = tc->data;
+ for (int i = 0; i < tc->data_len; i++, td++) {
+ transform_snap_anim_flush_data(t, td, autosnap, td->loc);
+ }
+ }
+ }
/* For each strip we've got, perform some additional validation of the values
* that got set before using RNA to set the value (which does some special
* operations when setting these values to make sure that everything works ok).
*/
- for (i = 0; i < tc->data_len; i++, tdn++) {
+ TransDataNla *tdn = tc->custom.type.data;
+ for (int i = 0; i < tc->data_len; i++, tdn++) {
NlaStrip *strip = tdn->strip;
PointerRNA strip_ptr;
- short iter;
int delta_y1, delta_y2;
/* if this tdn has no handles, that means it is just a dummy that should be skipped */
@@ -367,8 +381,7 @@ void recalcData_nla(TransInfo *t)
next = next->next;
}
- for (iter = 0; iter < 5; iter++) {
-
+ for (short iter = 0; iter < 5; iter++) {
const bool pExceeded = (prev != NULL) && (tdn->h1[0] < prev->end);
const bool nExceeded = (next != NULL) && (tdn->h2[0] > next->start);
@@ -407,50 +420,6 @@ void recalcData_nla(TransInfo *t)
}
}
- /* handle auto-snapping
- * NOTE: only do this when transform is still running, or we can't restore
- */
- if (t->state != TRANS_CANCEL) {
- switch (snla->autosnap) {
- case SACTSNAP_FRAME: /* snap to nearest frame */
- case SACTSNAP_STEP: /* frame step - this is basically the same,
- * since we don't have any remapping going on */
- {
- tdn->h1[0] = floorf(tdn->h1[0] + 0.5f);
- tdn->h2[0] = floorf(tdn->h2[0] + 0.5f);
- break;
- }
-
- case SACTSNAP_SECOND: /* snap to nearest second */
- case SACTSNAP_TSTEP: /* second step - this is basically the same,
- * since we don't have any remapping going on */
- {
- /* This case behaves differently from the rest, since lengths of strips
- * may not be multiples of a second. If we just naively resize adjust
- * the handles, things may not work correctly. Instead, we only snap
- * the first handle, and move the other to fit.
- *
- * FIXME: we do run into problems here when user attempts to negatively
- * scale the strip, as it then just compresses down and refuses
- * to expand out the other end.
- */
- float h1_new = (float)(floor(((double)tdn->h1[0] / secf) + 0.5) * secf);
- float delta = h1_new - tdn->h1[0];
-
- tdn->h1[0] = h1_new;
- tdn->h2[0] += delta;
- break;
- }
-
- case SACTSNAP_MARKER: /* snap to nearest marker */
- {
- tdn->h1[0] = (float)ED_markers_find_nearest_marker_time(&t->scene->markers, tdn->h1[0]);
- tdn->h2[0] = (float)ED_markers_find_nearest_marker_time(&t->scene->markers, tdn->h2[0]);
- break;
- }
- }
- }
-
/* Use RNA to write the values to ensure that constraints on these are obeyed
* (e.g. for transition strips, the values are taken from the neighbors)
*
diff --git a/source/blender/editors/transform/transform_convert_object.c b/source/blender/editors/transform/transform_convert_object.c
index ee6cb391fdc..bcbac009948 100644
--- a/source/blender/editors/transform/transform_convert_object.c
+++ b/source/blender/editors/transform/transform_convert_object.c
@@ -749,7 +749,7 @@ static void autokeyframe_object(
/* Get flags used for inserting keyframes. */
flag = ANIM_get_keyframing_flags(scene, true);
- /* add datasource override for the object */
+ /* Add data-source override for the object. */
ANIM_relative_keyingset_add_source(&dsources, id, NULL, NULL);
if (IS_AUTOKEY_FLAG(scene, ONLYKEYINGSET) && (active_ks)) {
diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c
index e89ab6729d2..81fc1496b1a 100644
--- a/source/blender/editors/transform/transform_generics.c
+++ b/source/blender/editors/transform/transform_generics.c
@@ -60,6 +60,7 @@
#include "UI_view2d.h"
#include "transform.h"
+#include "transform_convert.h"
#include "transform_mode.h"
#include "transform_orientations.h"
#include "transform_snap.h"
@@ -152,8 +153,7 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
t->flag = 0;
- if (obact && !(t->options & (CTX_CURSOR | CTX_TEXTURE_SPACE)) &&
- ELEM(object_mode, OB_MODE_EDIT, OB_MODE_EDIT_GPENCIL)) {
+ if (obact && ELEM(object_mode, OB_MODE_EDIT, OB_MODE_EDIT_GPENCIL)) {
t->obedit_type = obact->type;
}
else {
diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c
index 65a673940f8..8df95222fa1 100644
--- a/source/blender/editors/transform/transform_mode.c
+++ b/source/blender/editors/transform/transform_mode.c
@@ -968,9 +968,9 @@ void ElementResize(const TransInfo *t,
float obsizemat[3][3];
/* Reorient the size mat to fit the oriented object. */
mul_m3_m3m3(obsizemat, tmat, td->axismtx);
- /* print_m3("obsizemat", obsizemat); */
+ // print_m3("obsizemat", obsizemat);
TransMat3ToSize(obsizemat, td->axismtx, fsize);
- /* print_v3("fsize", fsize); */
+ // print_v3("fsize", fsize);
}
else {
mat3_to_size(fsize, tmat);
@@ -1068,102 +1068,6 @@ void ElementResize(const TransInfo *t,
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Transform (Frame Utils)
- * \{ */
-
-/**
- * This function returns the snapping 'mode' for Animation Editors only.
- * We cannot use the standard snapping due to NLA-strip scaling complexities.
- *
- * TODO: these modifier checks should be key-mappable.
- */
-short getAnimEdit_SnapMode(TransInfo *t)
-{
- short autosnap = SACTSNAP_OFF;
-
- if (t->spacetype == SPACE_ACTION) {
- SpaceAction *saction = (SpaceAction *)t->area->spacedata.first;
-
- if (saction) {
- autosnap = saction->autosnap;
- }
- }
- else if (t->spacetype == SPACE_GRAPH) {
- SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first;
-
- if (sipo) {
- autosnap = sipo->autosnap;
- }
- }
- else if (t->spacetype == SPACE_NLA) {
- SpaceNla *snla = (SpaceNla *)t->area->spacedata.first;
-
- if (snla) {
- autosnap = snla->autosnap;
- }
- }
- else {
- autosnap = SACTSNAP_OFF;
- }
-
- /* toggle autosnap on/off
- * - when toggling on, prefer nearest frame over 1.0 frame increments
- */
- if (t->modifiers & MOD_SNAP_INVERT) {
- if (autosnap) {
- autosnap = SACTSNAP_OFF;
- }
- else {
- autosnap = SACTSNAP_FRAME;
- }
- }
-
- return autosnap;
-}
-
-/* This function is used by Animation Editor specific transform functions to do
- * the Snap Keyframe to Nearest Frame/Marker
- */
-void doAnimEdit_SnapFrame(
- TransInfo *t, TransData *td, TransData2D *td2d, AnimData *adt, short autosnap)
-{
- if (autosnap != SACTSNAP_OFF) {
- float val;
-
- /* convert frame to nla-action time (if needed) */
- if (adt && (t->spacetype != SPACE_SEQ)) {
- val = BKE_nla_tweakedit_remap(adt, *(td->val), NLATIME_CONVERT_MAP);
- }
- else {
- val = *(td->val);
- }
-
- snapFrameTransform(t, autosnap, true, val, &val);
-
- /* convert frame out of nla-action time */
- if (adt && (t->spacetype != SPACE_SEQ)) {
- *(td->val) = BKE_nla_tweakedit_remap(adt, val, NLATIME_CONVERT_UNMAP);
- }
- else {
- *(td->val) = val;
- }
- }
-
- /* If the handles are to be moved too
- * (as side-effect of keyframes moving, to keep the general effect)
- * offset them by the same amount so that the general angles are maintained
- * (i.e. won't change while handles are free-to-roam and keyframes are snap-locked).
- */
- if ((td->flag & TD_MOVEHANDLE1) && td2d->h1) {
- td2d->h1[0] = td2d->ih1[0] + *td->val - td->ival;
- }
- if ((td->flag & TD_MOVEHANDLE2) && td2d->h2) {
- td2d->h2[0] = td2d->ih2[0] + *td->val - td->ival;
- }
-}
-/** \} */
-
-/* -------------------------------------------------------------------- */
/** \name Transform Mode Initialization
* \{ */
diff --git a/source/blender/editors/transform/transform_mode.h b/source/blender/editors/transform/transform_mode.h
index 027fb6b6982..d8601000ddb 100644
--- a/source/blender/editors/transform/transform_mode.h
+++ b/source/blender/editors/transform/transform_mode.h
@@ -63,9 +63,6 @@ void ElementResize(const TransInfo *t,
const TransDataContainer *tc,
TransData *td,
const float mat[3][3]);
-short getAnimEdit_SnapMode(TransInfo *t);
-void doAnimEdit_SnapFrame(
- TransInfo *t, TransData *td, TransData2D *td2d, struct AnimData *adt, short autosnap);
void transform_mode_init(TransInfo *t, struct wmOperator *op, const int mode);
void transform_mode_default_modal_orientation_set(TransInfo *t, int type);
diff --git a/source/blender/editors/transform/transform_mode_align.c b/source/blender/editors/transform/transform_mode_align.c
index 5bc2aa68443..1a1d84699f4 100644
--- a/source/blender/editors/transform/transform_mode_align.c
+++ b/source/blender/editors/transform/transform_mode_align.c
@@ -32,6 +32,8 @@
#include "BLT_translation.h"
#include "transform.h"
+#include "transform_convert.h"
+
#include "transform_mode.h"
/* -------------------------------------------------------------------- */
diff --git a/source/blender/editors/transform/transform_mode_baketime.c b/source/blender/editors/transform/transform_mode_baketime.c
index 5efed6920dc..653944b56a7 100644
--- a/source/blender/editors/transform/transform_mode_baketime.c
+++ b/source/blender/editors/transform/transform_mode_baketime.c
@@ -36,9 +36,11 @@
#include "BLT_translation.h"
#include "transform.h"
-#include "transform_mode.h"
+#include "transform_convert.h"
#include "transform_snap.h"
+#include "transform_mode.h"
+
/* -------------------------------------------------------------------- */
/** \name Transform (Bake-Time)
* \{ */
diff --git a/source/blender/editors/transform/transform_mode_bbone_resize.c b/source/blender/editors/transform/transform_mode_bbone_resize.c
index e827e604327..95e2d944b9b 100644
--- a/source/blender/editors/transform/transform_mode_bbone_resize.c
+++ b/source/blender/editors/transform/transform_mode_bbone_resize.c
@@ -37,9 +37,11 @@
#include "transform.h"
#include "transform_constraints.h"
-#include "transform_mode.h"
+#include "transform_convert.h"
#include "transform_snap.h"
+#include "transform_mode.h"
+
/* -------------------------------------------------------------------- */
/** \name Transform (EditBone B-Bone width scaling)
* \{ */
diff --git a/source/blender/editors/transform/transform_mode_bend.c b/source/blender/editors/transform/transform_mode_bend.c
index 850d26571cd..6d84c397fa6 100644
--- a/source/blender/editors/transform/transform_mode_bend.c
+++ b/source/blender/editors/transform/transform_mode_bend.c
@@ -44,9 +44,11 @@
#include "BLT_translation.h"
#include "transform.h"
-#include "transform_mode.h"
+#include "transform_convert.h"
#include "transform_snap.h"
+#include "transform_mode.h"
+
/* -------------------------------------------------------------------- */
/** \name Transform (Bend) Custom Data
* \{ */
diff --git a/source/blender/editors/transform/transform_mode_boneenvelope.c b/source/blender/editors/transform/transform_mode_boneenvelope.c
index ced159a76c9..da7393ab42e 100644
--- a/source/blender/editors/transform/transform_mode_boneenvelope.c
+++ b/source/blender/editors/transform/transform_mode_boneenvelope.c
@@ -36,9 +36,11 @@
#include "BLT_translation.h"
#include "transform.h"
-#include "transform_mode.h"
+#include "transform_convert.h"
#include "transform_snap.h"
+#include "transform_mode.h"
+
/* -------------------------------------------------------------------- */
/** \name Transform (Bone Envelope)
* \{ */
diff --git a/source/blender/editors/transform/transform_mode_boneroll.c b/source/blender/editors/transform/transform_mode_boneroll.c
index da6c0b44c3a..cd04ca2b844 100644
--- a/source/blender/editors/transform/transform_mode_boneroll.c
+++ b/source/blender/editors/transform/transform_mode_boneroll.c
@@ -36,9 +36,11 @@
#include "BLT_translation.h"
#include "transform.h"
-#include "transform_mode.h"
+#include "transform_convert.h"
#include "transform_snap.h"
+#include "transform_mode.h"
+
/* -------------------------------------------------------------------- */
/** \name Transform (EditBone Roll)
* \{ */
diff --git a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c
index 68416c780ef..9433502ef55 100644
--- a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c
+++ b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c
@@ -36,9 +36,11 @@
#include "BLT_translation.h"
#include "transform.h"
-#include "transform_mode.h"
+#include "transform_convert.h"
#include "transform_snap.h"
+#include "transform_mode.h"
+
/* -------------------------------------------------------------------- */
/** \name Transform (Curve Shrink/Fatten)
* \{ */
diff --git a/source/blender/editors/transform/transform_mode_edge_bevelweight.c b/source/blender/editors/transform/transform_mode_edge_bevelweight.c
index 425bfec241e..5466ba3e91f 100644
--- a/source/blender/editors/transform/transform_mode_edge_bevelweight.c
+++ b/source/blender/editors/transform/transform_mode_edge_bevelweight.c
@@ -37,9 +37,11 @@
#include "BLT_translation.h"
#include "transform.h"
-#include "transform_mode.h"
+#include "transform_convert.h"
#include "transform_snap.h"
+#include "transform_mode.h"
+
/* -------------------------------------------------------------------- */
/** \name Transform (Bevel Weight) Element
* \{ */
diff --git a/source/blender/editors/transform/transform_mode_edge_crease.c b/source/blender/editors/transform/transform_mode_edge_crease.c
index 91e2507e544..1d3b4dbb4f0 100644
--- a/source/blender/editors/transform/transform_mode_edge_crease.c
+++ b/source/blender/editors/transform/transform_mode_edge_crease.c
@@ -37,9 +37,11 @@
#include "BLT_translation.h"
#include "transform.h"
-#include "transform_mode.h"
+#include "transform_convert.h"
#include "transform_snap.h"
+#include "transform_mode.h"
+
/* -------------------------------------------------------------------- */
/** \name Transform (Crease) Element
* \{ */
diff --git a/source/blender/editors/transform/transform_mode_edge_rotate_normal.c b/source/blender/editors/transform/transform_mode_edge_rotate_normal.c
index 6f2bcc148ce..1f57bacf78f 100644
--- a/source/blender/editors/transform/transform_mode_edge_rotate_normal.c
+++ b/source/blender/editors/transform/transform_mode_edge_rotate_normal.c
@@ -35,9 +35,10 @@
#include "UI_interface.h"
#include "transform.h"
-#include "transform_mode.h"
+#include "transform_convert.h"
#include "transform_snap.h"
+#include "transform_mode.h"
/* -------------------------------------------------------------------- */
/** \name Transform (Normal Rotation)
* \{ */
diff --git a/source/blender/editors/transform/transform_mode_edge_slide.c b/source/blender/editors/transform/transform_mode_edge_slide.c
index 066a2853dc7..cfcb17b8da0 100644
--- a/source/blender/editors/transform/transform_mode_edge_slide.c
+++ b/source/blender/editors/transform/transform_mode_edge_slide.c
@@ -852,7 +852,7 @@ static EdgeSlideData *createEdgeSlideVerts_double_side(TransInfo *t, TransDataCo
#undef EDGESLIDE_VERT_IS_INNER
}
- /* EDBM_flag_disable_all(em, BM_ELEM_SELECT); */
+ // EDBM_flag_disable_all(em, BM_ELEM_SELECT);
BLI_assert(STACK_SIZE(sv_array) == (uint)sv_tot);
@@ -1037,7 +1037,7 @@ static EdgeSlideData *createEdgeSlideVerts_single_side(TransInfo *t, TransDataCo
}
}
- /* EDBM_flag_disable_all(em, BM_ELEM_SELECT); */
+ // EDBM_flag_disable_all(em, BM_ELEM_SELECT);
sld->sv = sv_array;
sld->totsv = sv_tot;
diff --git a/source/blender/editors/transform/transform_mode_gpopacity.c b/source/blender/editors/transform/transform_mode_gpopacity.c
index 7c496d271ef..748769491f1 100644
--- a/source/blender/editors/transform/transform_mode_gpopacity.c
+++ b/source/blender/editors/transform/transform_mode_gpopacity.c
@@ -38,9 +38,11 @@
#include "BLT_translation.h"
#include "transform.h"
-#include "transform_mode.h"
+#include "transform_convert.h"
#include "transform_snap.h"
+#include "transform_mode.h"
+
/* -------------------------------------------------------------------- */
/** \name Transform (GPencil Strokes Opacity)
* \{ */
diff --git a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c
index 608a49f38b1..bc081edd597 100644
--- a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c
+++ b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c
@@ -38,9 +38,11 @@
#include "BLT_translation.h"
#include "transform.h"
-#include "transform_mode.h"
+#include "transform_convert.h"
#include "transform_snap.h"
+#include "transform_mode.h"
+
/* -------------------------------------------------------------------- */
/** \name Transform (GPencil Strokes Shrink/Fatten)
* \{ */
diff --git a/source/blender/editors/transform/transform_mode_maskshrinkfatten.c b/source/blender/editors/transform/transform_mode_maskshrinkfatten.c
index cfbd6030788..327a639773c 100644
--- a/source/blender/editors/transform/transform_mode_maskshrinkfatten.c
+++ b/source/blender/editors/transform/transform_mode_maskshrinkfatten.c
@@ -36,9 +36,11 @@
#include "BLT_translation.h"
#include "transform.h"
-#include "transform_mode.h"
+#include "transform_convert.h"
#include "transform_snap.h"
+#include "transform_mode.h"
+
/* -------------------------------------------------------------------- */
/** \name Transform (Mask Shrink/Fatten)
* \{ */
diff --git a/source/blender/editors/transform/transform_mode_mirror.c b/source/blender/editors/transform/transform_mode_mirror.c
index f225f1a7c06..2ae32f3545a 100644
--- a/source/blender/editors/transform/transform_mode_mirror.c
+++ b/source/blender/editors/transform/transform_mode_mirror.c
@@ -37,6 +37,8 @@
#include "BLT_translation.h"
#include "transform.h"
+#include "transform_convert.h"
+
#include "transform_mode.h"
/* -------------------------------------------------------------------- */
diff --git a/source/blender/editors/transform/transform_mode_push_pull.c b/source/blender/editors/transform/transform_mode_push_pull.c
index 0492ec8df8c..0527d1bc08e 100644
--- a/source/blender/editors/transform/transform_mode_push_pull.c
+++ b/source/blender/editors/transform/transform_mode_push_pull.c
@@ -38,9 +38,11 @@
#include "transform.h"
#include "transform_constraints.h"
-#include "transform_mode.h"
+#include "transform_convert.h"
#include "transform_snap.h"
+#include "transform_mode.h"
+
/* -------------------------------------------------------------------- */
/** \name Transform (Push/Pull) Element
* \{ */
diff --git a/source/blender/editors/transform/transform_mode_rotate.c b/source/blender/editors/transform/transform_mode_rotate.c
index 44a29cfac45..bfbdaa389f4 100644
--- a/source/blender/editors/transform/transform_mode_rotate.c
+++ b/source/blender/editors/transform/transform_mode_rotate.c
@@ -34,9 +34,11 @@
#include "UI_interface.h"
#include "transform.h"
-#include "transform_mode.h"
+#include "transform_convert.h"
#include "transform_snap.h"
+#include "transform_mode.h"
+
/* -------------------------------------------------------------------- */
/** \name Transform (Rotation) Matrix Cache
* \{ */
diff --git a/source/blender/editors/transform/transform_mode_shear.c b/source/blender/editors/transform/transform_mode_shear.c
index f5672887905..018725ec6dd 100644
--- a/source/blender/editors/transform/transform_mode_shear.c
+++ b/source/blender/editors/transform/transform_mode_shear.c
@@ -41,9 +41,11 @@
#include "BLT_translation.h"
#include "transform.h"
-#include "transform_mode.h"
+#include "transform_convert.h"
#include "transform_snap.h"
+#include "transform_mode.h"
+
/* -------------------------------------------------------------------- */
/** \name Transform (Shear) Element
* \{ */
diff --git a/source/blender/editors/transform/transform_mode_shrink_fatten.c b/source/blender/editors/transform/transform_mode_shrink_fatten.c
index 4cdaab599b4..b96b8103392 100644
--- a/source/blender/editors/transform/transform_mode_shrink_fatten.c
+++ b/source/blender/editors/transform/transform_mode_shrink_fatten.c
@@ -40,9 +40,11 @@
#include "BLT_translation.h"
#include "transform.h"
-#include "transform_mode.h"
+#include "transform_convert.h"
#include "transform_snap.h"
+#include "transform_mode.h"
+
/* -------------------------------------------------------------------- */
/** \name Transform (Shrink-Fatten) Element
* \{ */
diff --git a/source/blender/editors/transform/transform_mode_skin_resize.c b/source/blender/editors/transform/transform_mode_skin_resize.c
index 0a7eea8a989..236c9024201 100644
--- a/source/blender/editors/transform/transform_mode_skin_resize.c
+++ b/source/blender/editors/transform/transform_mode_skin_resize.c
@@ -35,9 +35,11 @@
#include "transform.h"
#include "transform_constraints.h"
-#include "transform_mode.h"
+#include "transform_convert.h"
#include "transform_snap.h"
+#include "transform_mode.h"
+
/* -------------------------------------------------------------------- */
/** \name Transform (Skin) Element
* \{ */
diff --git a/source/blender/editors/transform/transform_mode_tilt.c b/source/blender/editors/transform/transform_mode_tilt.c
index d3b72fdf503..b48f474e16e 100644
--- a/source/blender/editors/transform/transform_mode_tilt.c
+++ b/source/blender/editors/transform/transform_mode_tilt.c
@@ -36,9 +36,11 @@
#include "BLT_translation.h"
#include "transform.h"
-#include "transform_mode.h"
+#include "transform_convert.h"
#include "transform_snap.h"
+#include "transform_mode.h"
+
/* -------------------------------------------------------------------- */
/** \name Transform (Tilt)
* \{ */
diff --git a/source/blender/editors/transform/transform_mode_timescale.c b/source/blender/editors/transform/transform_mode_timescale.c
index 7ae97c66660..50fd714727b 100644
--- a/source/blender/editors/transform/transform_mode_timescale.c
+++ b/source/blender/editors/transform/transform_mode_timescale.c
@@ -39,6 +39,9 @@
#include "BLT_translation.h"
#include "transform.h"
+#include "transform_convert.h"
+#include "transform_snap.h"
+
#include "transform_mode.h"
/* -------------------------------------------------------------------- */
@@ -62,7 +65,6 @@ static void headerTimeScale(TransInfo *t, char str[UI_MAX_DRAW_STR])
static void applyTimeScaleValue(TransInfo *t, float value)
{
Scene *scene = t->scene;
- const short autosnap = getAnimEdit_SnapMode(t);
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
TransData *td = tc->data;
@@ -86,9 +88,6 @@ static void applyTimeScaleValue(TransInfo *t, float value)
/* now, calculate the new value */
*(td->val) = ((td->ival - startx) * fac) + startx;
-
- /* apply nearest snapping */
- doAnimEdit_SnapFrame(t, td, td2d, adt, autosnap);
}
}
}
diff --git a/source/blender/editors/transform/transform_mode_timeslide.c b/source/blender/editors/transform/transform_mode_timeslide.c
index 34d3251f9cf..5cc53eb08ce 100644
--- a/source/blender/editors/transform/transform_mode_timeslide.c
+++ b/source/blender/editors/transform/transform_mode_timeslide.c
@@ -42,6 +42,8 @@
#include "BLT_translation.h"
#include "transform.h"
+#include "transform_convert.h"
+
#include "transform_mode.h"
/* -------------------------------------------------------------------- */
diff --git a/source/blender/editors/transform/transform_mode_timetranslate.c b/source/blender/editors/transform/transform_mode_timetranslate.c
index 948242e547f..294040946bd 100644
--- a/source/blender/editors/transform/transform_mode_timetranslate.c
+++ b/source/blender/editors/transform/transform_mode_timetranslate.c
@@ -39,9 +39,11 @@
#include "BLT_translation.h"
#include "transform.h"
-#include "transform_mode.h"
+#include "transform_convert.h"
#include "transform_snap.h"
+#include "transform_mode.h"
+
/* -------------------------------------------------------------------- */
/** \name Transform (Animation Translation)
* \{ */
@@ -57,10 +59,18 @@ static void headerTimeTranslate(TransInfo *t, char str[UI_MAX_DRAW_STR])
}
else {
const short autosnap = getAnimEdit_SnapMode(t);
- float val = t->values_final[0];
+ float ival = TRANS_DATA_CONTAINER_FIRST_OK(t)->data->ival;
+ float val = ival + t->values_final[0];
- float snap_val;
- snapFrameTransform(t, autosnap, false, val, &snap_val);
+ float snap_val = val;
+ snapFrameTransform(t, autosnap, ival, val, &snap_val);
+
+ if (ELEM(autosnap, SACTSNAP_SECOND, SACTSNAP_TSTEP)) {
+ /* Convert to seconds. */
+ const Scene *scene = t->scene;
+ const double secf = FPS;
+ snap_val /= secf;
+ }
if (autosnap == SACTSNAP_FRAME) {
BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.2f (%.4f)", snap_val, val);
@@ -86,24 +96,11 @@ static void headerTimeTranslate(TransInfo *t, char str[UI_MAX_DRAW_STR])
static void applyTimeTranslateValue(TransInfo *t, const float deltax)
{
- const short autosnap = getAnimEdit_SnapMode(t);
-
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ /* It doesn't matter whether we apply to t->data. */
TransData *td = tc->data;
- TransData2D *td2d = tc->data_2d;
- /* It doesn't matter whether we apply to t->data or
- * t->data2d, but t->data2d is more convenient. */
- for (int i = 0; i < tc->data_len; i++, td++, td2d++) {
- /* It is assumed that td->extra is a pointer to the AnimData,
- * whose active action is where this keyframe comes from.
- * (this is only valid when not in NLA)
- * (also: masks and gpencil don't have animadata)
- */
- AnimData *adt = (t->spacetype != SPACE_NLA) ? td->extra : NULL;
-
- /* apply nearest snapping */
- *(td->val) = td->ival + deltax * td->factor;
- doAnimEdit_SnapFrame(t, td, td2d, adt, autosnap);
+ for (int i = 0; i < tc->data_len; i++, td++) {
+ *(td->val) = td->loc[0] = td->ival + deltax * td->factor;
}
}
}
diff --git a/source/blender/editors/transform/transform_mode_tosphere.c b/source/blender/editors/transform/transform_mode_tosphere.c
index 8587d5ae140..bfc85b2fe44 100644
--- a/source/blender/editors/transform/transform_mode_tosphere.c
+++ b/source/blender/editors/transform/transform_mode_tosphere.c
@@ -39,9 +39,11 @@
#include "BLT_translation.h"
#include "transform.h"
-#include "transform_mode.h"
+#include "transform_convert.h"
#include "transform_snap.h"
+#include "transform_mode.h"
+
/* -------------------------------------------------------------------- */
/** \name To Sphere Utilities
* \{ */
diff --git a/source/blender/editors/transform/transform_mode_trackball.c b/source/blender/editors/transform/transform_mode_trackball.c
index 68177c6becf..aa8b0783d0a 100644
--- a/source/blender/editors/transform/transform_mode_trackball.c
+++ b/source/blender/editors/transform/transform_mode_trackball.c
@@ -37,9 +37,11 @@
#include "BLT_translation.h"
#include "transform.h"
-#include "transform_mode.h"
+#include "transform_convert.h"
#include "transform_snap.h"
+#include "transform_mode.h"
+
/* -------------------------------------------------------------------- */
/** \name Transform (Rotation - Trackball) Element
* \{ */
diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c
index 75744f26c15..e44e346d3e4 100644
--- a/source/blender/editors/transform/transform_mode_translate.c
+++ b/source/blender/editors/transform/transform_mode_translate.c
@@ -221,22 +221,30 @@ static void headerTranslation(TransInfo *t, const float vec[3], char str[UI_MAX_
}
else {
float dvec[3];
+ copy_v3_v3(dvec, vec);
+ if (t->spacetype == SPACE_GRAPH) {
+ /* WORKAROUND:
+ * Special case where snapping is done in #recalData.
+ * Update the header based on the first element. */
+ const short autosnap = getAnimEdit_SnapMode(t);
+ float ival = TRANS_DATA_CONTAINER_FIRST_OK(t)->data->ival;
+ float val = ival + dvec[0];
+ snapFrameTransform(t, autosnap, ival, val, &dvec[0]);
+ }
+
if (t->con.mode & CON_APPLY) {
int i = 0;
zero_v3(dvec);
if (t->con.mode & CON_AXIS0) {
- dvec[i++] = vec[0];
+ dvec[i++] = dvec[0];
}
if (t->con.mode & CON_AXIS1) {
- dvec[i++] = vec[1];
+ dvec[i++] = dvec[1];
}
if (t->con.mode & CON_AXIS2) {
- dvec[i++] = vec[2];
+ dvec[i++] = dvec[2];
}
}
- else {
- copy_v3_v3(dvec, vec);
- }
if (t->flag & T_2D_EDIT) {
applyAspectRatio(t, dvec);
diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c
index 2c424d8ace3..45c077b8a07 100644
--- a/source/blender/editors/transform/transform_ops.c
+++ b/source/blender/editors/transform/transform_ops.c
@@ -371,25 +371,24 @@ static void transformops_exit(bContext *C, wmOperator *op)
G.moving = 0;
}
+static int transformops_mode(wmOperator *op)
+{
+ for (TransformModeItem *tmode = transform_modes; tmode->idname; tmode++) {
+ if (op->type->idname == tmode->idname) {
+ return tmode->mode;
+ }
+ }
+
+ return RNA_enum_get(op->ptr, "mode");
+}
+
static int transformops_data(bContext *C, wmOperator *op, const wmEvent *event)
{
int retval = 1;
if (op->customdata == NULL) {
TransInfo *t = MEM_callocN(sizeof(TransInfo), "TransInfo data2");
- TransformModeItem *tmode;
- int mode = -1;
-
- for (tmode = transform_modes; tmode->idname; tmode++) {
- if (op->type->idname == tmode->idname) {
- mode = tmode->mode;
- break;
- }
- }
-
- if (mode == -1) {
- mode = RNA_enum_get(op->ptr, "mode");
- }
+ int mode = transformops_mode(op);
retval = initTransform(C, t, op, event, mode);
/* store data */
@@ -556,6 +555,16 @@ static bool transform_poll_property(const bContext *UNUSED(C),
}
}
+ /* Orientation Axis. */
+ {
+ if (STREQ(prop_id, "orient_axis")) {
+ eTfmMode mode = (eTfmMode)transformops_mode(op);
+ if (mode == TFM_ALIGN) {
+ return false;
+ }
+ }
+ }
+
/* Proportional Editing. */
{
PropertyRNA *prop_pet = RNA_struct_find_property(op->ptr, "use_proportional_edit");
diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c
index 2619fdf3403..05a20a14477 100644
--- a/source/blender/editors/transform/transform_snap.c
+++ b/source/blender/editors/transform/transform_snap.c
@@ -575,7 +575,7 @@ static void initSnappingMode(TransInfo *t)
{
ToolSettings *ts = t->settings;
/* All obedit types will match. */
- const int obedit_type = t->data_container->obedit ? t->data_container->obedit->type : -1;
+ const int obedit_type = t->obedit_type;
ViewLayer *view_layer = t->view_layer;
Base *base_act = view_layer->basact;
@@ -594,7 +594,7 @@ static void initSnappingMode(TransInfo *t)
else if (t->spacetype == SPACE_SEQ) {
t->tsnap.mode = SEQ_tool_settings_snap_mode_get(t->scene);
}
- else {
+ else if (ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE) && !(t->options & CTX_CAMERA)) {
/* force project off when not supported */
if ((ts->snap_mode & SCE_SNAP_MODE_FACE) == 0) {
t->tsnap.project = 0;
@@ -608,16 +608,31 @@ static void initSnappingMode(TransInfo *t)
t->tsnap.mode |= SCE_SNAP_MODE_GRID;
}
}
+ else if (ELEM(t->spacetype, SPACE_GRAPH, SPACE_ACTION, SPACE_NLA)) {
+ /* No incremental snapping. */
+ t->tsnap.mode = 0;
+ }
+ else {
+ /* Fallback. */
+ t->tsnap.mode = SCE_SNAP_MODE_INCREMENT;
+ }
- if ((t->spacetype == SPACE_VIEW3D || t->spacetype == SPACE_IMAGE) &&
- (t->options & CTX_CAMERA) == 0) {
+ if (ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE) && !(t->options & CTX_CAMERA)) {
/* Only 3D view or UV. */
/* Not with camera selected in camera view. */
setSnappingCallback(t);
- if ((obedit_type != -1) &&
- ELEM(obedit_type, OB_MESH, OB_ARMATURE, OB_CURVE, OB_LATTICE, OB_MBALL)) {
+ if (t->options & (CTX_GPENCIL_STROKES | CTX_CURSOR | CTX_OBMODE_XFORM_OBDATA)) {
+ /* In "Edit Strokes" mode,
+ * snap tool can perform snap to selected or active objects (see T49632)
+ * TODO: perform self snap in gpencil_strokes.
+ *
+ * When we're moving the origins, allow snapping onto our own geometry (see T69132). */
+ t->tsnap.modeSelect = SNAP_ALL;
+ }
+ else if ((obedit_type != -1) &&
+ ELEM(obedit_type, OB_MESH, OB_ARMATURE, OB_CURVE, OB_LATTICE, OB_MBALL)) {
/* Edit mode */
/* Temporary limited to edit mode meshes, armature, curves, metaballs. */
@@ -636,17 +651,7 @@ static void initSnappingMode(TransInfo *t)
}
else if (obedit_type == -1) {
/* Object mode */
- if (t->options & (CTX_GPENCIL_STROKES | CTX_CURSOR | CTX_OBMODE_XFORM_OBDATA)) {
- /* In "Edit Strokes" mode,
- * snap tool can perform snap to selected or active objects (see T49632)
- * TODO: perform self snap in gpencil_strokes.
- *
- * When we're moving the origins, allow snapping onto our own geometry (see T69132). */
- t->tsnap.modeSelect = SNAP_ALL;
- }
- else {
- t->tsnap.modeSelect = SNAP_NOT_SELECTED;
- }
+ t->tsnap.modeSelect = SNAP_NOT_SELECTED;
}
else {
/* Increment if snap is not possible */
@@ -657,10 +662,6 @@ static void initSnappingMode(TransInfo *t)
setSnappingCallback(t);
t->tsnap.modeSelect = SNAP_NOT_SELECTED;
}
- else {
- /* Fallback. */
- t->tsnap.mode = SCE_SNAP_MODE_INCREMENT;
- }
if (t->spacetype == SPACE_VIEW3D) {
if (t->tsnap.object_context == NULL) {
@@ -1464,47 +1465,9 @@ bool snapNodesTransform(
/** \} */
/* -------------------------------------------------------------------- */
-/** \name snap Frames
+/** \name snap Grid
* \{ */
-/* This function is used by Animation Editor specific transform functions to do
- * the Snap Keyframe to Nearest Frame/Marker
- */
-void snapFrameTransform(TransInfo *t,
- const eAnimEdit_AutoSnap autosnap,
- const bool is_frame_value,
- const float delta,
- float *r_val)
-{
- double val = delta;
- switch (autosnap) {
- case SACTSNAP_STEP:
- case SACTSNAP_FRAME:
- val = floor(val + 0.5);
- break;
- case SACTSNAP_MARKER:
- /* snap to nearest marker */
- /* TODO: need some more careful checks for where data comes from. */
- val = ED_markers_find_nearest_marker_time(&t->scene->markers, (float)val);
- break;
- case SACTSNAP_SECOND:
- case SACTSNAP_TSTEP: {
- /* second step */
- const Scene *scene = t->scene;
- const double secf = FPS;
- val = floor((val / secf) + 0.5);
- if (is_frame_value) {
- val *= secf;
- }
- break;
- }
- case SACTSNAP_OFF: {
- break;
- }
- }
- *r_val = (float)val;
-}
-
static void snap_grid_apply(
TransInfo *t, const int max_index, const float grid_dist, const float loc[3], float r_out[3])
{
diff --git a/source/blender/editors/transform/transform_snap.h b/source/blender/editors/transform/transform_snap.h
index 6dfaeab93e6..ed7f93304bc 100644
--- a/source/blender/editors/transform/transform_snap.h
+++ b/source/blender/editors/transform/transform_snap.h
@@ -45,12 +45,6 @@ bool snapNodesTransform(struct TransInfo *t,
float r_loc[2],
float *r_dist_px,
char *r_node_border);
-void snapFrameTransform(struct TransInfo *t,
- const eAnimEdit_AutoSnap autosnap,
- const bool is_frame_value,
- const float delta,
- /* return args */
- float *r_val);
bool transformModeUseSnap(const TransInfo *t);
@@ -86,3 +80,15 @@ struct TransSeqSnapData *transform_snap_sequencer_data_alloc(const TransInfo *t)
void transform_snap_sequencer_data_free(struct TransSeqSnapData *data);
bool transform_snap_sequencer_calc(struct TransInfo *t);
void transform_snap_sequencer_apply_translate(TransInfo *t, float *vec);
+
+/* transform_snap_animation.c */
+short getAnimEdit_SnapMode(TransInfo *t);
+void snapFrameTransform(TransInfo *t,
+ const eAnimEdit_AutoSnap autosnap,
+ const float val_initial,
+ const float val_final,
+ float *r_val_final);
+void transform_snap_anim_flush_data(TransInfo *t,
+ TransData *td,
+ const eAnimEdit_AutoSnap autosnap,
+ float *r_val_final);
diff --git a/source/blender/editors/transform/transform_snap_animation.c b/source/blender/editors/transform/transform_snap_animation.c
new file mode 100644
index 00000000000..08335924ddf
--- /dev/null
+++ b/source/blender/editors/transform/transform_snap_animation.c
@@ -0,0 +1,159 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup edtransform
+ */
+
+#include "DNA_anim_types.h"
+
+#include "BLI_math.h"
+
+#include "BKE_context.h"
+#include "BKE_nla.h"
+
+#include "ED_markers.h"
+#include "ED_screen.h"
+
+#include "transform.h"
+#include "transform_snap.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Snapping in Anim Editors
+ * \{ */
+
+/**
+ * This function returns the snapping 'mode' for Animation Editors only.
+ * We cannot use the standard snapping due to NLA-strip scaling complexities.
+ *
+ * TODO: these modifier checks should be accessible from the key-map.
+ */
+short getAnimEdit_SnapMode(TransInfo *t)
+{
+ short autosnap = SACTSNAP_OFF;
+
+ if (t->spacetype == SPACE_ACTION) {
+ SpaceAction *saction = (SpaceAction *)t->area->spacedata.first;
+
+ if (saction) {
+ autosnap = saction->autosnap;
+ }
+ }
+ else if (t->spacetype == SPACE_GRAPH) {
+ SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first;
+
+ if (sipo) {
+ autosnap = sipo->autosnap;
+ }
+ }
+ else if (t->spacetype == SPACE_NLA) {
+ SpaceNla *snla = (SpaceNla *)t->area->spacedata.first;
+
+ if (snla) {
+ autosnap = snla->autosnap;
+ }
+ }
+ else {
+ autosnap = SACTSNAP_OFF;
+ }
+
+ /* toggle autosnap on/off
+ * - when toggling on, prefer nearest frame over 1.0 frame increments
+ */
+ if (t->modifiers & MOD_SNAP_INVERT) {
+ if (autosnap) {
+ autosnap = SACTSNAP_OFF;
+ }
+ else {
+ autosnap = SACTSNAP_FRAME;
+ }
+ }
+
+ return autosnap;
+}
+
+void snapFrameTransform(TransInfo *t,
+ const eAnimEdit_AutoSnap autosnap,
+ const float val_initial,
+ const float val_final,
+ float *r_val_final)
+{
+ float deltax = val_final - val_initial;
+ switch (autosnap) {
+ case SACTSNAP_FRAME:
+ *r_val_final = floorf(val_final + 0.5f);
+ break;
+ case SACTSNAP_MARKER:
+ /* Snap to nearest marker. */
+ /* TODO: need some more careful checks for where data comes from. */
+ *r_val_final = (float)ED_markers_find_nearest_marker_time(&t->scene->markers, val_final);
+ break;
+ case SACTSNAP_SECOND:
+ case SACTSNAP_TSTEP: {
+ const Scene *scene = t->scene;
+ const double secf = FPS;
+ if (autosnap == SACTSNAP_SECOND) {
+ *r_val_final = floorf((val_final / secf) + 0.5) * secf;
+ }
+ else {
+ deltax = (float)(floor((deltax / secf) + 0.5) * secf);
+ *r_val_final = val_initial + deltax;
+ }
+ break;
+ }
+ case SACTSNAP_STEP:
+ deltax = floorf(deltax + 0.5f);
+ *r_val_final = val_initial + deltax;
+ break;
+ case SACTSNAP_OFF:
+ break;
+ }
+}
+
+/* This function is used by Animation Editor specific transform functions to do
+ * the Snap Keyframe to Nearest Frame/Marker
+ */
+void transform_snap_anim_flush_data(TransInfo *t,
+ TransData *td,
+ const eAnimEdit_AutoSnap autosnap,
+ float *r_val_final)
+{
+ BLI_assert(autosnap != SACTSNAP_OFF);
+
+ float val = td->loc[0];
+ float ival = td->iloc[0];
+ AnimData *adt = (!ELEM(t->spacetype, SPACE_NLA, SPACE_SEQ)) ? td->extra : NULL;
+
+ /* Convert frame to nla-action time (if needed) */
+ if (adt) {
+ val = BKE_nla_tweakedit_remap(adt, val, NLATIME_CONVERT_MAP);
+ ival = BKE_nla_tweakedit_remap(adt, ival, NLATIME_CONVERT_MAP);
+ }
+
+ snapFrameTransform(t, autosnap, ival, val, &val);
+
+ /* Convert frame out of nla-action time. */
+ if (adt) {
+ val = BKE_nla_tweakedit_remap(adt, val, NLATIME_CONVERT_UNMAP);
+ }
+
+ *r_val_final = val;
+}
+
+/** \} */
diff --git a/source/blender/editors/transform/transform_snap_sequencer.c b/source/blender/editors/transform/transform_snap_sequencer.c
index a1f396eb503..6e926f36fba 100644
--- a/source/blender/editors/transform/transform_snap_sequencer.c
+++ b/source/blender/editors/transform/transform_snap_sequencer.c
@@ -141,7 +141,7 @@ static SeqCollection *query_snap_targets(const TransInfo *t, SeqCollection *snap
const short snap_flag = SEQ_tool_settings_snap_flag_get(t->scene);
SeqCollection *snap_targets = SEQ_collection_create(__func__);
LISTBASE_FOREACH (Sequence *, seq, seqbase) {
- if ((seq->flag & SELECT)) {
+ if (seq->flag & SELECT) {
continue; /* Selected are being transformed. */
}
if ((seq->flag & SEQ_MUTE) && (snap_flag & SEQ_SNAP_IGNORE_MUTED)) {
diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt
index 54ec6b22e70..b396e348845 100644
--- a/source/blender/editors/util/CMakeLists.txt
+++ b/source/blender/editors/util/CMakeLists.txt
@@ -64,6 +64,7 @@ set(SRC
../include/ED_info.h
../include/ED_keyframes_draw.h
../include/ED_keyframes_edit.h
+ ../include/ED_keyframes_keylist.h
../include/ED_keyframing.h
../include/ED_lattice.h
../include/ED_markers.h
diff --git a/source/blender/editors/util/numinput.c b/source/blender/editors/util/numinput.c
index 15d672dea56..823837e2a42 100644
--- a/source/blender/editors/util/numinput.c
+++ b/source/blender/editors/util/numinput.c
@@ -309,7 +309,7 @@ bool user_string_to_number(bContext *C,
return success;
#else
- UNUSED_VARS(C, unit, type);
+ UNUSED_VARS(C, unit, type, use_single_line_error, r_error);
*r_value = atof(str);
return true;
#endif
diff --git a/source/blender/editors/uvedit/uvedit_parametrizer.c b/source/blender/editors/uvedit/uvedit_parametrizer.c
index f97403a0919..b4bdeace716 100644
--- a/source/blender/editors/uvedit/uvedit_parametrizer.c
+++ b/source/blender/editors/uvedit/uvedit_parametrizer.c
@@ -3399,10 +3399,7 @@ static void p_chart_lscm_end(PChart *chart)
EIG_linear_solver_delete(chart->u.lscm.context);
}
- if (chart->u.lscm.abf_alpha) {
- MEM_freeN(chart->u.lscm.abf_alpha);
- chart->u.lscm.abf_alpha = NULL;
- }
+ MEM_SAFE_FREE(chart->u.lscm.abf_alpha);
chart->u.lscm.context = NULL;
chart->u.lscm.pin1 = NULL;
diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c
index 20aadb84b7b..5a82cd31112 100644
--- a/source/blender/editors/uvedit/uvedit_select.c
+++ b/source/blender/editors/uvedit/uvedit_select.c
@@ -2140,11 +2140,12 @@ void UV_OT_select(wmOperatorType *ot)
/* properties */
PropertyRNA *prop;
- RNA_def_boolean(ot->srna,
- "extend",
- 0,
- "Extend",
- "Extend selection rather than clearing the existing selection");
+ prop = RNA_def_boolean(ot->srna,
+ "extend",
+ 0,
+ "Extend",
+ "Extend selection rather than clearing the existing selection");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
prop = RNA_def_boolean(ot->srna,
"deselect_all",
false,
@@ -2152,7 +2153,7 @@ void UV_OT_select(wmOperatorType *ot)
"Deselect all when nothing under the cursor");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- RNA_def_float_vector(
+ prop = RNA_def_float_vector(
ot->srna,
"location",
2,
@@ -2163,6 +2164,7 @@ void UV_OT_select(wmOperatorType *ot)
"Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds",
-100.0f,
100.0f);
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
/** \} */
@@ -2296,12 +2298,14 @@ void UV_OT_select_loop(wmOperatorType *ot)
ot->poll = ED_operator_uvedit; /* requires space image */
/* properties */
- RNA_def_boolean(ot->srna,
- "extend",
- 0,
- "Extend",
- "Extend selection rather than clearing the existing selection");
- RNA_def_float_vector(
+ PropertyRNA *prop;
+ prop = RNA_def_boolean(ot->srna,
+ "extend",
+ 0,
+ "Extend",
+ "Extend selection rather than clearing the existing selection");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_float_vector(
ot->srna,
"location",
2,
@@ -2312,6 +2316,7 @@ void UV_OT_select_loop(wmOperatorType *ot)
"Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds",
-100.0f,
100.0f);
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
/** \} */
@@ -2494,17 +2499,20 @@ void UV_OT_select_linked_pick(wmOperatorType *ot)
ot->poll = ED_operator_uvedit; /* requires space image */
/* properties */
- RNA_def_boolean(ot->srna,
- "extend",
- 0,
- "Extend",
- "Extend selection rather than clearing the existing selection");
- RNA_def_boolean(ot->srna,
- "deselect",
- 0,
- "Deselect",
- "Deselect linked UV vertices rather than selecting them");
- RNA_def_float_vector(
+ PropertyRNA *prop;
+ prop = RNA_def_boolean(ot->srna,
+ "extend",
+ 0,
+ "Extend",
+ "Extend selection rather than clearing the existing selection");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(ot->srna,
+ "deselect",
+ 0,
+ "Deselect",
+ "Deselect linked UV vertices rather than selecting them");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_float_vector(
ot->srna,
"location",
2,
@@ -2515,6 +2523,7 @@ void UV_OT_select_linked_pick(wmOperatorType *ot)
"Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds",
-100.0f,
100.0f);
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
/** \} */
@@ -3903,7 +3912,7 @@ BMLoop **ED_uvedit_selected_verts(Scene *scene, BMesh *bm, int len_max, int *r_v
BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
if (!BM_elem_flag_test(l_iter, BM_ELEM_TAG)) {
const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
- if ((luv->flag & MLOOPUV_VERTSEL)) {
+ if (luv->flag & MLOOPUV_VERTSEL) {
BM_elem_flag_enable(l_iter->v, BM_ELEM_TAG);
verts[verts_len++] = l_iter;
diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c
index 535a0e00347..c5485cc1495 100644
--- a/source/blender/editors/uvedit/uvedit_smart_stitch.c
+++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c
@@ -265,26 +265,11 @@ static StitchPreviewer *stitch_preview_init(void)
static void stitch_preview_delete(StitchPreviewer *stitch_preview)
{
if (stitch_preview) {
- if (stitch_preview->preview_polys) {
- MEM_freeN(stitch_preview->preview_polys);
- stitch_preview->preview_polys = NULL;
- }
- if (stitch_preview->uvs_per_polygon) {
- MEM_freeN(stitch_preview->uvs_per_polygon);
- stitch_preview->uvs_per_polygon = NULL;
- }
- if (stitch_preview->preview_stitchable) {
- MEM_freeN(stitch_preview->preview_stitchable);
- stitch_preview->preview_stitchable = NULL;
- }
- if (stitch_preview->preview_unstitchable) {
- MEM_freeN(stitch_preview->preview_unstitchable);
- stitch_preview->preview_unstitchable = NULL;
- }
- if (stitch_preview->static_tris) {
- MEM_freeN(stitch_preview->static_tris);
- stitch_preview->static_tris = NULL;
- }
+ MEM_SAFE_FREE(stitch_preview->preview_polys);
+ MEM_SAFE_FREE(stitch_preview->uvs_per_polygon);
+ MEM_SAFE_FREE(stitch_preview->preview_stitchable);
+ MEM_SAFE_FREE(stitch_preview->preview_unstitchable);
+ MEM_SAFE_FREE(stitch_preview->static_tris);
MEM_freeN(stitch_preview);
}
}
@@ -1769,7 +1754,7 @@ static void stitch_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), void
GPU_blend(GPU_BLEND_ALPHA);
- /* Static Tris */
+ /* Static Triangles. */
if (stitch_preview->static_tris) {
UI_GetThemeColor4fv(TH_STITCH_PREVIEW_ACTIVE, col);
vbo = GPU_vertbuf_create_with_format(&format);
diff --git a/source/blender/freestyle/intern/python/BPy_Freestyle.cpp b/source/blender/freestyle/intern/python/BPy_Freestyle.cpp
index db85292c6c0..2d0021a1fe8 100644
--- a/source/blender/freestyle/intern/python/BPy_Freestyle.cpp
+++ b/source/blender/freestyle/intern/python/BPy_Freestyle.cpp
@@ -266,7 +266,7 @@ static PyObject *Freestyle_evaluateCurveMappingF(PyObject * /*self*/, PyObject *
cumap = (CurveMapping *)py_srna->ptr.data;
BKE_curvemapping_init(cumap);
/* disable extrapolation if enabled */
- if ((cumap->flag & CUMA_EXTEND_EXTRAPOLATE)) {
+ if (cumap->flag & CUMA_EXTEND_EXTRAPOLATE) {
cumap->flag &= ~CUMA_EXTEND_EXTRAPOLATE;
BKE_curvemapping_changed(cumap, false);
}
diff --git a/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp b/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp
index afb23690a84..b1aea8bf6cf 100644
--- a/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp
+++ b/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp
@@ -527,7 +527,7 @@ static void computeCumulativeVisibility(ViewMap *ioViewMap,
fe = fe->nextEdge();
continue;
}
- if ((maxCard < qiMajority)) {
+ if (maxCard < qiMajority) {
// ARB: change &wFace to wFace and use reference in called function
tmpQI = computeVisibility<G, I>(
ioViewMap, fe, grid, epsilon, *ve, &wFace, &foundOccluders);
@@ -725,7 +725,7 @@ static void computeDetailedVisibility(ViewMap *ioViewMap,
fe = fe->nextEdge();
continue;
}
- if ((maxCard < qiMajority)) {
+ if (maxCard < qiMajority) {
// ARB: change &wFace to wFace and use reference in called function
tmpQI = computeVisibility<G, I>(
ioViewMap, fe, grid, epsilon, *ve, &wFace, &foundOccluders);
@@ -891,7 +891,7 @@ static void computeFastVisibility(ViewMap *ioViewMap, G &grid, real epsilon)
continue;
}
if (even_test) {
- if ((maxCard < qiMajority)) {
+ if (maxCard < qiMajority) {
// ARB: change &wFace to wFace and use reference in called function
tmpQI = computeVisibility<G, I>(
ioViewMap, fe, grid, epsilon, *ve, &wFace, &foundOccluders);
@@ -1607,7 +1607,7 @@ void ViewMapBuilder::ComputeRayCastingVisibility(ViewMap *ioViewMap, real epsilo
memset(qiClasses, 0, 256 * sizeof(*qiClasses));
set<ViewShape *> occluders;
do {
- if ((maxCard < qiMajority)) {
+ if (maxCard < qiMajority) {
tmpQI = ComputeRayCastingVisibility(fe, _Grid, epsilon, occluders, &aFace, timestamp++);
#if LOGGING
@@ -1763,7 +1763,7 @@ void ViewMapBuilder::ComputeFastRayCastingVisibility(ViewMap *ioViewMap, real ep
fe = (*ve)->fedgeA();
do {
if (even_test) {
- if ((maxCard < qiMajority)) {
+ if (maxCard < qiMajority) {
tmpQI = ComputeRayCastingVisibility(fe, _Grid, epsilon, occluders, &aFace, timestamp++);
// ARB: This is an error condition, not an alert condition.
diff --git a/source/blender/functions/CMakeLists.txt b/source/blender/functions/CMakeLists.txt
index 809294ad274..f8d2acc74a8 100644
--- a/source/blender/functions/CMakeLists.txt
+++ b/source/blender/functions/CMakeLists.txt
@@ -33,9 +33,6 @@ set(SRC
intern/generic_virtual_vector_array.cc
intern/multi_function.cc
intern/multi_function_builder.cc
- intern/multi_function_network.cc
- intern/multi_function_network_evaluation.cc
- intern/multi_function_network_optimization.cc
FN_cpp_type.hh
FN_cpp_type_make.hh
@@ -49,9 +46,6 @@ set(SRC
FN_multi_function_builder.hh
FN_multi_function_context.hh
FN_multi_function_data_type.hh
- FN_multi_function_network.hh
- FN_multi_function_network_evaluation.hh
- FN_multi_function_network_optimization.hh
FN_multi_function_param_type.hh
FN_multi_function_params.hh
FN_multi_function_signature.hh
@@ -68,7 +62,6 @@ if(WITH_GTESTS)
tests/FN_cpp_type_test.cc
tests/FN_generic_span_test.cc
tests/FN_generic_vector_array_test.cc
- tests/FN_multi_function_network_test.cc
tests/FN_multi_function_test.cc
)
set(TEST_LIB
diff --git a/source/blender/functions/FN_cpp_type.hh b/source/blender/functions/FN_cpp_type.hh
index bc3f398c8e9..7277bf99c12 100644
--- a/source/blender/functions/FN_cpp_type.hh
+++ b/source/blender/functions/FN_cpp_type.hh
@@ -70,64 +70,73 @@
#include "BLI_string_ref.hh"
#include "BLI_utility_mixins.hh"
+/**
+ * Different types support different features. Features like copy constructability can be detected
+ * automatically easily. For some features this is harder as of C++17. Those have flags in this
+ * enum and need to be determined by the programmer.
+ */
+enum class CPPTypeFlags {
+ None = 0,
+ Hashable = 1 << 0,
+ Printable = 1 << 1,
+ EqualityComparable = 1 << 2,
+
+ BasicType = Hashable | Printable | EqualityComparable,
+};
+ENUM_OPERATORS(CPPTypeFlags, CPPTypeFlags::EqualityComparable)
+
namespace blender::fn {
-struct CPPTypeMembers {
- int64_t size = 0;
- int64_t alignment = 0;
- uintptr_t alignment_mask = 0;
- bool is_trivially_destructible = false;
- bool has_special_member_functions = false;
+/** Utility class to pass template parameters to constructor of `CPPType`. */
+template<typename T, CPPTypeFlags Flags> struct CPPTypeParam {
+};
- void (*default_construct)(void *ptr) = nullptr;
- void (*default_construct_indices)(void *ptr, IndexMask mask) = nullptr;
+class CPPType : NonCopyable, NonMovable {
+ private:
+ int64_t size_ = 0;
+ int64_t alignment_ = 0;
+ uintptr_t alignment_mask_ = 0;
+ bool is_trivially_destructible_ = false;
+ bool has_special_member_functions_ = false;
- void (*destruct)(void *ptr) = nullptr;
- void (*destruct_indices)(void *ptr, IndexMask mask) = nullptr;
+ void (*default_construct_)(void *ptr) = nullptr;
+ void (*default_construct_indices_)(void *ptr, IndexMask mask) = nullptr;
- void (*copy_assign)(const void *src, void *dst) = nullptr;
- void (*copy_assign_indices)(const void *src, void *dst, IndexMask mask) = nullptr;
+ void (*destruct_)(void *ptr) = nullptr;
+ void (*destruct_indices_)(void *ptr, IndexMask mask) = nullptr;
- void (*copy_construct)(const void *src, void *dst) = nullptr;
- void (*copy_construct_indices)(const void *src, void *dst, IndexMask mask) = nullptr;
+ void (*copy_assign_)(const void *src, void *dst) = nullptr;
+ void (*copy_assign_indices_)(const void *src, void *dst, IndexMask mask) = nullptr;
- void (*move_assign)(void *src, void *dst) = nullptr;
- void (*move_assign_indices)(void *src, void *dst, IndexMask mask) = nullptr;
+ void (*copy_construct_)(const void *src, void *dst) = nullptr;
+ void (*copy_construct_indices_)(const void *src, void *dst, IndexMask mask) = nullptr;
- void (*move_construct)(void *src, void *dst) = nullptr;
- void (*move_construct_indices)(void *src, void *dst, IndexMask mask) = nullptr;
+ void (*move_assign_)(void *src, void *dst) = nullptr;
+ void (*move_assign_indices_)(void *src, void *dst, IndexMask mask) = nullptr;
- void (*relocate_assign)(void *src, void *dst) = nullptr;
- void (*relocate_assign_indices)(void *src, void *dst, IndexMask mask) = nullptr;
+ void (*move_construct_)(void *src, void *dst) = nullptr;
+ void (*move_construct_indices_)(void *src, void *dst, IndexMask mask) = nullptr;
- void (*relocate_construct)(void *src, void *dst) = nullptr;
- void (*relocate_construct_indices)(void *src, void *dst, IndexMask mask) = nullptr;
+ void (*relocate_assign_)(void *src, void *dst) = nullptr;
+ void (*relocate_assign_indices_)(void *src, void *dst, IndexMask mask) = nullptr;
- void (*fill_assign_indices)(const void *value, void *dst, IndexMask mask) = nullptr;
+ void (*relocate_construct_)(void *src, void *dst) = nullptr;
+ void (*relocate_construct_indices_)(void *src, void *dst, IndexMask mask) = nullptr;
- void (*fill_construct_indices)(const void *value, void *dst, IndexMask mask) = nullptr;
+ void (*fill_assign_indices_)(const void *value, void *dst, IndexMask mask) = nullptr;
- void (*print)(const void *value, std::stringstream &ss) = nullptr;
- bool (*is_equal)(const void *a, const void *b) = nullptr;
- uint64_t (*hash)(const void *value) = nullptr;
+ void (*fill_construct_indices_)(const void *value, void *dst, IndexMask mask) = nullptr;
- const void *default_value = nullptr;
- std::string name;
-};
+ void (*print_)(const void *value, std::stringstream &ss) = nullptr;
+ bool (*is_equal_)(const void *a, const void *b) = nullptr;
+ uint64_t (*hash_)(const void *value) = nullptr;
-class CPPType : NonCopyable, NonMovable {
- private:
- CPPTypeMembers m_;
+ const void *default_value_ = nullptr;
+ std::string debug_name_;
public:
- CPPType(CPPTypeMembers members) : m_(std::move(members))
- {
- BLI_assert(is_power_of_2_i(m_.alignment));
- m_.alignment_mask = (uintptr_t)members.alignment - (uintptr_t)1;
- m_.has_special_member_functions = (m_.default_construct && m_.copy_construct &&
- m_.copy_assign && m_.move_construct && m_.move_assign &&
- m_.destruct);
- }
+ template<typename T, CPPTypeFlags Flags> CPPType(CPPTypeParam<T, Flags>, StringRef debug_name);
+ virtual ~CPPType() = default;
/**
* Two types only compare equal when their pointer is equal. No two instances of CPPType for the
@@ -148,7 +157,11 @@ class CPPType : NonCopyable, NonMovable {
* This only works for types that actually implement the template specialization using
* `MAKE_CPP_TYPE`.
*/
- template<typename T> static const CPPType &get();
+ template<typename T> static const CPPType &get()
+ {
+ return CPPType::get_impl<std::remove_cv_t<T>>();
+ }
+ template<typename T> static const CPPType &get_impl();
/**
* Returns the name of the type for debugging purposes. This name should not be used as
@@ -156,7 +169,7 @@ class CPPType : NonCopyable, NonMovable {
*/
StringRefNull name() const
{
- return m_.name;
+ return debug_name_;
}
/**
@@ -167,7 +180,7 @@ class CPPType : NonCopyable, NonMovable {
*/
int64_t size() const
{
- return m_.size;
+ return size_;
}
/**
@@ -178,7 +191,7 @@ class CPPType : NonCopyable, NonMovable {
*/
int64_t alignment() const
{
- return m_.alignment;
+ return alignment_;
}
/**
@@ -190,52 +203,52 @@ class CPPType : NonCopyable, NonMovable {
*/
bool is_trivially_destructible() const
{
- return m_.is_trivially_destructible;
+ return is_trivially_destructible_;
}
bool is_default_constructible() const
{
- return m_.default_construct != nullptr;
+ return default_construct_ != nullptr;
}
bool is_copy_constructible() const
{
- return m_.copy_assign != nullptr;
+ return copy_assign_ != nullptr;
}
bool is_move_constructible() const
{
- return m_.move_assign != nullptr;
+ return move_assign_ != nullptr;
}
bool is_destructible() const
{
- return m_.destruct != nullptr;
+ return destruct_ != nullptr;
}
bool is_copy_assignable() const
{
- return m_.copy_assign != nullptr;
+ return copy_assign_ != nullptr;
}
bool is_move_assignable() const
{
- return m_.copy_construct != nullptr;
+ return copy_construct_ != nullptr;
}
bool is_printable() const
{
- return m_.print != nullptr;
+ return print_ != nullptr;
}
bool is_equality_comparable() const
{
- return m_.is_equal != nullptr;
+ return is_equal_ != nullptr;
}
bool is_hashable() const
{
- return m_.hash != nullptr;
+ return hash_ != nullptr;
}
/**
@@ -249,7 +262,7 @@ class CPPType : NonCopyable, NonMovable {
*/
bool has_special_member_functions() const
{
- return m_.has_special_member_functions;
+ return has_special_member_functions_;
}
/**
@@ -257,7 +270,7 @@ class CPPType : NonCopyable, NonMovable {
*/
bool pointer_has_valid_alignment(const void *ptr) const
{
- return ((uintptr_t)ptr & m_.alignment_mask) == 0;
+ return ((uintptr_t)ptr & alignment_mask_) == 0;
}
bool pointer_can_point_to_instance(const void *ptr) const
@@ -277,7 +290,7 @@ class CPPType : NonCopyable, NonMovable {
{
BLI_assert(this->pointer_can_point_to_instance(ptr));
- m_.default_construct(ptr);
+ default_construct_(ptr);
}
void default_construct_n(void *ptr, int64_t n) const
@@ -289,7 +302,7 @@ class CPPType : NonCopyable, NonMovable {
{
BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(ptr));
- m_.default_construct_indices(ptr, mask);
+ default_construct_indices_(ptr, mask);
}
/**
@@ -304,7 +317,7 @@ class CPPType : NonCopyable, NonMovable {
{
BLI_assert(this->pointer_can_point_to_instance(ptr));
- m_.destruct(ptr);
+ destruct_(ptr);
}
void destruct_n(void *ptr, int64_t n) const
@@ -316,7 +329,7 @@ class CPPType : NonCopyable, NonMovable {
{
BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(ptr));
- m_.destruct_indices(ptr, mask);
+ destruct_indices_(ptr, mask);
}
/**
@@ -331,7 +344,7 @@ class CPPType : NonCopyable, NonMovable {
BLI_assert(this->pointer_can_point_to_instance(src));
BLI_assert(this->pointer_can_point_to_instance(dst));
- m_.copy_assign(src, dst);
+ copy_assign_(src, dst);
}
void copy_assign_n(const void *src, void *dst, int64_t n) const
@@ -345,7 +358,7 @@ class CPPType : NonCopyable, NonMovable {
BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src));
BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst));
- m_.copy_assign_indices(src, dst, mask);
+ copy_assign_indices_(src, dst, mask);
}
/**
@@ -362,7 +375,7 @@ class CPPType : NonCopyable, NonMovable {
BLI_assert(this->pointer_can_point_to_instance(src));
BLI_assert(this->pointer_can_point_to_instance(dst));
- m_.copy_construct(src, dst);
+ copy_construct_(src, dst);
}
void copy_construct_n(const void *src, void *dst, int64_t n) const
@@ -376,7 +389,7 @@ class CPPType : NonCopyable, NonMovable {
BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src));
BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst));
- m_.copy_construct_indices(src, dst, mask);
+ copy_construct_indices_(src, dst, mask);
}
/**
@@ -393,7 +406,7 @@ class CPPType : NonCopyable, NonMovable {
BLI_assert(this->pointer_can_point_to_instance(src));
BLI_assert(this->pointer_can_point_to_instance(dst));
- m_.move_assign(src, dst);
+ move_assign_(src, dst);
}
void move_assign_n(void *src, void *dst, int64_t n) const
@@ -407,7 +420,7 @@ class CPPType : NonCopyable, NonMovable {
BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src));
BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst));
- m_.move_assign_indices(src, dst, mask);
+ move_assign_indices_(src, dst, mask);
}
/**
@@ -424,7 +437,7 @@ class CPPType : NonCopyable, NonMovable {
BLI_assert(this->pointer_can_point_to_instance(src));
BLI_assert(this->pointer_can_point_to_instance(dst));
- m_.move_construct(src, dst);
+ move_construct_(src, dst);
}
void move_construct_n(void *src, void *dst, int64_t n) const
@@ -438,7 +451,7 @@ class CPPType : NonCopyable, NonMovable {
BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src));
BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst));
- m_.move_construct_indices(src, dst, mask);
+ move_construct_indices_(src, dst, mask);
}
/**
@@ -455,7 +468,7 @@ class CPPType : NonCopyable, NonMovable {
BLI_assert(this->pointer_can_point_to_instance(src));
BLI_assert(this->pointer_can_point_to_instance(dst));
- m_.relocate_assign(src, dst);
+ relocate_assign_(src, dst);
}
void relocate_assign_n(void *src, void *dst, int64_t n) const
@@ -469,7 +482,7 @@ class CPPType : NonCopyable, NonMovable {
BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src));
BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst));
- m_.relocate_assign_indices(src, dst, mask);
+ relocate_assign_indices_(src, dst, mask);
}
/**
@@ -486,7 +499,7 @@ class CPPType : NonCopyable, NonMovable {
BLI_assert(this->pointer_can_point_to_instance(src));
BLI_assert(this->pointer_can_point_to_instance(dst));
- m_.relocate_construct(src, dst);
+ relocate_construct_(src, dst);
}
void relocate_construct_n(void *src, void *dst, int64_t n) const
@@ -500,7 +513,7 @@ class CPPType : NonCopyable, NonMovable {
BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src));
BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst));
- m_.relocate_construct_indices(src, dst, mask);
+ relocate_construct_indices_(src, dst, mask);
}
/**
@@ -518,7 +531,7 @@ class CPPType : NonCopyable, NonMovable {
BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(value));
BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst));
- m_.fill_assign_indices(value, dst, mask);
+ fill_assign_indices_(value, dst, mask);
}
/**
@@ -536,13 +549,13 @@ class CPPType : NonCopyable, NonMovable {
BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(value));
BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst));
- m_.fill_construct_indices(value, dst, mask);
+ fill_construct_indices_(value, dst, mask);
}
void print(const void *value, std::stringstream &ss) const
{
BLI_assert(this->pointer_can_point_to_instance(value));
- m_.print(value, ss);
+ print_(value, ss);
}
std::string to_string(const void *value) const
@@ -566,7 +579,7 @@ class CPPType : NonCopyable, NonMovable {
{
BLI_assert(this->pointer_can_point_to_instance(a));
BLI_assert(this->pointer_can_point_to_instance(b));
- return m_.is_equal(a, b);
+ return is_equal_(a, b);
}
bool is_equal_or_false(const void *a, const void *b) const
@@ -580,7 +593,7 @@ class CPPType : NonCopyable, NonMovable {
uint64_t hash(const void *value) const
{
BLI_assert(this->pointer_can_point_to_instance(value));
- return m_.hash(value);
+ return hash_(value);
}
uint64_t hash_or_fallback(const void *value, uint64_t fallback_hash) const
@@ -597,7 +610,7 @@ class CPPType : NonCopyable, NonMovable {
*/
const void *default_value() const
{
- return m_.default_value;
+ return default_value_;
}
uint64_t hash() const
@@ -605,12 +618,9 @@ class CPPType : NonCopyable, NonMovable {
return get_default_hash(this);
}
- /**
- * Low level access to the callbacks for this CPPType.
- */
- const CPPTypeMembers &members() const
+ void (*destruct_fn() const)(void *)
{
- return m_;
+ return destruct_;
}
template<typename T> bool is() const
diff --git a/source/blender/functions/FN_cpp_type_make.hh b/source/blender/functions/FN_cpp_type_make.hh
index b8e5373ccf7..088f6b469f4 100644
--- a/source/blender/functions/FN_cpp_type_make.hh
+++ b/source/blender/functions/FN_cpp_type_make.hh
@@ -185,100 +185,80 @@ template<typename T> uint64_t hash_cb(const void *value)
} // namespace blender::fn::cpp_type_util
-/**
- * Different types support different features. Features like copy constructability can be detected
- * automatically easily. For some features this is harder as of C++17. Those have flags in this
- * enum and need to be determined by the programmer.
- */
-enum class CPPTypeFlags {
- None = 0,
- Hashable = 1 << 0,
- Printable = 1 << 1,
- EqualityComparable = 1 << 2,
-
- BasicType = Hashable | Printable | EqualityComparable,
-};
-ENUM_OPERATORS(CPPTypeFlags, CPPTypeFlags::EqualityComparable)
-
namespace blender::fn {
-template<typename T, CPPTypeFlags flags>
-inline std::unique_ptr<const CPPType> create_cpp_type(StringRef name)
+template<typename T, CPPTypeFlags Flags>
+CPPType::CPPType(CPPTypeParam<T, Flags> /* unused */, StringRef debug_name)
{
using namespace cpp_type_util;
- CPPTypeMembers m;
- m.name = name;
- m.size = (int64_t)sizeof(T);
- m.alignment = (int64_t)alignof(T);
- m.is_trivially_destructible = std::is_trivially_destructible_v<T>;
+ debug_name_ = debug_name;
+ size_ = (int64_t)sizeof(T);
+ alignment_ = (int64_t)alignof(T);
+ is_trivially_destructible_ = std::is_trivially_destructible_v<T>;
if constexpr (std::is_default_constructible_v<T>) {
- m.default_construct = default_construct_cb<T>;
- m.default_construct_indices = default_construct_indices_cb<T>;
+ default_construct_ = default_construct_cb<T>;
+ default_construct_indices_ = default_construct_indices_cb<T>;
static T default_value;
- m.default_value = (void *)&default_value;
+ default_value_ = (void *)&default_value;
}
if constexpr (std::is_destructible_v<T>) {
- m.destruct = destruct_cb<T>;
- m.destruct_indices = destruct_indices_cb<T>;
+ destruct_ = destruct_cb<T>;
+ destruct_indices_ = destruct_indices_cb<T>;
}
if constexpr (std::is_copy_assignable_v<T>) {
- m.copy_assign = copy_assign_cb<T>;
- m.copy_assign_indices = copy_assign_indices_cb<T>;
+ copy_assign_ = copy_assign_cb<T>;
+ copy_assign_indices_ = copy_assign_indices_cb<T>;
}
if constexpr (std::is_copy_constructible_v<T>) {
- m.copy_construct = copy_construct_cb<T>;
- m.copy_construct_indices = copy_construct_indices_cb<T>;
+ copy_construct_ = copy_construct_cb<T>;
+ copy_construct_indices_ = copy_construct_indices_cb<T>;
}
if constexpr (std::is_move_assignable_v<T>) {
- m.move_assign = move_assign_cb<T>;
- m.move_assign_indices = move_assign_indices_cb<T>;
+ move_assign_ = move_assign_cb<T>;
+ move_assign_indices_ = move_assign_indices_cb<T>;
}
if constexpr (std::is_move_constructible_v<T>) {
- m.move_construct = move_construct_cb<T>;
- m.move_construct_indices = move_construct_indices_cb<T>;
+ move_construct_ = move_construct_cb<T>;
+ move_construct_indices_ = move_construct_indices_cb<T>;
}
if constexpr (std::is_destructible_v<T>) {
if constexpr (std::is_move_assignable_v<T>) {
- m.relocate_assign = relocate_assign_cb<T>;
- m.relocate_assign_indices = relocate_assign_indices_cb<T>;
+ relocate_assign_ = relocate_assign_cb<T>;
+ relocate_assign_indices_ = relocate_assign_indices_cb<T>;
}
if constexpr (std::is_move_constructible_v<T>) {
- m.relocate_construct = relocate_construct_cb<T>;
- m.relocate_construct_indices = relocate_construct_indices_cb<T>;
+ relocate_construct_ = relocate_construct_cb<T>;
+ relocate_construct_indices_ = relocate_construct_indices_cb<T>;
}
}
if constexpr (std::is_copy_assignable_v<T>) {
- m.fill_assign_indices = fill_assign_indices_cb<T>;
+ fill_assign_indices_ = fill_assign_indices_cb<T>;
}
if constexpr (std::is_copy_constructible_v<T>) {
- m.fill_construct_indices = fill_construct_indices_cb<T>;
+ fill_construct_indices_ = fill_construct_indices_cb<T>;
}
- if constexpr ((bool)(flags & CPPTypeFlags::Hashable)) {
- m.hash = hash_cb<T>;
+ if constexpr ((bool)(Flags & CPPTypeFlags::Hashable)) {
+ hash_ = hash_cb<T>;
}
- if constexpr ((bool)(flags & CPPTypeFlags::Printable)) {
- m.print = print_cb<T>;
+ if constexpr ((bool)(Flags & CPPTypeFlags::Printable)) {
+ print_ = print_cb<T>;
}
- if constexpr ((bool)(flags & CPPTypeFlags::EqualityComparable)) {
- m.is_equal = is_equal_cb<T>;
+ if constexpr ((bool)(Flags & CPPTypeFlags::EqualityComparable)) {
+ is_equal_ = is_equal_cb<T>;
}
- const CPPType *type = new CPPType(std::move(m));
- return std::unique_ptr<const CPPType>(type);
+ alignment_mask_ = (uintptr_t)alignment_ - (uintptr_t)1;
+ has_special_member_functions_ = (default_construct_ && copy_construct_ && copy_assign_ &&
+ move_construct_ && move_assign_ && destruct_);
}
} // namespace blender::fn
#define MAKE_CPP_TYPE(IDENTIFIER, TYPE_NAME, FLAGS) \
- template<> const blender::fn::CPPType &blender::fn::CPPType::get<TYPE_NAME>() \
- { \
- static std::unique_ptr<const CPPType> cpp_type = \
- blender::fn::create_cpp_type<TYPE_NAME, FLAGS>(STRINGIFY(IDENTIFIER)); \
- return *cpp_type; \
- } \
- /* Support using `CPPType::get<const T>()`. Otherwise the caller would have to remove const. */ \
- template<> const blender::fn::CPPType &blender::fn::CPPType::get<const TYPE_NAME>() \
+ template<> const blender::fn::CPPType &blender::fn::CPPType::get_impl<TYPE_NAME>() \
{ \
- return blender::fn::CPPType::get<TYPE_NAME>(); \
+ static CPPType cpp_type{blender::fn::CPPTypeParam<TYPE_NAME, FLAGS>(), \
+ STRINGIFY(IDENTIFIER)}; \
+ return cpp_type; \
}
diff --git a/source/blender/functions/FN_generic_vector_array.hh b/source/blender/functions/FN_generic_vector_array.hh
index eeba0c9dba2..179e85671f8 100644
--- a/source/blender/functions/FN_generic_vector_array.hh
+++ b/source/blender/functions/FN_generic_vector_array.hh
@@ -82,6 +82,8 @@ class GVectorArray : NonCopyable, NonMovable {
void extend(IndexMask mask, const GVVectorArray &values);
void extend(IndexMask mask, const GVectorArray &values);
+ void clear(IndexMask mask);
+
GMutableSpan operator[](int64_t index);
GSpan operator[](int64_t index) const;
diff --git a/source/blender/functions/FN_generic_virtual_array.hh b/source/blender/functions/FN_generic_virtual_array.hh
index c9398ceb547..f429243e2de 100644
--- a/source/blender/functions/FN_generic_virtual_array.hh
+++ b/source/blender/functions/FN_generic_virtual_array.hh
@@ -129,7 +129,7 @@ class GVArray {
}
/* Same as `get_internal_single`, but `r_value` points to initialized memory. */
- void get_single_to_uninitialized(void *r_value) const
+ void get_internal_single_to_uninitialized(void *r_value) const
{
type_->default_construct(r_value);
this->get_internal_single(r_value);
diff --git a/source/blender/functions/FN_multi_function_network.hh b/source/blender/functions/FN_multi_function_network.hh
deleted file mode 100644
index b303589106a..00000000000
--- a/source/blender/functions/FN_multi_function_network.hh
+++ /dev/null
@@ -1,536 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#pragma once
-
-/** \file
- * \ingroup fn
- *
- * A multi-function network (`MFNetwork`) allows you to connect multiple multi-functions. The
- * `MFNetworkEvaluator` is a multi-function that wraps an entire network into a new multi-function
- * (which can be used in another network and so on).
- *
- * A MFNetwork is a graph data structure with two kinds of nodes:
- * - MFFunctionNode: Represents a multi-function. Its input and output sockets correspond to
- * parameters of the referenced multi-function.
- * - MFDummyNode: Does not reference a multi-function. Instead it just has sockets that can be
- * used to represent node group inputs and outputs.
- *
- * Links represent data flow. Unlinked input sockets have no value. In order to execute a function
- * node, all its inputs have to be connected to something.
- *
- * Links are only allowed between sockets with the exact same MFDataType. There are no implicit
- * conversions.
- *
- * Every input and output parameter of a multi-function corresponds to exactly one input or output
- * socket respectively. A multiple parameter belongs to exactly one input AND one output socket.
- *
- * There is an .to_dot() method that generates a graph in dot format for debugging purposes.
- */
-
-#include "FN_multi_function.hh"
-
-#include "BLI_vector_set.hh"
-
-namespace blender::fn {
-
-class MFNode;
-class MFFunctionNode;
-class MFDummyNode;
-class MFSocket;
-class MFInputSocket;
-class MFOutputSocket;
-class MFNetwork;
-
-class MFNode : NonCopyable, NonMovable {
- protected:
- MFNetwork *network_;
- Span<MFInputSocket *> inputs_;
- Span<MFOutputSocket *> outputs_;
- bool is_dummy_;
- int id_;
-
- friend MFNetwork;
-
- public:
- StringRefNull name() const;
-
- int id() const;
-
- MFNetwork &network();
- const MFNetwork &network() const;
-
- bool is_dummy() const;
- bool is_function() const;
-
- MFDummyNode &as_dummy();
- const MFDummyNode &as_dummy() const;
-
- MFFunctionNode &as_function();
- const MFFunctionNode &as_function() const;
-
- MFInputSocket &input(int index);
- const MFInputSocket &input(int index) const;
-
- MFOutputSocket &output(int index);
- const MFOutputSocket &output(int index) const;
-
- Span<MFInputSocket *> inputs();
- Span<const MFInputSocket *> inputs() const;
-
- Span<MFOutputSocket *> outputs();
- Span<const MFOutputSocket *> outputs() const;
-
- bool has_unlinked_inputs() const;
-
- private:
- void destruct_sockets();
-};
-
-class MFFunctionNode : public MFNode {
- private:
- const MultiFunction *function_;
- Span<int> input_param_indices_;
- Span<int> output_param_indices_;
-
- friend MFNetwork;
-
- public:
- StringRefNull name() const;
-
- const MultiFunction &function() const;
-
- const MFInputSocket &input_for_param(int param_index) const;
- const MFOutputSocket &output_for_param(int param_index) const;
-};
-
-class MFDummyNode : public MFNode {
- protected:
- StringRefNull name_;
- MutableSpan<StringRefNull> input_names_;
- MutableSpan<StringRefNull> output_names_;
-
- friend MFNetwork;
-
- public:
- StringRefNull name() const;
-
- Span<StringRefNull> input_names() const;
- Span<StringRefNull> output_names() const;
-};
-
-class MFSocket : NonCopyable, NonMovable {
- protected:
- MFNode *node_;
- bool is_output_;
- int index_;
- MFDataType data_type_;
- int id_;
- StringRefNull name_;
-
- friend MFNetwork;
-
- public:
- StringRefNull name() const;
-
- int id() const;
- int index() const;
-
- const MFDataType &data_type() const;
-
- MFNode &node();
- const MFNode &node() const;
-
- bool is_input() const;
- bool is_output() const;
-
- MFInputSocket &as_input();
- const MFInputSocket &as_input() const;
-
- MFOutputSocket &as_output();
- const MFOutputSocket &as_output() const;
-};
-
-class MFInputSocket : public MFSocket {
- private:
- MFOutputSocket *origin_;
-
- friend MFNetwork;
-
- public:
- MFOutputSocket *origin();
- const MFOutputSocket *origin() const;
-};
-
-class MFOutputSocket : public MFSocket {
- private:
- Vector<MFInputSocket *, 1> targets_;
-
- friend MFNetwork;
-
- public:
- Span<MFInputSocket *> targets();
- Span<const MFInputSocket *> targets() const;
-};
-
-class MFNetwork : NonCopyable, NonMovable {
- private:
- LinearAllocator<> allocator_;
-
- VectorSet<MFFunctionNode *> function_nodes_;
- VectorSet<MFDummyNode *> dummy_nodes_;
-
- Vector<MFNode *> node_or_null_by_id_;
- Vector<MFSocket *> socket_or_null_by_id_;
-
- public:
- MFNetwork() = default;
- ~MFNetwork();
-
- MFFunctionNode &add_function(const MultiFunction &function);
- MFDummyNode &add_dummy(StringRef name,
- Span<MFDataType> input_types,
- Span<MFDataType> output_types,
- Span<StringRef> input_names,
- Span<StringRef> output_names);
- void add_link(MFOutputSocket &from, MFInputSocket &to);
-
- MFOutputSocket &add_input(StringRef name, MFDataType data_type);
- MFInputSocket &add_output(StringRef name, MFDataType data_type);
-
- void relink(MFOutputSocket &old_output, MFOutputSocket &new_output);
-
- void remove(MFNode &node);
- void remove(Span<MFNode *> nodes);
-
- int socket_id_amount() const;
- int node_id_amount() const;
-
- Span<MFDummyNode *> dummy_nodes();
- Span<MFFunctionNode *> function_nodes();
-
- MFNode *node_or_null_by_id(int id);
- const MFNode *node_or_null_by_id(int id) const;
-
- MFSocket *socket_or_null_by_id(int id);
- const MFSocket *socket_or_null_by_id(int id) const;
-
- void find_dependencies(Span<const MFInputSocket *> sockets,
- VectorSet<const MFOutputSocket *> &r_dummy_sockets,
- VectorSet<const MFInputSocket *> &r_unlinked_inputs) const;
-
- bool have_dummy_or_unlinked_dependencies(Span<const MFInputSocket *> sockets) const;
-
- std::string to_dot(Span<const MFNode *> marked_nodes = {}) const;
-};
-
-/* --------------------------------------------------------------------
- * MFNode inline methods.
- */
-
-inline StringRefNull MFNode::name() const
-{
- if (is_dummy_) {
- return this->as_dummy().name();
- }
- else {
- return this->as_function().name();
- }
-}
-
-inline int MFNode::id() const
-{
- return id_;
-}
-
-inline MFNetwork &MFNode::network()
-{
- return *network_;
-}
-
-inline const MFNetwork &MFNode::network() const
-{
- return *network_;
-}
-
-inline bool MFNode::is_dummy() const
-{
- return is_dummy_;
-}
-
-inline bool MFNode::is_function() const
-{
- return !is_dummy_;
-}
-
-inline MFDummyNode &MFNode::as_dummy()
-{
- BLI_assert(is_dummy_);
- return static_cast<MFDummyNode &>(*this);
-}
-
-inline const MFDummyNode &MFNode::as_dummy() const
-{
- BLI_assert(is_dummy_);
- return static_cast<const MFDummyNode &>(*this);
-}
-
-inline MFFunctionNode &MFNode::as_function()
-{
- BLI_assert(!is_dummy_);
- return static_cast<MFFunctionNode &>(*this);
-}
-
-inline const MFFunctionNode &MFNode::as_function() const
-{
- BLI_assert(!is_dummy_);
- return static_cast<const MFFunctionNode &>(*this);
-}
-
-inline MFInputSocket &MFNode::input(int index)
-{
- return *inputs_[index];
-}
-
-inline const MFInputSocket &MFNode::input(int index) const
-{
- return *inputs_[index];
-}
-
-inline MFOutputSocket &MFNode::output(int index)
-{
- return *outputs_[index];
-}
-
-inline const MFOutputSocket &MFNode::output(int index) const
-{
- return *outputs_[index];
-}
-
-inline Span<MFInputSocket *> MFNode::inputs()
-{
- return inputs_;
-}
-
-inline Span<const MFInputSocket *> MFNode::inputs() const
-{
- return inputs_;
-}
-
-inline Span<MFOutputSocket *> MFNode::outputs()
-{
- return outputs_;
-}
-
-inline Span<const MFOutputSocket *> MFNode::outputs() const
-{
- return outputs_;
-}
-
-inline bool MFNode::has_unlinked_inputs() const
-{
- for (const MFInputSocket *socket : inputs_) {
- if (socket->origin() == nullptr) {
- return true;
- }
- }
- return false;
-}
-
-/* --------------------------------------------------------------------
- * MFFunctionNode inline methods.
- */
-
-inline StringRefNull MFFunctionNode::name() const
-{
- return function_->name();
-}
-
-inline const MultiFunction &MFFunctionNode::function() const
-{
- return *function_;
-}
-
-inline const MFInputSocket &MFFunctionNode::input_for_param(int param_index) const
-{
- return this->input(input_param_indices_.first_index(param_index));
-}
-
-inline const MFOutputSocket &MFFunctionNode::output_for_param(int param_index) const
-{
- return this->output(output_param_indices_.first_index(param_index));
-}
-
-/* --------------------------------------------------------------------
- * MFDummyNode inline methods.
- */
-
-inline StringRefNull MFDummyNode::name() const
-{
- return name_;
-}
-
-inline Span<StringRefNull> MFDummyNode::input_names() const
-{
- return input_names_;
-}
-
-inline Span<StringRefNull> MFDummyNode::output_names() const
-{
- return output_names_;
-}
-
-/* --------------------------------------------------------------------
- * MFSocket inline methods.
- */
-
-inline StringRefNull MFSocket::name() const
-{
- return name_;
-}
-
-inline int MFSocket::id() const
-{
- return id_;
-}
-
-inline int MFSocket::index() const
-{
- return index_;
-}
-
-inline const MFDataType &MFSocket::data_type() const
-{
- return data_type_;
-}
-
-inline MFNode &MFSocket::node()
-{
- return *node_;
-}
-
-inline const MFNode &MFSocket::node() const
-{
- return *node_;
-}
-
-inline bool MFSocket::is_input() const
-{
- return !is_output_;
-}
-
-inline bool MFSocket::is_output() const
-{
- return is_output_;
-}
-
-inline MFInputSocket &MFSocket::as_input()
-{
- BLI_assert(this->is_input());
- return static_cast<MFInputSocket &>(*this);
-}
-
-inline const MFInputSocket &MFSocket::as_input() const
-{
- BLI_assert(this->is_input());
- return static_cast<const MFInputSocket &>(*this);
-}
-
-inline MFOutputSocket &MFSocket::as_output()
-{
- BLI_assert(this->is_output());
- return static_cast<MFOutputSocket &>(*this);
-}
-
-inline const MFOutputSocket &MFSocket::as_output() const
-{
- BLI_assert(this->is_output());
- return static_cast<const MFOutputSocket &>(*this);
-}
-
-/* --------------------------------------------------------------------
- * MFInputSocket inline methods.
- */
-
-inline MFOutputSocket *MFInputSocket::origin()
-{
- return origin_;
-}
-
-inline const MFOutputSocket *MFInputSocket::origin() const
-{
- return origin_;
-}
-
-/* --------------------------------------------------------------------
- * MFOutputSocket inline methods.
- */
-
-inline Span<MFInputSocket *> MFOutputSocket::targets()
-{
- return targets_;
-}
-
-inline Span<const MFInputSocket *> MFOutputSocket::targets() const
-{
- return targets_;
-}
-
-/* --------------------------------------------------------------------
- * MFNetwork inline methods.
- */
-
-inline Span<MFDummyNode *> MFNetwork::dummy_nodes()
-{
- return dummy_nodes_;
-}
-
-inline Span<MFFunctionNode *> MFNetwork::function_nodes()
-{
- return function_nodes_;
-}
-
-inline MFNode *MFNetwork::node_or_null_by_id(int id)
-{
- return node_or_null_by_id_[id];
-}
-
-inline const MFNode *MFNetwork::node_or_null_by_id(int id) const
-{
- return node_or_null_by_id_[id];
-}
-
-inline MFSocket *MFNetwork::socket_or_null_by_id(int id)
-{
- return socket_or_null_by_id_[id];
-}
-
-inline const MFSocket *MFNetwork::socket_or_null_by_id(int id) const
-{
- return socket_or_null_by_id_[id];
-}
-
-inline int MFNetwork::socket_id_amount() const
-{
- return socket_or_null_by_id_.size();
-}
-
-inline int MFNetwork::node_id_amount() const
-{
- return node_or_null_by_id_.size();
-}
-
-} // namespace blender::fn
diff --git a/source/blender/functions/FN_multi_function_network_evaluation.hh b/source/blender/functions/FN_multi_function_network_evaluation.hh
deleted file mode 100644
index 17cffa406f7..00000000000
--- a/source/blender/functions/FN_multi_function_network_evaluation.hh
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#pragma once
-
-/** \file
- * \ingroup fn
- */
-
-#include "FN_multi_function_network.hh"
-
-namespace blender::fn {
-
-class MFNetworkEvaluationStorage;
-
-class MFNetworkEvaluator : public MultiFunction {
- private:
- MFSignature signature_;
- Vector<const MFOutputSocket *> inputs_;
- Vector<const MFInputSocket *> outputs_;
-
- public:
- MFNetworkEvaluator(Vector<const MFOutputSocket *> inputs, Vector<const MFInputSocket *> outputs);
-
- void call(IndexMask mask, MFParams params, MFContext context) const override;
-
- private:
- using Storage = MFNetworkEvaluationStorage;
-
- void copy_inputs_to_storage(MFParams params, Storage &storage) const;
- void copy_outputs_to_storage(
- MFParams params,
- Storage &storage,
- Vector<const MFInputSocket *> &outputs_to_initialize_in_the_end) const;
-
- void evaluate_network_to_compute_outputs(MFContext &global_context, Storage &storage) const;
-
- void evaluate_function(MFContext &global_context,
- const MFFunctionNode &function_node,
- Storage &storage) const;
-
- bool can_do_single_value_evaluation(const MFFunctionNode &function_node, Storage &storage) const;
-
- void initialize_remaining_outputs(MFParams params,
- Storage &storage,
- Span<const MFInputSocket *> remaining_outputs) const;
-};
-
-} // namespace blender::fn
diff --git a/source/blender/functions/FN_multi_function_params.hh b/source/blender/functions/FN_multi_function_params.hh
index e292d11def7..a480287d578 100644
--- a/source/blender/functions/FN_multi_function_params.hh
+++ b/source/blender/functions/FN_multi_function_params.hh
@@ -54,6 +54,11 @@ class MFParamsBuilder {
MFParamsBuilder(const class MultiFunction &fn, int64_t min_array_size);
+ template<typename T> void add_readonly_single_input_value(T value, StringRef expected_name = "")
+ {
+ T *value_ptr = &scope_.add_value<T>(std::move(value), __func__);
+ this->add_readonly_single_input(value_ptr, expected_name);
+ }
template<typename T> void add_readonly_single_input(const T *value, StringRef expected_name = "")
{
this->add_readonly_single_input(scope_.construct<GVArray_For_SingleValueRef>(
@@ -83,6 +88,12 @@ class MFParamsBuilder {
this->add_readonly_vector_input(
scope_.construct<GVVectorArray_For_GVectorArray>(__func__, vector_array), expected_name);
}
+ void add_readonly_vector_input(const GSpan single_vector, StringRef expected_name = "")
+ {
+ this->add_readonly_vector_input(
+ scope_.construct<GVVectorArray_For_SingleGSpan>(__func__, single_vector, min_array_size_),
+ expected_name);
+ }
void add_readonly_vector_input(const GVVectorArray &ref, StringRef expected_name = "")
{
this->assert_current_param_type(MFParamType::ForVectorInput(ref.type()), expected_name);
diff --git a/source/blender/functions/FN_multi_function_signature.hh b/source/blender/functions/FN_multi_function_signature.hh
index 23309c9a5e6..d05948cc645 100644
--- a/source/blender/functions/FN_multi_function_signature.hh
+++ b/source/blender/functions/FN_multi_function_signature.hh
@@ -160,6 +160,21 @@ class MFSignatureBuilder {
}
}
+ void add(StringRef name, const MFParamType &param_type)
+ {
+ switch (param_type.interface_type()) {
+ case MFParamType::Input:
+ this->input(name, param_type.data_type());
+ break;
+ case MFParamType::Mutable:
+ this->mutable_(name, param_type.data_type());
+ break;
+ case MFParamType::Output:
+ this->output(name, param_type.data_type());
+ break;
+ }
+ }
+
/* Context */
/** This indicates that the function accesses the context. This disables optimizations that
diff --git a/source/blender/functions/intern/generic_vector_array.cc b/source/blender/functions/intern/generic_vector_array.cc
index 9556d24218e..ec95a283919 100644
--- a/source/blender/functions/intern/generic_vector_array.cc
+++ b/source/blender/functions/intern/generic_vector_array.cc
@@ -78,6 +78,15 @@ void GVectorArray::extend(IndexMask mask, const GVectorArray &values)
this->extend(mask, virtual_values);
}
+void GVectorArray::clear(IndexMask mask)
+{
+ for (const int64_t i : mask) {
+ Item &item = items_[i];
+ type_.destruct_n(item.start, item.length);
+ item.length = 0;
+ }
+}
+
GMutableSpan GVectorArray::operator[](const int64_t index)
{
Item &item = items_[index];
diff --git a/source/blender/functions/intern/multi_function_network.cc b/source/blender/functions/intern/multi_function_network.cc
deleted file mode 100644
index b5c2c09a35a..00000000000
--- a/source/blender/functions/intern/multi_function_network.cc
+++ /dev/null
@@ -1,330 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#include "BLI_dot_export.hh"
-#include "BLI_stack.hh"
-
-#include "FN_multi_function_network.hh"
-
-namespace blender::fn {
-
-MFNetwork::~MFNetwork()
-{
- for (MFFunctionNode *node : function_nodes_) {
- node->destruct_sockets();
- node->~MFFunctionNode();
- }
- for (MFDummyNode *node : dummy_nodes_) {
- node->destruct_sockets();
- node->~MFDummyNode();
- }
-}
-
-void MFNode::destruct_sockets()
-{
- for (MFInputSocket *socket : inputs_) {
- socket->~MFInputSocket();
- }
- for (MFOutputSocket *socket : outputs_) {
- socket->~MFOutputSocket();
- }
-}
-
-/**
- * Add a new function node to the network. The caller keeps the ownership of the function. The
- * function should not be freed before the network. A reference to the new node is returned. The
- * node is owned by the network.
- */
-MFFunctionNode &MFNetwork::add_function(const MultiFunction &function)
-{
- Vector<int, 16> input_param_indices, output_param_indices;
-
- for (int param_index : function.param_indices()) {
- switch (function.param_type(param_index).interface_type()) {
- case MFParamType::Input: {
- input_param_indices.append(param_index);
- break;
- }
- case MFParamType::Output: {
- output_param_indices.append(param_index);
- break;
- }
- case MFParamType::Mutable: {
- input_param_indices.append(param_index);
- output_param_indices.append(param_index);
- break;
- }
- }
- }
-
- MFFunctionNode &node = *allocator_.construct<MFFunctionNode>().release();
- function_nodes_.add_new(&node);
-
- node.network_ = this;
- node.is_dummy_ = false;
- node.id_ = node_or_null_by_id_.append_and_get_index(&node);
- node.function_ = &function;
- node.input_param_indices_ = allocator_.construct_array_copy<int>(input_param_indices);
- node.output_param_indices_ = allocator_.construct_array_copy<int>(output_param_indices);
-
- node.inputs_ = allocator_.construct_elements_and_pointer_array<MFInputSocket>(
- input_param_indices.size());
- node.outputs_ = allocator_.construct_elements_and_pointer_array<MFOutputSocket>(
- output_param_indices.size());
-
- for (int i : input_param_indices.index_range()) {
- int param_index = input_param_indices[i];
- MFParamType param = function.param_type(param_index);
- BLI_assert(param.is_input_or_mutable());
-
- MFInputSocket &socket = *node.inputs_[i];
- socket.data_type_ = param.data_type();
- socket.node_ = &node;
- socket.index_ = i;
- socket.is_output_ = false;
- socket.name_ = function.param_name(param_index);
- socket.origin_ = nullptr;
- socket.id_ = socket_or_null_by_id_.append_and_get_index(&socket);
- }
-
- for (int i : output_param_indices.index_range()) {
- int param_index = output_param_indices[i];
- MFParamType param = function.param_type(param_index);
- BLI_assert(param.is_output_or_mutable());
-
- MFOutputSocket &socket = *node.outputs_[i];
- socket.data_type_ = param.data_type();
- socket.node_ = &node;
- socket.index_ = i;
- socket.is_output_ = true;
- socket.name_ = function.param_name(param_index);
- socket.id_ = socket_or_null_by_id_.append_and_get_index(&socket);
- }
-
- return node;
-}
-
-/**
- * Add a dummy node with the given input and output sockets.
- */
-MFDummyNode &MFNetwork::add_dummy(StringRef name,
- Span<MFDataType> input_types,
- Span<MFDataType> output_types,
- Span<StringRef> input_names,
- Span<StringRef> output_names)
-{
- assert_same_size(input_types, input_names);
- assert_same_size(output_types, output_names);
-
- MFDummyNode &node = *allocator_.construct<MFDummyNode>().release();
- dummy_nodes_.add_new(&node);
-
- node.network_ = this;
- node.is_dummy_ = true;
- node.name_ = allocator_.copy_string(name);
- node.id_ = node_or_null_by_id_.append_and_get_index(&node);
-
- node.inputs_ = allocator_.construct_elements_and_pointer_array<MFInputSocket>(
- input_types.size());
- node.outputs_ = allocator_.construct_elements_and_pointer_array<MFOutputSocket>(
- output_types.size());
-
- node.input_names_ = allocator_.allocate_array<StringRefNull>(input_types.size());
- node.output_names_ = allocator_.allocate_array<StringRefNull>(output_types.size());
-
- for (int i : input_types.index_range()) {
- MFInputSocket &socket = *node.inputs_[i];
- socket.data_type_ = input_types[i];
- socket.node_ = &node;
- socket.index_ = i;
- socket.is_output_ = false;
- socket.name_ = allocator_.copy_string(input_names[i]);
- socket.id_ = socket_or_null_by_id_.append_and_get_index(&socket);
- node.input_names_[i] = socket.name_;
- }
-
- for (int i : output_types.index_range()) {
- MFOutputSocket &socket = *node.outputs_[i];
- socket.data_type_ = output_types[i];
- socket.node_ = &node;
- socket.index_ = i;
- socket.is_output_ = true;
- socket.name_ = allocator_.copy_string(output_names[i]);
- socket.id_ = socket_or_null_by_id_.append_and_get_index(&socket);
- node.output_names_[i] = socket.name_;
- }
-
- return node;
-}
-
-/**
- * Connect two sockets. This invokes undefined behavior if the sockets belong to different
- * networks, the sockets have a different data type, or the `to` socket is connected to something
- * else already.
- */
-void MFNetwork::add_link(MFOutputSocket &from, MFInputSocket &to)
-{
- BLI_assert(to.origin_ == nullptr);
- BLI_assert(from.node_->network_ == to.node_->network_);
- BLI_assert(from.data_type_ == to.data_type_);
- from.targets_.append(&to);
- to.origin_ = &from;
-}
-
-MFOutputSocket &MFNetwork::add_input(StringRef name, MFDataType data_type)
-{
- return this->add_dummy(name, {}, {data_type}, {}, {"Value"}).output(0);
-}
-
-MFInputSocket &MFNetwork::add_output(StringRef name, MFDataType data_type)
-{
- return this->add_dummy(name, {data_type}, {}, {"Value"}, {}).input(0);
-}
-
-void MFNetwork::relink(MFOutputSocket &old_output, MFOutputSocket &new_output)
-{
- BLI_assert(&old_output != &new_output);
- BLI_assert(old_output.data_type_ == new_output.data_type_);
- for (MFInputSocket *input : old_output.targets()) {
- input->origin_ = &new_output;
- }
- new_output.targets_.extend(old_output.targets_);
- old_output.targets_.clear();
-}
-
-void MFNetwork::remove(MFNode &node)
-{
- for (MFInputSocket *socket : node.inputs_) {
- if (socket->origin_ != nullptr) {
- socket->origin_->targets_.remove_first_occurrence_and_reorder(socket);
- }
- socket_or_null_by_id_[socket->id_] = nullptr;
- }
- for (MFOutputSocket *socket : node.outputs_) {
- for (MFInputSocket *other : socket->targets_) {
- other->origin_ = nullptr;
- }
- socket_or_null_by_id_[socket->id_] = nullptr;
- }
- node.destruct_sockets();
- if (node.is_dummy()) {
- MFDummyNode &dummy_node = node.as_dummy();
- dummy_node.~MFDummyNode();
- dummy_nodes_.remove_contained(&dummy_node);
- }
- else {
- MFFunctionNode &function_node = node.as_function();
- function_node.~MFFunctionNode();
- function_nodes_.remove_contained(&function_node);
- }
- node_or_null_by_id_[node.id_] = nullptr;
-}
-
-void MFNetwork::remove(Span<MFNode *> nodes)
-{
- for (MFNode *node : nodes) {
- this->remove(*node);
- }
-}
-
-void MFNetwork::find_dependencies(Span<const MFInputSocket *> sockets,
- VectorSet<const MFOutputSocket *> &r_dummy_sockets,
- VectorSet<const MFInputSocket *> &r_unlinked_inputs) const
-{
- Set<const MFNode *> visited_nodes;
- Stack<const MFInputSocket *> sockets_to_check;
- sockets_to_check.push_multiple(sockets);
-
- while (!sockets_to_check.is_empty()) {
- const MFInputSocket &socket = *sockets_to_check.pop();
- const MFOutputSocket *origin_socket = socket.origin();
- if (origin_socket == nullptr) {
- r_unlinked_inputs.add(&socket);
- continue;
- }
-
- const MFNode &origin_node = origin_socket->node();
-
- if (origin_node.is_dummy()) {
- r_dummy_sockets.add(origin_socket);
- continue;
- }
-
- if (visited_nodes.add(&origin_node)) {
- sockets_to_check.push_multiple(origin_node.inputs());
- }
- }
-}
-
-bool MFNetwork::have_dummy_or_unlinked_dependencies(Span<const MFInputSocket *> sockets) const
-{
- VectorSet<const MFOutputSocket *> dummy_sockets;
- VectorSet<const MFInputSocket *> unlinked_inputs;
- this->find_dependencies(sockets, dummy_sockets, unlinked_inputs);
- return dummy_sockets.size() + unlinked_inputs.size() > 0;
-}
-
-std::string MFNetwork::to_dot(Span<const MFNode *> marked_nodes) const
-{
- dot::DirectedGraph digraph;
- digraph.set_rankdir(dot::Attr_rankdir::LeftToRight);
-
- Map<const MFNode *, dot::NodeWithSocketsRef> dot_nodes;
-
- Vector<const MFNode *> all_nodes;
- all_nodes.extend(function_nodes_.as_span().cast<const MFNode *>());
- all_nodes.extend(dummy_nodes_.as_span().cast<const MFNode *>());
-
- for (const MFNode *node : all_nodes) {
- dot::Node &dot_node = digraph.new_node("");
-
- Vector<std::string> input_names, output_names;
- for (const MFInputSocket *socket : node->inputs_) {
- input_names.append(socket->name() + "(" + socket->data_type().to_string() + ")");
- }
- for (const MFOutputSocket *socket : node->outputs_) {
- output_names.append(socket->name() + " (" + socket->data_type().to_string() + ")");
- }
-
- dot::NodeWithSocketsRef dot_node_ref{dot_node, node->name(), input_names, output_names};
- dot_nodes.add_new(node, dot_node_ref);
- }
-
- for (const MFDummyNode *node : dummy_nodes_) {
- dot_nodes.lookup(node).node().set_background_color("#77EE77");
- }
- for (const MFNode *node : marked_nodes) {
- dot_nodes.lookup(node).node().set_background_color("#7777EE");
- }
-
- for (const MFNode *to_node : all_nodes) {
- dot::NodeWithSocketsRef to_dot_node = dot_nodes.lookup(to_node);
-
- for (const MFInputSocket *to_socket : to_node->inputs_) {
- const MFOutputSocket *from_socket = to_socket->origin_;
- if (from_socket != nullptr) {
- const MFNode *from_node = from_socket->node_;
- dot::NodeWithSocketsRef from_dot_node = dot_nodes.lookup(from_node);
- digraph.new_edge(from_dot_node.output(from_socket->index_),
- to_dot_node.input(to_socket->index_));
- }
- }
- }
-
- return digraph.to_dot_string();
-}
-
-} // namespace blender::fn
diff --git a/source/blender/functions/intern/multi_function_network_evaluation.cc b/source/blender/functions/intern/multi_function_network_evaluation.cc
deleted file mode 100644
index 9a0cb0c35ce..00000000000
--- a/source/blender/functions/intern/multi_function_network_evaluation.cc
+++ /dev/null
@@ -1,1083 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-/** \file
- * \ingroup fn
- *
- * The `MFNetworkEvaluator` class is a multi-function that consists of potentially many smaller
- * multi-functions. When called, it traverses the underlying MFNetwork and executes the required
- * function nodes.
- *
- * There are many possible approaches to evaluate a function network. The approach implemented
- * below has the following features:
- * - It does not use recursion. Those could become problematic with long node chains.
- * - It can handle all existing parameter types (including mutable parameters).
- * - Avoids data copies in many cases.
- * - Every node is executed at most once.
- * - Can compute sub-functions on a single element, when the result is the same for all elements.
- *
- * Possible improvements:
- * - Cache and reuse buffers.
- * - Use "deepest depth first" heuristic to decide which order the inputs of a node should be
- * computed. This reduces the number of required temporary buffers when they are reused.
- */
-
-#include "FN_multi_function_network_evaluation.hh"
-
-#include "BLI_resource_scope.hh"
-#include "BLI_stack.hh"
-
-namespace blender::fn {
-
-struct Value;
-
-/**
- * This keeps track of all the values that flow through the multi-function network. Therefore it
- * maintains a mapping between output sockets and their corresponding values. Every `value`
- * references some memory, that is owned either by the caller or this storage.
- *
- * A value can be owned by different sockets over time to avoid unnecessary copies.
- */
-class MFNetworkEvaluationStorage {
- private:
- LinearAllocator<> allocator_;
- IndexMask mask_;
- Array<Value *> value_per_output_id_;
- int64_t min_array_size_;
-
- public:
- MFNetworkEvaluationStorage(IndexMask mask, int socket_id_amount);
- ~MFNetworkEvaluationStorage();
-
- /* Add the values that have been provided by the caller of the multi-function network. */
- void add_single_input_from_caller(const MFOutputSocket &socket, const GVArray &virtual_array);
- void add_vector_input_from_caller(const MFOutputSocket &socket,
- const GVVectorArray &virtual_vector_array);
- void add_single_output_from_caller(const MFOutputSocket &socket, GMutableSpan span);
- void add_vector_output_from_caller(const MFOutputSocket &socket, GVectorArray &vector_array);
-
- /* Get input buffers for function node evaluations. */
- const GVArray &get_single_input__full(const MFInputSocket &socket, ResourceScope &scope);
- const GVArray &get_single_input__single(const MFInputSocket &socket, ResourceScope &scope);
- const GVVectorArray &get_vector_input__full(const MFInputSocket &socket, ResourceScope &scope);
- const GVVectorArray &get_vector_input__single(const MFInputSocket &socket, ResourceScope &scope);
-
- /* Get output buffers for function node evaluations. */
- GMutableSpan get_single_output__full(const MFOutputSocket &socket);
- GMutableSpan get_single_output__single(const MFOutputSocket &socket);
- GVectorArray &get_vector_output__full(const MFOutputSocket &socket);
- GVectorArray &get_vector_output__single(const MFOutputSocket &socket);
-
- /* Get mutable buffers for function node evaluations. */
- GMutableSpan get_mutable_single__full(const MFInputSocket &input,
- const MFOutputSocket &output,
- ResourceScope &scope);
- GMutableSpan get_mutable_single__single(const MFInputSocket &input,
- const MFOutputSocket &output,
- ResourceScope &scope);
- GVectorArray &get_mutable_vector__full(const MFInputSocket &input,
- const MFOutputSocket &output,
- ResourceScope &scope);
- GVectorArray &get_mutable_vector__single(const MFInputSocket &input,
- const MFOutputSocket &output,
- ResourceScope &scope);
-
- /* Mark a node as being done with evaluation. This might free temporary buffers that are no
- * longer needed. */
- void finish_node(const MFFunctionNode &node);
- void finish_output_socket(const MFOutputSocket &socket);
- void finish_input_socket(const MFInputSocket &socket);
-
- IndexMask mask() const;
- bool socket_is_computed(const MFOutputSocket &socket);
- bool is_same_value_for_every_index(const MFOutputSocket &socket);
- bool socket_has_buffer_for_output(const MFOutputSocket &socket);
-};
-
-MFNetworkEvaluator::MFNetworkEvaluator(Vector<const MFOutputSocket *> inputs,
- Vector<const MFInputSocket *> outputs)
- : inputs_(std::move(inputs)), outputs_(std::move(outputs))
-{
- BLI_assert(outputs_.size() > 0);
- MFSignatureBuilder signature{"Function Tree"};
-
- for (const MFOutputSocket *socket : inputs_) {
- BLI_assert(socket->node().is_dummy());
-
- MFDataType type = socket->data_type();
- switch (type.category()) {
- case MFDataType::Single:
- signature.single_input(socket->name(), type.single_type());
- break;
- case MFDataType::Vector:
- signature.vector_input(socket->name(), type.vector_base_type());
- break;
- }
- }
-
- for (const MFInputSocket *socket : outputs_) {
- BLI_assert(socket->node().is_dummy());
-
- MFDataType type = socket->data_type();
- switch (type.category()) {
- case MFDataType::Single:
- signature.single_output(socket->name(), type.single_type());
- break;
- case MFDataType::Vector:
- signature.vector_output(socket->name(), type.vector_base_type());
- break;
- }
- }
-
- signature_ = signature.build();
- this->set_signature(&signature_);
-}
-
-void MFNetworkEvaluator::call(IndexMask mask, MFParams params, MFContext context) const
-{
- if (mask.size() == 0) {
- return;
- }
-
- const MFNetwork &network = outputs_[0]->node().network();
- Storage storage(mask, network.socket_id_amount());
-
- Vector<const MFInputSocket *> outputs_to_initialize_in_the_end;
-
- this->copy_inputs_to_storage(params, storage);
- this->copy_outputs_to_storage(params, storage, outputs_to_initialize_in_the_end);
- this->evaluate_network_to_compute_outputs(context, storage);
- this->initialize_remaining_outputs(params, storage, outputs_to_initialize_in_the_end);
-}
-
-BLI_NOINLINE void MFNetworkEvaluator::copy_inputs_to_storage(MFParams params,
- Storage &storage) const
-{
- for (int input_index : inputs_.index_range()) {
- int param_index = input_index + 0;
- const MFOutputSocket &socket = *inputs_[input_index];
- switch (socket.data_type().category()) {
- case MFDataType::Single: {
- const GVArray &input_list = params.readonly_single_input(param_index);
- storage.add_single_input_from_caller(socket, input_list);
- break;
- }
- case MFDataType::Vector: {
- const GVVectorArray &input_list_list = params.readonly_vector_input(param_index);
- storage.add_vector_input_from_caller(socket, input_list_list);
- break;
- }
- }
- }
-}
-
-BLI_NOINLINE void MFNetworkEvaluator::copy_outputs_to_storage(
- MFParams params,
- Storage &storage,
- Vector<const MFInputSocket *> &outputs_to_initialize_in_the_end) const
-{
- for (int output_index : outputs_.index_range()) {
- int param_index = output_index + inputs_.size();
- const MFInputSocket &socket = *outputs_[output_index];
- const MFOutputSocket &origin = *socket.origin();
-
- if (origin.node().is_dummy()) {
- BLI_assert(inputs_.contains(&origin));
- /* Don't overwrite input buffers. */
- outputs_to_initialize_in_the_end.append(&socket);
- continue;
- }
-
- if (storage.socket_has_buffer_for_output(origin)) {
- /* When two outputs will be initialized to the same values. */
- outputs_to_initialize_in_the_end.append(&socket);
- continue;
- }
-
- switch (socket.data_type().category()) {
- case MFDataType::Single: {
- GMutableSpan span = params.uninitialized_single_output(param_index);
- storage.add_single_output_from_caller(origin, span);
- break;
- }
- case MFDataType::Vector: {
- GVectorArray &vector_array = params.vector_output(param_index);
- storage.add_vector_output_from_caller(origin, vector_array);
- break;
- }
- }
- }
-}
-
-BLI_NOINLINE void MFNetworkEvaluator::evaluate_network_to_compute_outputs(
- MFContext &global_context, Storage &storage) const
-{
- Stack<const MFOutputSocket *, 32> sockets_to_compute;
- for (const MFInputSocket *socket : outputs_) {
- sockets_to_compute.push(socket->origin());
- }
-
- /* This is the main loop that traverses the MFNetwork. */
- while (!sockets_to_compute.is_empty()) {
- const MFOutputSocket &socket = *sockets_to_compute.peek();
- const MFNode &node = socket.node();
-
- if (storage.socket_is_computed(socket)) {
- sockets_to_compute.pop();
- continue;
- }
-
- BLI_assert(node.is_function());
- BLI_assert(!node.has_unlinked_inputs());
- const MFFunctionNode &function_node = node.as_function();
-
- bool all_origins_are_computed = true;
- for (const MFInputSocket *input_socket : function_node.inputs()) {
- const MFOutputSocket *origin = input_socket->origin();
- if (origin != nullptr) {
- if (!storage.socket_is_computed(*origin)) {
- sockets_to_compute.push(origin);
- all_origins_are_computed = false;
- }
- }
- }
-
- if (all_origins_are_computed) {
- this->evaluate_function(global_context, function_node, storage);
- sockets_to_compute.pop();
- }
- }
-}
-
-BLI_NOINLINE void MFNetworkEvaluator::evaluate_function(MFContext &global_context,
- const MFFunctionNode &function_node,
- Storage &storage) const
-{
-
- const MultiFunction &function = function_node.function();
- // std::cout << "Function: " << function.name() << "\n";
-
- if (this->can_do_single_value_evaluation(function_node, storage)) {
- /* The function output would be the same for all elements. Therefore, it is enough to call the
- * function only on a single element. This can avoid many duplicate computations. */
- MFParamsBuilder params{function, 1};
- ResourceScope &scope = params.resource_scope();
-
- for (int param_index : function.param_indices()) {
- MFParamType param_type = function.param_type(param_index);
- switch (param_type.category()) {
- case MFParamType::SingleInput: {
- const MFInputSocket &socket = function_node.input_for_param(param_index);
- const GVArray &values = storage.get_single_input__single(socket, scope);
- params.add_readonly_single_input(values);
- break;
- }
- case MFParamType::VectorInput: {
- const MFInputSocket &socket = function_node.input_for_param(param_index);
- const GVVectorArray &values = storage.get_vector_input__single(socket, scope);
- params.add_readonly_vector_input(values);
- break;
- }
- case MFParamType::SingleOutput: {
- const MFOutputSocket &socket = function_node.output_for_param(param_index);
- GMutableSpan values = storage.get_single_output__single(socket);
- params.add_uninitialized_single_output(values);
- break;
- }
- case MFParamType::VectorOutput: {
- const MFOutputSocket &socket = function_node.output_for_param(param_index);
- GVectorArray &values = storage.get_vector_output__single(socket);
- params.add_vector_output(values);
- break;
- }
- case MFParamType::SingleMutable: {
- const MFInputSocket &input = function_node.input_for_param(param_index);
- const MFOutputSocket &output = function_node.output_for_param(param_index);
- GMutableSpan values = storage.get_mutable_single__single(input, output, scope);
- params.add_single_mutable(values);
- break;
- }
- case MFParamType::VectorMutable: {
- const MFInputSocket &input = function_node.input_for_param(param_index);
- const MFOutputSocket &output = function_node.output_for_param(param_index);
- GVectorArray &values = storage.get_mutable_vector__single(input, output, scope);
- params.add_vector_mutable(values);
- break;
- }
- }
- }
-
- function.call(IndexRange(1), params, global_context);
- }
- else {
- MFParamsBuilder params{function, storage.mask().min_array_size()};
- ResourceScope &scope = params.resource_scope();
-
- for (int param_index : function.param_indices()) {
- MFParamType param_type = function.param_type(param_index);
- switch (param_type.category()) {
- case MFParamType::SingleInput: {
- const MFInputSocket &socket = function_node.input_for_param(param_index);
- const GVArray &values = storage.get_single_input__full(socket, scope);
- params.add_readonly_single_input(values);
- break;
- }
- case MFParamType::VectorInput: {
- const MFInputSocket &socket = function_node.input_for_param(param_index);
- const GVVectorArray &values = storage.get_vector_input__full(socket, scope);
- params.add_readonly_vector_input(values);
- break;
- }
- case MFParamType::SingleOutput: {
- const MFOutputSocket &socket = function_node.output_for_param(param_index);
- GMutableSpan values = storage.get_single_output__full(socket);
- params.add_uninitialized_single_output(values);
- break;
- }
- case MFParamType::VectorOutput: {
- const MFOutputSocket &socket = function_node.output_for_param(param_index);
- GVectorArray &values = storage.get_vector_output__full(socket);
- params.add_vector_output(values);
- break;
- }
- case MFParamType::SingleMutable: {
- const MFInputSocket &input = function_node.input_for_param(param_index);
- const MFOutputSocket &output = function_node.output_for_param(param_index);
- GMutableSpan values = storage.get_mutable_single__full(input, output, scope);
- params.add_single_mutable(values);
- break;
- }
- case MFParamType::VectorMutable: {
- const MFInputSocket &input = function_node.input_for_param(param_index);
- const MFOutputSocket &output = function_node.output_for_param(param_index);
- GVectorArray &values = storage.get_mutable_vector__full(input, output, scope);
- params.add_vector_mutable(values);
- break;
- }
- }
- }
-
- function.call(storage.mask(), params, global_context);
- }
-
- storage.finish_node(function_node);
-}
-
-bool MFNetworkEvaluator::can_do_single_value_evaluation(const MFFunctionNode &function_node,
- Storage &storage) const
-{
- for (const MFInputSocket *socket : function_node.inputs()) {
- if (!storage.is_same_value_for_every_index(*socket->origin())) {
- return false;
- }
- }
- if (storage.mask().min_array_size() >= 1) {
- for (const MFOutputSocket *socket : function_node.outputs()) {
- if (storage.socket_has_buffer_for_output(*socket)) {
- return false;
- }
- }
- }
- return true;
-}
-
-BLI_NOINLINE void MFNetworkEvaluator::initialize_remaining_outputs(
- MFParams params, Storage &storage, Span<const MFInputSocket *> remaining_outputs) const
-{
- ResourceScope scope;
- for (const MFInputSocket *socket : remaining_outputs) {
- int param_index = inputs_.size() + outputs_.first_index_of(socket);
-
- switch (socket->data_type().category()) {
- case MFDataType::Single: {
- const GVArray &values = storage.get_single_input__full(*socket, scope);
- GMutableSpan output_values = params.uninitialized_single_output(param_index);
- values.materialize_to_uninitialized(storage.mask(), output_values.data());
- break;
- }
- case MFDataType::Vector: {
- const GVVectorArray &values = storage.get_vector_input__full(*socket, scope);
- GVectorArray &output_values = params.vector_output(param_index);
- output_values.extend(storage.mask(), values);
- break;
- }
- }
- }
-}
-
-/* -------------------------------------------------------------------- */
-/** \name Value Types
- * \{ */
-
-enum class ValueType {
- InputSingle,
- InputVector,
- OutputSingle,
- OutputVector,
- OwnSingle,
- OwnVector,
-};
-
-struct Value {
- ValueType type;
-
- Value(ValueType type) : type(type)
- {
- }
-};
-
-struct InputSingleValue : public Value {
- /** This virtual array has been provided by the code that called the multi-function network. */
- const GVArray &virtual_array;
-
- InputSingleValue(const GVArray &virtual_array)
- : Value(ValueType::InputSingle), virtual_array(virtual_array)
- {
- }
-};
-
-struct InputVectorValue : public Value {
- /** This virtual vector has been provided by the code that called the multi-function network. */
- const GVVectorArray &virtual_vector_array;
-
- InputVectorValue(const GVVectorArray &virtual_vector_array)
- : Value(ValueType::InputVector), virtual_vector_array(virtual_vector_array)
- {
- }
-};
-
-struct OutputValue : public Value {
- bool is_computed = false;
-
- OutputValue(ValueType type) : Value(type)
- {
- }
-};
-
-struct OutputSingleValue : public OutputValue {
- /** This span has been provided by the code that called the multi-function network. */
- GMutableSpan span;
-
- OutputSingleValue(GMutableSpan span) : OutputValue(ValueType::OutputSingle), span(span)
- {
- }
-};
-
-struct OutputVectorValue : public OutputValue {
- /** This vector array has been provided by the code that called the multi-function network. */
- GVectorArray *vector_array;
-
- OutputVectorValue(GVectorArray &vector_array)
- : OutputValue(ValueType::OutputVector), vector_array(&vector_array)
- {
- }
-};
-
-struct OwnSingleValue : public Value {
- /** This span has been allocated during the evaluation of the multi-function network and contains
- * intermediate data. It has to be freed once the network evaluation is finished. */
- GMutableSpan span;
- int max_remaining_users;
- bool is_single_allocated;
-
- OwnSingleValue(GMutableSpan span, int max_remaining_users, bool is_single_allocated)
- : Value(ValueType::OwnSingle),
- span(span),
- max_remaining_users(max_remaining_users),
- is_single_allocated(is_single_allocated)
- {
- }
-};
-
-struct OwnVectorValue : public Value {
- /** This vector array has been allocated during the evaluation of the multi-function network and
- * contains intermediate data. It has to be freed once the network evaluation is finished. */
- GVectorArray *vector_array;
- int max_remaining_users;
-
- OwnVectorValue(GVectorArray &vector_array, int max_remaining_users)
- : Value(ValueType::OwnVector),
- vector_array(&vector_array),
- max_remaining_users(max_remaining_users)
- {
- }
-};
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Storage methods
- * \{ */
-
-MFNetworkEvaluationStorage::MFNetworkEvaluationStorage(IndexMask mask, int socket_id_amount)
- : mask_(mask),
- value_per_output_id_(socket_id_amount, nullptr),
- min_array_size_(mask.min_array_size())
-{
-}
-
-MFNetworkEvaluationStorage::~MFNetworkEvaluationStorage()
-{
- for (Value *any_value : value_per_output_id_) {
- if (any_value == nullptr) {
- continue;
- }
- if (any_value->type == ValueType::OwnSingle) {
- OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value);
- GMutableSpan span = value->span;
- const CPPType &type = span.type();
- if (value->is_single_allocated) {
- type.destruct(span.data());
- }
- else {
- type.destruct_indices(span.data(), mask_);
- MEM_freeN(span.data());
- }
- }
- else if (any_value->type == ValueType::OwnVector) {
- OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value);
- delete value->vector_array;
- }
- }
-}
-
-IndexMask MFNetworkEvaluationStorage::mask() const
-{
- return mask_;
-}
-
-bool MFNetworkEvaluationStorage::socket_is_computed(const MFOutputSocket &socket)
-{
- Value *any_value = value_per_output_id_[socket.id()];
- if (any_value == nullptr) {
- return false;
- }
- if (ELEM(any_value->type, ValueType::OutputSingle, ValueType::OutputVector)) {
- return static_cast<OutputValue *>(any_value)->is_computed;
- }
- return true;
-}
-
-bool MFNetworkEvaluationStorage::is_same_value_for_every_index(const MFOutputSocket &socket)
-{
- Value *any_value = value_per_output_id_[socket.id()];
- switch (any_value->type) {
- case ValueType::OwnSingle:
- return static_cast<OwnSingleValue *>(any_value)->span.size() == 1;
- case ValueType::OwnVector:
- return static_cast<OwnVectorValue *>(any_value)->vector_array->size() == 1;
- case ValueType::InputSingle:
- return static_cast<InputSingleValue *>(any_value)->virtual_array.is_single();
- case ValueType::InputVector:
- return static_cast<InputVectorValue *>(any_value)->virtual_vector_array.is_single_vector();
- case ValueType::OutputSingle:
- return static_cast<OutputSingleValue *>(any_value)->span.size() == 1;
- case ValueType::OutputVector:
- return static_cast<OutputVectorValue *>(any_value)->vector_array->size() == 1;
- }
- BLI_assert(false);
- return false;
-}
-
-bool MFNetworkEvaluationStorage::socket_has_buffer_for_output(const MFOutputSocket &socket)
-{
- Value *any_value = value_per_output_id_[socket.id()];
- if (any_value == nullptr) {
- return false;
- }
-
- BLI_assert(ELEM(any_value->type, ValueType::OutputSingle, ValueType::OutputVector));
- return true;
-}
-
-void MFNetworkEvaluationStorage::finish_node(const MFFunctionNode &node)
-{
- for (const MFInputSocket *socket : node.inputs()) {
- this->finish_input_socket(*socket);
- }
- for (const MFOutputSocket *socket : node.outputs()) {
- this->finish_output_socket(*socket);
- }
-}
-
-void MFNetworkEvaluationStorage::finish_output_socket(const MFOutputSocket &socket)
-{
- Value *any_value = value_per_output_id_[socket.id()];
- if (any_value == nullptr) {
- return;
- }
-
- if (ELEM(any_value->type, ValueType::OutputSingle, ValueType::OutputVector)) {
- static_cast<OutputValue *>(any_value)->is_computed = true;
- }
-}
-
-void MFNetworkEvaluationStorage::finish_input_socket(const MFInputSocket &socket)
-{
- const MFOutputSocket &origin = *socket.origin();
-
- Value *any_value = value_per_output_id_[origin.id()];
- if (any_value == nullptr) {
- /* Can happen when a value has been forward to the next node. */
- return;
- }
-
- switch (any_value->type) {
- case ValueType::InputSingle:
- case ValueType::OutputSingle:
- case ValueType::InputVector:
- case ValueType::OutputVector: {
- break;
- }
- case ValueType::OwnSingle: {
- OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value);
- BLI_assert(value->max_remaining_users >= 1);
- value->max_remaining_users--;
- if (value->max_remaining_users == 0) {
- GMutableSpan span = value->span;
- const CPPType &type = span.type();
- if (value->is_single_allocated) {
- type.destruct(span.data());
- }
- else {
- type.destruct_indices(span.data(), mask_);
- MEM_freeN(span.data());
- }
- value_per_output_id_[origin.id()] = nullptr;
- }
- break;
- }
- case ValueType::OwnVector: {
- OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value);
- BLI_assert(value->max_remaining_users >= 1);
- value->max_remaining_users--;
- if (value->max_remaining_users == 0) {
- delete value->vector_array;
- value_per_output_id_[origin.id()] = nullptr;
- }
- break;
- }
- }
-}
-
-void MFNetworkEvaluationStorage::add_single_input_from_caller(const MFOutputSocket &socket,
- const GVArray &virtual_array)
-{
- BLI_assert(value_per_output_id_[socket.id()] == nullptr);
- BLI_assert(virtual_array.size() >= min_array_size_);
-
- auto *value = allocator_.construct<InputSingleValue>(virtual_array).release();
- value_per_output_id_[socket.id()] = value;
-}
-
-void MFNetworkEvaluationStorage::add_vector_input_from_caller(
- const MFOutputSocket &socket, const GVVectorArray &virtual_vector_array)
-{
- BLI_assert(value_per_output_id_[socket.id()] == nullptr);
- BLI_assert(virtual_vector_array.size() >= min_array_size_);
-
- auto *value = allocator_.construct<InputVectorValue>(virtual_vector_array).release();
- value_per_output_id_[socket.id()] = value;
-}
-
-void MFNetworkEvaluationStorage::add_single_output_from_caller(const MFOutputSocket &socket,
- GMutableSpan span)
-{
- BLI_assert(value_per_output_id_[socket.id()] == nullptr);
- BLI_assert(span.size() >= min_array_size_);
-
- auto *value = allocator_.construct<OutputSingleValue>(span).release();
- value_per_output_id_[socket.id()] = value;
-}
-
-void MFNetworkEvaluationStorage::add_vector_output_from_caller(const MFOutputSocket &socket,
- GVectorArray &vector_array)
-{
- BLI_assert(value_per_output_id_[socket.id()] == nullptr);
- BLI_assert(vector_array.size() >= min_array_size_);
-
- auto *value = allocator_.construct<OutputVectorValue>(vector_array).release();
- value_per_output_id_[socket.id()] = value;
-}
-
-GMutableSpan MFNetworkEvaluationStorage::get_single_output__full(const MFOutputSocket &socket)
-{
- Value *any_value = value_per_output_id_[socket.id()];
- if (any_value == nullptr) {
- const CPPType &type = socket.data_type().single_type();
- void *buffer = MEM_mallocN_aligned(min_array_size_ * type.size(), type.alignment(), AT);
- GMutableSpan span(type, buffer, min_array_size_);
-
- auto *value =
- allocator_.construct<OwnSingleValue>(span, socket.targets().size(), false).release();
- value_per_output_id_[socket.id()] = value;
-
- return span;
- }
-
- BLI_assert(any_value->type == ValueType::OutputSingle);
- return static_cast<OutputSingleValue *>(any_value)->span;
-}
-
-GMutableSpan MFNetworkEvaluationStorage::get_single_output__single(const MFOutputSocket &socket)
-{
- Value *any_value = value_per_output_id_[socket.id()];
- if (any_value == nullptr) {
- const CPPType &type = socket.data_type().single_type();
- void *buffer = allocator_.allocate(type.size(), type.alignment());
- GMutableSpan span(type, buffer, 1);
-
- auto *value =
- allocator_.construct<OwnSingleValue>(span, socket.targets().size(), true).release();
- value_per_output_id_[socket.id()] = value;
-
- return value->span;
- }
-
- BLI_assert(any_value->type == ValueType::OutputSingle);
- GMutableSpan span = static_cast<OutputSingleValue *>(any_value)->span;
- BLI_assert(span.size() == 1);
- return span;
-}
-
-GVectorArray &MFNetworkEvaluationStorage::get_vector_output__full(const MFOutputSocket &socket)
-{
- Value *any_value = value_per_output_id_[socket.id()];
- if (any_value == nullptr) {
- const CPPType &type = socket.data_type().vector_base_type();
- GVectorArray *vector_array = new GVectorArray(type, min_array_size_);
-
- auto *value =
- allocator_.construct<OwnVectorValue>(*vector_array, socket.targets().size()).release();
- value_per_output_id_[socket.id()] = value;
-
- return *value->vector_array;
- }
-
- BLI_assert(any_value->type == ValueType::OutputVector);
- return *static_cast<OutputVectorValue *>(any_value)->vector_array;
-}
-
-GVectorArray &MFNetworkEvaluationStorage::get_vector_output__single(const MFOutputSocket &socket)
-{
- Value *any_value = value_per_output_id_[socket.id()];
- if (any_value == nullptr) {
- const CPPType &type = socket.data_type().vector_base_type();
- GVectorArray *vector_array = new GVectorArray(type, 1);
-
- auto *value =
- allocator_.construct<OwnVectorValue>(*vector_array, socket.targets().size()).release();
- value_per_output_id_[socket.id()] = value;
-
- return *value->vector_array;
- }
-
- BLI_assert(any_value->type == ValueType::OutputVector);
- GVectorArray &vector_array = *static_cast<OutputVectorValue *>(any_value)->vector_array;
- BLI_assert(vector_array.size() == 1);
- return vector_array;
-}
-
-GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__full(const MFInputSocket &input,
- const MFOutputSocket &output,
- ResourceScope &scope)
-{
- const MFOutputSocket &from = *input.origin();
- const MFOutputSocket &to = output;
- const CPPType &type = from.data_type().single_type();
-
- Value *from_any_value = value_per_output_id_[from.id()];
- Value *to_any_value = value_per_output_id_[to.id()];
- BLI_assert(from_any_value != nullptr);
- BLI_assert(type == to.data_type().single_type());
-
- if (to_any_value != nullptr) {
- BLI_assert(to_any_value->type == ValueType::OutputSingle);
- GMutableSpan span = static_cast<OutputSingleValue *>(to_any_value)->span;
- const GVArray &virtual_array = this->get_single_input__full(input, scope);
- virtual_array.materialize_to_uninitialized(mask_, span.data());
- return span;
- }
-
- if (from_any_value->type == ValueType::OwnSingle) {
- OwnSingleValue *value = static_cast<OwnSingleValue *>(from_any_value);
- if (value->max_remaining_users == 1 && !value->is_single_allocated) {
- value_per_output_id_[to.id()] = value;
- value_per_output_id_[from.id()] = nullptr;
- value->max_remaining_users = to.targets().size();
- return value->span;
- }
- }
-
- const GVArray &virtual_array = this->get_single_input__full(input, scope);
- void *new_buffer = MEM_mallocN_aligned(min_array_size_ * type.size(), type.alignment(), AT);
- GMutableSpan new_array_ref(type, new_buffer, min_array_size_);
- virtual_array.materialize_to_uninitialized(mask_, new_array_ref.data());
-
- OwnSingleValue *new_value =
- allocator_.construct<OwnSingleValue>(new_array_ref, to.targets().size(), false).release();
- value_per_output_id_[to.id()] = new_value;
- return new_array_ref;
-}
-
-GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__single(const MFInputSocket &input,
- const MFOutputSocket &output,
- ResourceScope &scope)
-{
- const MFOutputSocket &from = *input.origin();
- const MFOutputSocket &to = output;
- const CPPType &type = from.data_type().single_type();
-
- Value *from_any_value = value_per_output_id_[from.id()];
- Value *to_any_value = value_per_output_id_[to.id()];
- BLI_assert(from_any_value != nullptr);
- BLI_assert(type == to.data_type().single_type());
-
- if (to_any_value != nullptr) {
- BLI_assert(to_any_value->type == ValueType::OutputSingle);
- GMutableSpan span = static_cast<OutputSingleValue *>(to_any_value)->span;
- BLI_assert(span.size() == 1);
- const GVArray &virtual_array = this->get_single_input__single(input, scope);
- virtual_array.get_single_to_uninitialized(span[0]);
- return span;
- }
-
- if (from_any_value->type == ValueType::OwnSingle) {
- OwnSingleValue *value = static_cast<OwnSingleValue *>(from_any_value);
- if (value->max_remaining_users == 1) {
- value_per_output_id_[to.id()] = value;
- value_per_output_id_[from.id()] = nullptr;
- value->max_remaining_users = to.targets().size();
- BLI_assert(value->span.size() == 1);
- return value->span;
- }
- }
-
- const GVArray &virtual_array = this->get_single_input__single(input, scope);
-
- void *new_buffer = allocator_.allocate(type.size(), type.alignment());
- virtual_array.get_single_to_uninitialized(new_buffer);
- GMutableSpan new_array_ref(type, new_buffer, 1);
-
- OwnSingleValue *new_value =
- allocator_.construct<OwnSingleValue>(new_array_ref, to.targets().size(), true).release();
- value_per_output_id_[to.id()] = new_value;
- return new_array_ref;
-}
-
-GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__full(const MFInputSocket &input,
- const MFOutputSocket &output,
- ResourceScope &scope)
-{
- const MFOutputSocket &from = *input.origin();
- const MFOutputSocket &to = output;
- const CPPType &base_type = from.data_type().vector_base_type();
-
- Value *from_any_value = value_per_output_id_[from.id()];
- Value *to_any_value = value_per_output_id_[to.id()];
- BLI_assert(from_any_value != nullptr);
- BLI_assert(base_type == to.data_type().vector_base_type());
-
- if (to_any_value != nullptr) {
- BLI_assert(to_any_value->type == ValueType::OutputVector);
- GVectorArray &vector_array = *static_cast<OutputVectorValue *>(to_any_value)->vector_array;
- const GVVectorArray &virtual_vector_array = this->get_vector_input__full(input, scope);
- vector_array.extend(mask_, virtual_vector_array);
- return vector_array;
- }
-
- if (from_any_value->type == ValueType::OwnVector) {
- OwnVectorValue *value = static_cast<OwnVectorValue *>(from_any_value);
- if (value->max_remaining_users == 1) {
- value_per_output_id_[to.id()] = value;
- value_per_output_id_[from.id()] = nullptr;
- value->max_remaining_users = to.targets().size();
- return *value->vector_array;
- }
- }
-
- const GVVectorArray &virtual_vector_array = this->get_vector_input__full(input, scope);
-
- GVectorArray *new_vector_array = new GVectorArray(base_type, min_array_size_);
- new_vector_array->extend(mask_, virtual_vector_array);
-
- OwnVectorValue *new_value =
- allocator_.construct<OwnVectorValue>(*new_vector_array, to.targets().size()).release();
- value_per_output_id_[to.id()] = new_value;
-
- return *new_vector_array;
-}
-
-GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__single(const MFInputSocket &input,
- const MFOutputSocket &output,
- ResourceScope &scope)
-{
- const MFOutputSocket &from = *input.origin();
- const MFOutputSocket &to = output;
- const CPPType &base_type = from.data_type().vector_base_type();
-
- Value *from_any_value = value_per_output_id_[from.id()];
- Value *to_any_value = value_per_output_id_[to.id()];
- BLI_assert(from_any_value != nullptr);
- BLI_assert(base_type == to.data_type().vector_base_type());
-
- if (to_any_value != nullptr) {
- BLI_assert(to_any_value->type == ValueType::OutputVector);
- GVectorArray &vector_array = *static_cast<OutputVectorValue *>(to_any_value)->vector_array;
- BLI_assert(vector_array.size() == 1);
- const GVVectorArray &virtual_vector_array = this->get_vector_input__single(input, scope);
- vector_array.extend({0}, virtual_vector_array);
- return vector_array;
- }
-
- if (from_any_value->type == ValueType::OwnVector) {
- OwnVectorValue *value = static_cast<OwnVectorValue *>(from_any_value);
- if (value->max_remaining_users == 1) {
- value_per_output_id_[to.id()] = value;
- value_per_output_id_[from.id()] = nullptr;
- value->max_remaining_users = to.targets().size();
- return *value->vector_array;
- }
- }
-
- const GVVectorArray &virtual_vector_array = this->get_vector_input__single(input, scope);
-
- GVectorArray *new_vector_array = new GVectorArray(base_type, 1);
- new_vector_array->extend({0}, virtual_vector_array);
-
- OwnVectorValue *new_value =
- allocator_.construct<OwnVectorValue>(*new_vector_array, to.targets().size()).release();
- value_per_output_id_[to.id()] = new_value;
- return *new_vector_array;
-}
-
-const GVArray &MFNetworkEvaluationStorage::get_single_input__full(const MFInputSocket &socket,
- ResourceScope &scope)
-{
- const MFOutputSocket &origin = *socket.origin();
- Value *any_value = value_per_output_id_[origin.id()];
- BLI_assert(any_value != nullptr);
-
- if (any_value->type == ValueType::OwnSingle) {
- OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value);
- if (value->is_single_allocated) {
- return scope.construct<GVArray_For_SingleValueRef>(
- __func__, value->span.type(), min_array_size_, value->span.data());
- }
-
- return scope.construct<GVArray_For_GSpan>(__func__, value->span);
- }
- if (any_value->type == ValueType::InputSingle) {
- InputSingleValue *value = static_cast<InputSingleValue *>(any_value);
- return value->virtual_array;
- }
- if (any_value->type == ValueType::OutputSingle) {
- OutputSingleValue *value = static_cast<OutputSingleValue *>(any_value);
- BLI_assert(value->is_computed);
- return scope.construct<GVArray_For_GSpan>(__func__, value->span);
- }
-
- BLI_assert(false);
- return scope.construct<GVArray_For_Empty>(__func__, CPPType::get<float>());
-}
-
-const GVArray &MFNetworkEvaluationStorage::get_single_input__single(const MFInputSocket &socket,
- ResourceScope &scope)
-{
- const MFOutputSocket &origin = *socket.origin();
- Value *any_value = value_per_output_id_[origin.id()];
- BLI_assert(any_value != nullptr);
-
- if (any_value->type == ValueType::OwnSingle) {
- OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value);
- BLI_assert(value->span.size() == 1);
- return scope.construct<GVArray_For_GSpan>(__func__, value->span);
- }
- if (any_value->type == ValueType::InputSingle) {
- InputSingleValue *value = static_cast<InputSingleValue *>(any_value);
- BLI_assert(value->virtual_array.is_single());
- return value->virtual_array;
- }
- if (any_value->type == ValueType::OutputSingle) {
- OutputSingleValue *value = static_cast<OutputSingleValue *>(any_value);
- BLI_assert(value->is_computed);
- BLI_assert(value->span.size() == 1);
- return scope.construct<GVArray_For_GSpan>(__func__, value->span);
- }
-
- BLI_assert(false);
- return scope.construct<GVArray_For_Empty>(__func__, CPPType::get<float>());
-}
-
-const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__full(
- const MFInputSocket &socket, ResourceScope &scope)
-{
- const MFOutputSocket &origin = *socket.origin();
- Value *any_value = value_per_output_id_[origin.id()];
- BLI_assert(any_value != nullptr);
-
- if (any_value->type == ValueType::OwnVector) {
- OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value);
- if (value->vector_array->size() == 1) {
- GSpan span = (*value->vector_array)[0];
- return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, span, min_array_size_);
- }
-
- return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array);
- }
- if (any_value->type == ValueType::InputVector) {
- InputVectorValue *value = static_cast<InputVectorValue *>(any_value);
- return value->virtual_vector_array;
- }
- if (any_value->type == ValueType::OutputVector) {
- OutputVectorValue *value = static_cast<OutputVectorValue *>(any_value);
- return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array);
- }
-
- BLI_assert(false);
- return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0);
-}
-
-const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__single(
- const MFInputSocket &socket, ResourceScope &scope)
-{
- const MFOutputSocket &origin = *socket.origin();
- Value *any_value = value_per_output_id_[origin.id()];
- BLI_assert(any_value != nullptr);
-
- if (any_value->type == ValueType::OwnVector) {
- OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value);
- BLI_assert(value->vector_array->size() == 1);
- return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array);
- }
- if (any_value->type == ValueType::InputVector) {
- InputVectorValue *value = static_cast<InputVectorValue *>(any_value);
- BLI_assert(value->virtual_vector_array.is_single_vector());
- return value->virtual_vector_array;
- }
- if (any_value->type == ValueType::OutputVector) {
- OutputVectorValue *value = static_cast<OutputVectorValue *>(any_value);
- BLI_assert(value->vector_array->size() == 1);
- return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array);
- }
-
- BLI_assert(false);
- return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0);
-}
-
-/** \} */
-
-} // namespace blender::fn
diff --git a/source/blender/functions/intern/multi_function_network_optimization.cc b/source/blender/functions/intern/multi_function_network_optimization.cc
deleted file mode 100644
index 0f65d320f62..00000000000
--- a/source/blender/functions/intern/multi_function_network_optimization.cc
+++ /dev/null
@@ -1,501 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-/** \file
- * \ingroup fn
- */
-
-/* Used to check if two multi-functions have the exact same type. */
-#include <typeinfo>
-
-#include "FN_multi_function_builder.hh"
-#include "FN_multi_function_network_evaluation.hh"
-#include "FN_multi_function_network_optimization.hh"
-
-#include "BLI_disjoint_set.hh"
-#include "BLI_ghash.h"
-#include "BLI_map.hh"
-#include "BLI_multi_value_map.hh"
-#include "BLI_rand.h"
-#include "BLI_stack.hh"
-
-namespace blender::fn::mf_network_optimization {
-
-/* -------------------------------------------------------------------- */
-/** \name Utility functions to find nodes in a network.
- * \{ */
-
-static bool set_tag_and_check_if_modified(bool &tag, bool new_value)
-{
- if (tag != new_value) {
- tag = new_value;
- return true;
- }
-
- return false;
-}
-
-static Array<bool> mask_nodes_to_the_left(MFNetwork &network, Span<MFNode *> nodes)
-{
- Array<bool> is_to_the_left(network.node_id_amount(), false);
- Stack<MFNode *> nodes_to_check;
-
- for (MFNode *node : nodes) {
- is_to_the_left[node->id()] = true;
- nodes_to_check.push(node);
- }
-
- while (!nodes_to_check.is_empty()) {
- MFNode &node = *nodes_to_check.pop();
-
- for (MFInputSocket *input_socket : node.inputs()) {
- MFOutputSocket *origin = input_socket->origin();
- if (origin != nullptr) {
- MFNode &origin_node = origin->node();
- if (set_tag_and_check_if_modified(is_to_the_left[origin_node.id()], true)) {
- nodes_to_check.push(&origin_node);
- }
- }
- }
- }
-
- return is_to_the_left;
-}
-
-static Array<bool> mask_nodes_to_the_right(MFNetwork &network, Span<MFNode *> nodes)
-{
- Array<bool> is_to_the_right(network.node_id_amount(), false);
- Stack<MFNode *> nodes_to_check;
-
- for (MFNode *node : nodes) {
- is_to_the_right[node->id()] = true;
- nodes_to_check.push(node);
- }
-
- while (!nodes_to_check.is_empty()) {
- MFNode &node = *nodes_to_check.pop();
-
- for (MFOutputSocket *output_socket : node.outputs()) {
- for (MFInputSocket *target_socket : output_socket->targets()) {
- MFNode &target_node = target_socket->node();
- if (set_tag_and_check_if_modified(is_to_the_right[target_node.id()], true)) {
- nodes_to_check.push(&target_node);
- }
- }
- }
- }
-
- return is_to_the_right;
-}
-
-static Vector<MFNode *> find_nodes_based_on_mask(MFNetwork &network,
- Span<bool> id_mask,
- bool mask_value)
-{
- Vector<MFNode *> nodes;
- for (int id : id_mask.index_range()) {
- if (id_mask[id] == mask_value) {
- MFNode *node = network.node_or_null_by_id(id);
- if (node != nullptr) {
- nodes.append(node);
- }
- }
- }
- return nodes;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Dead Node Removal
- * \{ */
-
-/**
- * Unused nodes are all those nodes that no dummy node depends upon.
- */
-void dead_node_removal(MFNetwork &network)
-{
- Array<bool> node_is_used_mask = mask_nodes_to_the_left(network,
- network.dummy_nodes().cast<MFNode *>());
- Vector<MFNode *> nodes_to_remove = find_nodes_based_on_mask(network, node_is_used_mask, false);
- network.remove(nodes_to_remove);
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Constant Folding
- * \{ */
-
-static bool function_node_can_be_constant(MFFunctionNode *node)
-{
- if (node->has_unlinked_inputs()) {
- return false;
- }
- if (node->function().depends_on_context()) {
- return false;
- }
- return true;
-}
-
-static Vector<MFNode *> find_non_constant_nodes(MFNetwork &network)
-{
- Vector<MFNode *> non_constant_nodes;
- non_constant_nodes.extend(network.dummy_nodes().cast<MFNode *>());
-
- for (MFFunctionNode *node : network.function_nodes()) {
- if (!function_node_can_be_constant(node)) {
- non_constant_nodes.append(node);
- }
- }
- return non_constant_nodes;
-}
-
-static bool output_has_non_constant_target_node(MFOutputSocket *output_socket,
- Span<bool> is_not_constant_mask)
-{
- for (MFInputSocket *target_socket : output_socket->targets()) {
- MFNode &target_node = target_socket->node();
- bool target_is_not_constant = is_not_constant_mask[target_node.id()];
- if (target_is_not_constant) {
- return true;
- }
- }
- return false;
-}
-
-static MFInputSocket *try_find_dummy_target_socket(MFOutputSocket *output_socket)
-{
- for (MFInputSocket *target_socket : output_socket->targets()) {
- if (target_socket->node().is_dummy()) {
- return target_socket;
- }
- }
- return nullptr;
-}
-
-static Vector<MFInputSocket *> find_constant_inputs_to_fold(
- MFNetwork &network, Vector<MFDummyNode *> &r_temporary_nodes)
-{
- Vector<MFNode *> non_constant_nodes = find_non_constant_nodes(network);
- Array<bool> is_not_constant_mask = mask_nodes_to_the_right(network, non_constant_nodes);
- Vector<MFNode *> constant_nodes = find_nodes_based_on_mask(network, is_not_constant_mask, false);
-
- Vector<MFInputSocket *> sockets_to_compute;
- for (MFNode *node : constant_nodes) {
- if (node->inputs().size() == 0) {
- continue;
- }
-
- for (MFOutputSocket *output_socket : node->outputs()) {
- MFDataType data_type = output_socket->data_type();
- if (output_has_non_constant_target_node(output_socket, is_not_constant_mask)) {
- MFInputSocket *dummy_target = try_find_dummy_target_socket(output_socket);
- if (dummy_target == nullptr) {
- dummy_target = &network.add_output("Dummy", data_type);
- network.add_link(*output_socket, *dummy_target);
- r_temporary_nodes.append(&dummy_target->node().as_dummy());
- }
-
- sockets_to_compute.append(dummy_target);
- }
- }
- }
- return sockets_to_compute;
-}
-
-static void prepare_params_for_constant_folding(const MultiFunction &network_fn,
- MFParamsBuilder &params,
- ResourceScope &scope)
-{
- for (int param_index : network_fn.param_indices()) {
- MFParamType param_type = network_fn.param_type(param_index);
- MFDataType data_type = param_type.data_type();
-
- switch (data_type.category()) {
- case MFDataType::Single: {
- /* Allocates memory for a single constant folded value. */
- const CPPType &cpp_type = data_type.single_type();
- void *buffer = scope.linear_allocator().allocate(cpp_type.size(), cpp_type.alignment());
- GMutableSpan array{cpp_type, buffer, 1};
- params.add_uninitialized_single_output(array);
- break;
- }
- case MFDataType::Vector: {
- /* Allocates memory for a constant folded vector. */
- const CPPType &cpp_type = data_type.vector_base_type();
- GVectorArray &vector_array = scope.construct<GVectorArray>(AT, cpp_type, 1);
- params.add_vector_output(vector_array);
- break;
- }
- }
- }
-}
-
-static Array<MFOutputSocket *> add_constant_folded_sockets(const MultiFunction &network_fn,
- MFParamsBuilder &params,
- ResourceScope &scope,
- MFNetwork &network)
-{
- Array<MFOutputSocket *> folded_sockets{network_fn.param_indices().size(), nullptr};
-
- for (int param_index : network_fn.param_indices()) {
- MFParamType param_type = network_fn.param_type(param_index);
- MFDataType data_type = param_type.data_type();
-
- const MultiFunction *constant_fn = nullptr;
-
- switch (data_type.category()) {
- case MFDataType::Single: {
- const CPPType &cpp_type = data_type.single_type();
- GMutableSpan array = params.computed_array(param_index);
- void *buffer = array.data();
- scope.add(buffer, array.type().members().destruct, AT);
-
- constant_fn = &scope.construct<CustomMF_GenericConstant>(AT, cpp_type, buffer);
- break;
- }
- case MFDataType::Vector: {
- GVectorArray &vector_array = params.computed_vector_array(param_index);
- GSpan array = vector_array[0];
- constant_fn = &scope.construct<CustomMF_GenericConstantArray>(AT, array);
- break;
- }
- }
-
- MFFunctionNode &folded_node = network.add_function(*constant_fn);
- folded_sockets[param_index] = &folded_node.output(0);
- }
- return folded_sockets;
-}
-
-static Array<MFOutputSocket *> compute_constant_sockets_and_add_folded_nodes(
- MFNetwork &network, Span<const MFInputSocket *> sockets_to_compute, ResourceScope &scope)
-{
- MFNetworkEvaluator network_fn{{}, sockets_to_compute};
-
- MFContextBuilder context;
- MFParamsBuilder params{network_fn, 1};
- prepare_params_for_constant_folding(network_fn, params, scope);
- network_fn.call({0}, params, context);
- return add_constant_folded_sockets(network_fn, params, scope, network);
-}
-
-class MyClass {
- MFDummyNode node;
-};
-
-/**
- * Find function nodes that always output the same value and replace those with constant nodes.
- */
-void constant_folding(MFNetwork &network, ResourceScope &scope)
-{
- Vector<MFDummyNode *> temporary_nodes;
- Vector<MFInputSocket *> inputs_to_fold = find_constant_inputs_to_fold(network, temporary_nodes);
- if (inputs_to_fold.size() == 0) {
- return;
- }
-
- Array<MFOutputSocket *> folded_sockets = compute_constant_sockets_and_add_folded_nodes(
- network, inputs_to_fold, scope);
-
- for (int i : inputs_to_fold.index_range()) {
- MFOutputSocket &original_socket = *inputs_to_fold[i]->origin();
- network.relink(original_socket, *folded_sockets[i]);
- }
-
- network.remove(temporary_nodes.as_span().cast<MFNode *>());
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Common Sub-network Elimination
- * \{ */
-
-static uint64_t compute_node_hash(MFFunctionNode &node, RNG *rng, Span<uint64_t> node_hashes)
-{
- if (node.function().depends_on_context()) {
- return BLI_rng_get_uint(rng);
- }
- if (node.has_unlinked_inputs()) {
- return BLI_rng_get_uint(rng);
- }
-
- uint64_t combined_inputs_hash = 394659347u;
- for (MFInputSocket *input_socket : node.inputs()) {
- MFOutputSocket *origin_socket = input_socket->origin();
- uint64_t input_hash = BLI_ghashutil_combine_hash(node_hashes[origin_socket->node().id()],
- origin_socket->index());
- combined_inputs_hash = BLI_ghashutil_combine_hash(combined_inputs_hash, input_hash);
- }
-
- uint64_t function_hash = node.function().hash();
- uint64_t node_hash = BLI_ghashutil_combine_hash(combined_inputs_hash, function_hash);
- return node_hash;
-}
-
-/**
- * Produces a hash for every node. Two nodes with the same hash should have a high probability of
- * outputting the same values.
- */
-static Array<uint64_t> compute_node_hashes(MFNetwork &network)
-{
- RNG *rng = BLI_rng_new(0);
- Array<uint64_t> node_hashes(network.node_id_amount());
- Array<bool> node_is_hashed(network.node_id_amount(), false);
-
- /* No dummy nodes are not assumed to output the same values. */
- for (MFDummyNode *node : network.dummy_nodes()) {
- uint64_t node_hash = BLI_rng_get_uint(rng);
- node_hashes[node->id()] = node_hash;
- node_is_hashed[node->id()] = true;
- }
-
- Stack<MFFunctionNode *> nodes_to_check;
- nodes_to_check.push_multiple(network.function_nodes());
-
- while (!nodes_to_check.is_empty()) {
- MFFunctionNode &node = *nodes_to_check.peek();
- if (node_is_hashed[node.id()]) {
- nodes_to_check.pop();
- continue;
- }
-
- /* Make sure that origin nodes are hashed first. */
- bool all_dependencies_ready = true;
- for (MFInputSocket *input_socket : node.inputs()) {
- MFOutputSocket *origin_socket = input_socket->origin();
- if (origin_socket != nullptr) {
- MFNode &origin_node = origin_socket->node();
- if (!node_is_hashed[origin_node.id()]) {
- all_dependencies_ready = false;
- nodes_to_check.push(&origin_node.as_function());
- }
- }
- }
- if (!all_dependencies_ready) {
- continue;
- }
-
- uint64_t node_hash = compute_node_hash(node, rng, node_hashes);
- node_hashes[node.id()] = node_hash;
- node_is_hashed[node.id()] = true;
- nodes_to_check.pop();
- }
-
- BLI_rng_free(rng);
- return node_hashes;
-}
-
-static MultiValueMap<uint64_t, MFNode *> group_nodes_by_hash(MFNetwork &network,
- Span<uint64_t> node_hashes)
-{
- MultiValueMap<uint64_t, MFNode *> nodes_by_hash;
- for (int id : IndexRange(network.node_id_amount())) {
- MFNode *node = network.node_or_null_by_id(id);
- if (node != nullptr) {
- uint64_t node_hash = node_hashes[id];
- nodes_by_hash.add(node_hash, node);
- }
- }
- return nodes_by_hash;
-}
-
-static bool functions_are_equal(const MultiFunction &a, const MultiFunction &b)
-{
- if (&a == &b) {
- return true;
- }
- if (typeid(a) == typeid(b)) {
- return a.equals(b);
- }
- return false;
-}
-
-static bool nodes_output_same_values(DisjointSet &cache, const MFNode &a, const MFNode &b)
-{
- if (cache.in_same_set(a.id(), b.id())) {
- return true;
- }
-
- if (a.is_dummy() || b.is_dummy()) {
- return false;
- }
- if (!functions_are_equal(a.as_function().function(), b.as_function().function())) {
- return false;
- }
- for (int i : a.inputs().index_range()) {
- const MFOutputSocket *origin_a = a.input(i).origin();
- const MFOutputSocket *origin_b = b.input(i).origin();
- if (origin_a == nullptr || origin_b == nullptr) {
- return false;
- }
- if (!nodes_output_same_values(cache, origin_a->node(), origin_b->node())) {
- return false;
- }
- }
-
- cache.join(a.id(), b.id());
- return true;
-}
-
-static void relink_duplicate_nodes(MFNetwork &network,
- MultiValueMap<uint64_t, MFNode *> &nodes_by_hash)
-{
- DisjointSet same_node_cache{network.node_id_amount()};
-
- for (Span<MFNode *> nodes_with_same_hash : nodes_by_hash.values()) {
- if (nodes_with_same_hash.size() <= 1) {
- continue;
- }
-
- Vector<MFNode *, 16> nodes_to_check = nodes_with_same_hash;
- while (nodes_to_check.size() >= 2) {
- Vector<MFNode *, 16> remaining_nodes;
-
- MFNode &deduplicated_node = *nodes_to_check[0];
- for (MFNode *node : nodes_to_check.as_span().drop_front(1)) {
- /* This is true with fairly high probability, but hash collisions can happen. So we have to
- * check if the node actually output the same values. */
- if (nodes_output_same_values(same_node_cache, deduplicated_node, *node)) {
- for (int i : deduplicated_node.outputs().index_range()) {
- network.relink(node->output(i), deduplicated_node.output(i));
- }
- }
- else {
- remaining_nodes.append(node);
- }
- }
- nodes_to_check = std::move(remaining_nodes);
- }
- }
-}
-
-/**
- * Tries to detect duplicate sub-networks and eliminates them. This can help quite a lot when node
- * groups were used to create the network.
- */
-void common_subnetwork_elimination(MFNetwork &network)
-{
- Array<uint64_t> node_hashes = compute_node_hashes(network);
- MultiValueMap<uint64_t, MFNode *> nodes_by_hash = group_nodes_by_hash(network, node_hashes);
- relink_duplicate_nodes(network, nodes_by_hash);
-}
-
-/** \} */
-
-} // namespace blender::fn::mf_network_optimization
diff --git a/source/blender/functions/tests/FN_multi_function_network_test.cc b/source/blender/functions/tests/FN_multi_function_network_test.cc
deleted file mode 100644
index 7b9738e5ca4..00000000000
--- a/source/blender/functions/tests/FN_multi_function_network_test.cc
+++ /dev/null
@@ -1,280 +0,0 @@
-/* Apache License, Version 2.0 */
-
-#include "testing/testing.h"
-
-#include "FN_multi_function_builder.hh"
-#include "FN_multi_function_network.hh"
-#include "FN_multi_function_network_evaluation.hh"
-
-namespace blender::fn::tests {
-namespace {
-
-TEST(multi_function_network, Test1)
-{
- CustomMF_SI_SO<int, int> add_10_fn("add 10", [](int value) { return value + 10; });
- CustomMF_SI_SI_SO<int, int, int> multiply_fn("multiply", [](int a, int b) { return a * b; });
-
- MFNetwork network;
-
- MFNode &node1 = network.add_function(add_10_fn);
- MFNode &node2 = network.add_function(multiply_fn);
- MFOutputSocket &input_socket = network.add_input("Input", MFDataType::ForSingle<int>());
- MFInputSocket &output_socket = network.add_output("Output", MFDataType::ForSingle<int>());
- network.add_link(node1.output(0), node2.input(0));
- network.add_link(node1.output(0), node2.input(1));
- network.add_link(node2.output(0), output_socket);
- network.add_link(input_socket, node1.input(0));
-
- MFNetworkEvaluator network_fn{{&input_socket}, {&output_socket}};
-
- {
- Array<int> values = {4, 6, 1, 2, 0};
- Array<int> results(values.size(), 0);
-
- MFParamsBuilder params(network_fn, values.size());
- params.add_readonly_single_input(values.as_span());
- params.add_uninitialized_single_output(results.as_mutable_span());
-
- MFContextBuilder context;
-
- network_fn.call({0, 2, 3, 4}, params, context);
-
- EXPECT_EQ(results[0], 14 * 14);
- EXPECT_EQ(results[1], 0);
- EXPECT_EQ(results[2], 11 * 11);
- EXPECT_EQ(results[3], 12 * 12);
- EXPECT_EQ(results[4], 10 * 10);
- }
- {
- int value = 3;
- Array<int> results(5, 0);
-
- MFParamsBuilder params(network_fn, results.size());
- params.add_readonly_single_input(&value);
- params.add_uninitialized_single_output(results.as_mutable_span());
-
- MFContextBuilder context;
-
- network_fn.call({1, 2, 4}, params, context);
-
- EXPECT_EQ(results[0], 0);
- EXPECT_EQ(results[1], 13 * 13);
- EXPECT_EQ(results[2], 13 * 13);
- EXPECT_EQ(results[3], 0);
- EXPECT_EQ(results[4], 13 * 13);
- }
-}
-
-class ConcatVectorsFunction : public MultiFunction {
- public:
- ConcatVectorsFunction()
- {
- static MFSignature signature = create_signature();
- this->set_signature(&signature);
- }
-
- static MFSignature create_signature()
- {
- MFSignatureBuilder signature{"Concat Vectors"};
- signature.vector_mutable<int>("A");
- signature.vector_input<int>("B");
- return signature.build();
- }
-
- void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
- {
- GVectorArray &a = params.vector_mutable(0);
- const GVVectorArray &b = params.readonly_vector_input(1);
- a.extend(mask, b);
- }
-};
-
-class AppendFunction : public MultiFunction {
- public:
- AppendFunction()
- {
- static MFSignature signature = create_signature();
- this->set_signature(&signature);
- }
-
- static MFSignature create_signature()
- {
- MFSignatureBuilder signature{"Append"};
- signature.vector_mutable<int>("Vector");
- signature.single_input<int>("Value");
- return signature.build();
- }
-
- void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
- {
- GVectorArray_TypedMutableRef<int> vectors = params.vector_mutable<int>(0);
- const VArray<int> &values = params.readonly_single_input<int>(1);
-
- for (int64_t i : mask) {
- vectors.append(i, values[i]);
- }
- }
-};
-
-class SumVectorFunction : public MultiFunction {
- public:
- SumVectorFunction()
- {
- static MFSignature signature = create_signature();
- this->set_signature(&signature);
- }
-
- static MFSignature create_signature()
- {
- MFSignatureBuilder signature{"Sum Vectors"};
- signature.vector_input<int>("Vector");
- signature.single_output<int>("Sum");
- return signature.build();
- }
-
- void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
- {
- const VVectorArray<int> &vectors = params.readonly_vector_input<int>(0);
- MutableSpan<int> sums = params.uninitialized_single_output<int>(1);
-
- for (int64_t i : mask) {
- int sum = 0;
- for (int j : IndexRange(vectors.get_vector_size(i))) {
- sum += vectors.get_vector_element(i, j);
- }
- sums[i] = sum;
- }
- }
-};
-
-class CreateRangeFunction : public MultiFunction {
- public:
- CreateRangeFunction()
- {
- static MFSignature signature = create_signature();
- this->set_signature(&signature);
- }
-
- static MFSignature create_signature()
- {
- MFSignatureBuilder signature{"Create Range"};
- signature.single_input<int>("Size");
- signature.vector_output<int>("Range");
- return signature.build();
- }
-
- void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
- {
- const VArray<int> &sizes = params.readonly_single_input<int>(0, "Size");
- GVectorArray_TypedMutableRef<int> ranges = params.vector_output<int>(1, "Range");
-
- for (int64_t i : mask) {
- int size = sizes[i];
- for (int j : IndexRange(size)) {
- ranges.append(i, j);
- }
- }
- }
-};
-
-TEST(multi_function_network, Test2)
-{
- CustomMF_SI_SO<int, int> add_3_fn("add 3", [](int value) { return value + 3; });
-
- ConcatVectorsFunction concat_vectors_fn;
- AppendFunction append_fn;
- SumVectorFunction sum_fn;
- CreateRangeFunction create_range_fn;
-
- MFNetwork network;
-
- MFOutputSocket &input1 = network.add_input("Input 1", MFDataType::ForVector<int>());
- MFOutputSocket &input2 = network.add_input("Input 2", MFDataType::ForSingle<int>());
- MFInputSocket &output1 = network.add_output("Output 1", MFDataType::ForVector<int>());
- MFInputSocket &output2 = network.add_output("Output 2", MFDataType::ForSingle<int>());
-
- MFNode &node1 = network.add_function(add_3_fn);
- MFNode &node2 = network.add_function(create_range_fn);
- MFNode &node3 = network.add_function(concat_vectors_fn);
- MFNode &node4 = network.add_function(sum_fn);
- MFNode &node5 = network.add_function(append_fn);
- MFNode &node6 = network.add_function(sum_fn);
-
- network.add_link(input2, node1.input(0));
- network.add_link(node1.output(0), node2.input(0));
- network.add_link(node2.output(0), node3.input(1));
- network.add_link(input1, node3.input(0));
- network.add_link(input1, node4.input(0));
- network.add_link(node4.output(0), node5.input(1));
- network.add_link(node3.output(0), node5.input(0));
- network.add_link(node5.output(0), node6.input(0));
- network.add_link(node3.output(0), output1);
- network.add_link(node6.output(0), output2);
-
- // std::cout << network.to_dot() << "\n\n";
-
- MFNetworkEvaluator network_fn{{&input1, &input2}, {&output1, &output2}};
-
- {
- Array<int> input_value_1 = {3, 6};
- int input_value_2 = 4;
-
- GVectorArray output_value_1(CPPType::get<int32_t>(), 5);
- Array<int> output_value_2(5, -1);
-
- MFParamsBuilder params(network_fn, 5);
- GVVectorArray_For_SingleGSpan inputs_1{input_value_1.as_span(), 5};
- params.add_readonly_vector_input(inputs_1);
- params.add_readonly_single_input(&input_value_2);
- params.add_vector_output(output_value_1);
- params.add_uninitialized_single_output(output_value_2.as_mutable_span());
-
- MFContextBuilder context;
-
- network_fn.call({1, 2, 4}, params, context);
-
- EXPECT_EQ(output_value_1[0].size(), 0);
- EXPECT_EQ(output_value_1[1].size(), 9);
- EXPECT_EQ(output_value_1[2].size(), 9);
- EXPECT_EQ(output_value_1[3].size(), 0);
- EXPECT_EQ(output_value_1[4].size(), 9);
-
- EXPECT_EQ(output_value_2[0], -1);
- EXPECT_EQ(output_value_2[1], 39);
- EXPECT_EQ(output_value_2[2], 39);
- EXPECT_EQ(output_value_2[3], -1);
- EXPECT_EQ(output_value_2[4], 39);
- }
- {
- GVectorArray input_value_1(CPPType::get<int32_t>(), 3);
- GVectorArray_TypedMutableRef<int> input_value_1_ref{input_value_1};
- input_value_1_ref.extend(0, {3, 4, 5});
- input_value_1_ref.extend(1, {1, 2});
-
- Array<int> input_value_2 = {4, 2, 3};
-
- GVectorArray output_value_1(CPPType::get<int32_t>(), 3);
- Array<int> output_value_2(3, -1);
-
- MFParamsBuilder params(network_fn, 3);
- params.add_readonly_vector_input(input_value_1);
- params.add_readonly_single_input(input_value_2.as_span());
- params.add_vector_output(output_value_1);
- params.add_uninitialized_single_output(output_value_2.as_mutable_span());
-
- MFContextBuilder context;
-
- network_fn.call({0, 1, 2}, params, context);
-
- EXPECT_EQ(output_value_1[0].size(), 10);
- EXPECT_EQ(output_value_1[1].size(), 7);
- EXPECT_EQ(output_value_1[2].size(), 6);
-
- EXPECT_EQ(output_value_2[0], 45);
- EXPECT_EQ(output_value_2[1], 16);
- EXPECT_EQ(output_value_2[2], 15);
- }
-}
-
-} // namespace
-} // namespace blender::fn::tests
diff --git a/source/blender/functions/tests/FN_multi_function_test.cc b/source/blender/functions/tests/FN_multi_function_test.cc
index 3d73e020eb2..91c72a51dd6 100644
--- a/source/blender/functions/tests/FN_multi_function_test.cc
+++ b/source/blender/functions/tests/FN_multi_function_test.cc
@@ -4,6 +4,7 @@
#include "FN_multi_function.hh"
#include "FN_multi_function_builder.hh"
+#include "FN_multi_function_test_common.hh"
namespace blender::fn::tests {
namespace {
@@ -59,33 +60,6 @@ TEST(multi_function, AddFunction)
EXPECT_EQ(output[2], 36);
}
-class AddPrefixFunction : public MultiFunction {
- public:
- AddPrefixFunction()
- {
- static MFSignature signature = create_signature();
- this->set_signature(&signature);
- }
-
- static MFSignature create_signature()
- {
- MFSignatureBuilder signature{"Add Prefix"};
- signature.single_input<std::string>("Prefix");
- signature.single_mutable<std::string>("Strings");
- return signature.build();
- }
-
- void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
- {
- const VArray<std::string> &prefixes = params.readonly_single_input<std::string>(0, "Prefix");
- MutableSpan<std::string> strings = params.single_mutable<std::string>(1, "Strings");
-
- for (int64_t i : mask) {
- strings[i] = prefixes[i] + strings[i];
- }
- }
-};
-
TEST(multi_function, AddPrefixFunction)
{
AddPrefixFunction fn;
@@ -113,43 +87,13 @@ TEST(multi_function, AddPrefixFunction)
EXPECT_EQ(strings[3], "ABAnother much longer string to trigger an allocation");
}
-class CreateRangeFunction : public MultiFunction {
- public:
- CreateRangeFunction()
- {
- static MFSignature signature = create_signature();
- this->set_signature(&signature);
- }
-
- static MFSignature create_signature()
- {
- MFSignatureBuilder signature{"Create Range"};
- signature.single_input<uint>("Size");
- signature.vector_output<uint>("Range");
- return signature.build();
- }
-
- void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
- {
- const VArray<uint> &sizes = params.readonly_single_input<uint>(0, "Size");
- GVectorArray &ranges = params.vector_output(1, "Range");
-
- for (int64_t i : mask) {
- uint size = sizes[i];
- for (uint j : IndexRange(size)) {
- ranges.append(i, &j);
- }
- }
- }
-};
-
TEST(multi_function, CreateRangeFunction)
{
CreateRangeFunction fn;
- GVectorArray ranges(CPPType::get<uint>(), 5);
- GVectorArray_TypedMutableRef<uint> ranges_ref{ranges};
- Array<uint> sizes = {3, 0, 6, 1, 4};
+ GVectorArray ranges(CPPType::get<int>(), 5);
+ GVectorArray_TypedMutableRef<int> ranges_ref{ranges};
+ Array<int> sizes = {3, 0, 6, 1, 4};
MFParamsBuilder params(fn, ranges.size());
params.add_readonly_single_input(sizes.as_span());
@@ -172,34 +116,6 @@ TEST(multi_function, CreateRangeFunction)
EXPECT_EQ(ranges_ref[2][1], 1);
}
-class GenericAppendFunction : public MultiFunction {
- private:
- MFSignature signature_;
-
- public:
- GenericAppendFunction(const CPPType &type)
- {
- MFSignatureBuilder signature{"Append"};
- signature.vector_mutable("Vector", type);
- signature.single_input("Value", type);
- signature_ = signature.build();
- this->set_signature(&signature_);
- }
-
- void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
- {
- GVectorArray &vectors = params.vector_mutable(0, "Vector");
- const GVArray &values = params.readonly_single_input(1, "Value");
-
- for (int64_t i : mask) {
- BUFFER_FOR_CPP_TYPE_VALUE(values.type(), buffer);
- values.get(i, buffer);
- vectors.append(i, buffer);
- values.type().destruct(buffer);
- }
- }
-};
-
TEST(multi_function, GenericAppendFunction)
{
GenericAppendFunction fn(CPPType::get<int32_t>());
diff --git a/source/blender/functions/tests/FN_multi_function_test_common.hh b/source/blender/functions/tests/FN_multi_function_test_common.hh
new file mode 100644
index 00000000000..51c8fac8a96
--- /dev/null
+++ b/source/blender/functions/tests/FN_multi_function_test_common.hh
@@ -0,0 +1,174 @@
+/* Apache License, Version 2.0 */
+
+#include "FN_multi_function.hh"
+
+namespace blender::fn::tests {
+
+class AddPrefixFunction : public MultiFunction {
+ public:
+ AddPrefixFunction()
+ {
+ static MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static MFSignature create_signature()
+ {
+ MFSignatureBuilder signature{"Add Prefix"};
+ signature.single_input<std::string>("Prefix");
+ signature.single_mutable<std::string>("Strings");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
+ {
+ const VArray<std::string> &prefixes = params.readonly_single_input<std::string>(0, "Prefix");
+ MutableSpan<std::string> strings = params.single_mutable<std::string>(1, "Strings");
+
+ for (int64_t i : mask) {
+ strings[i] = prefixes[i] + strings[i];
+ }
+ }
+};
+
+class CreateRangeFunction : public MultiFunction {
+ public:
+ CreateRangeFunction()
+ {
+ static MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static MFSignature create_signature()
+ {
+ MFSignatureBuilder signature{"Create Range"};
+ signature.single_input<int>("Size");
+ signature.vector_output<int>("Range");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
+ {
+ const VArray<int> &sizes = params.readonly_single_input<int>(0, "Size");
+ GVectorArray &ranges = params.vector_output(1, "Range");
+
+ for (int64_t i : mask) {
+ int size = sizes[i];
+ for (int j : IndexRange(size)) {
+ ranges.append(i, &j);
+ }
+ }
+ }
+};
+
+class GenericAppendFunction : public MultiFunction {
+ private:
+ MFSignature signature_;
+
+ public:
+ GenericAppendFunction(const CPPType &type)
+ {
+ MFSignatureBuilder signature{"Append"};
+ signature.vector_mutable("Vector", type);
+ signature.single_input("Value", type);
+ signature_ = signature.build();
+ this->set_signature(&signature_);
+ }
+
+ void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
+ {
+ GVectorArray &vectors = params.vector_mutable(0, "Vector");
+ const GVArray &values = params.readonly_single_input(1, "Value");
+
+ for (int64_t i : mask) {
+ BUFFER_FOR_CPP_TYPE_VALUE(values.type(), buffer);
+ values.get(i, buffer);
+ vectors.append(i, buffer);
+ values.type().destruct(buffer);
+ }
+ }
+};
+
+class ConcatVectorsFunction : public MultiFunction {
+ public:
+ ConcatVectorsFunction()
+ {
+ static MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static MFSignature create_signature()
+ {
+ MFSignatureBuilder signature{"Concat Vectors"};
+ signature.vector_mutable<int>("A");
+ signature.vector_input<int>("B");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
+ {
+ GVectorArray &a = params.vector_mutable(0);
+ const GVVectorArray &b = params.readonly_vector_input(1);
+ a.extend(mask, b);
+ }
+};
+
+class AppendFunction : public MultiFunction {
+ public:
+ AppendFunction()
+ {
+ static MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static MFSignature create_signature()
+ {
+ MFSignatureBuilder signature{"Append"};
+ signature.vector_mutable<int>("Vector");
+ signature.single_input<int>("Value");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
+ {
+ GVectorArray_TypedMutableRef<int> vectors = params.vector_mutable<int>(0);
+ const VArray<int> &values = params.readonly_single_input<int>(1);
+
+ for (int64_t i : mask) {
+ vectors.append(i, values[i]);
+ }
+ }
+};
+
+class SumVectorFunction : public MultiFunction {
+ public:
+ SumVectorFunction()
+ {
+ static MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static MFSignature create_signature()
+ {
+ MFSignatureBuilder signature{"Sum Vectors"};
+ signature.vector_input<int>("Vector");
+ signature.single_output<int>("Sum");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override
+ {
+ const VVectorArray<int> &vectors = params.readonly_vector_input<int>(0);
+ MutableSpan<int> sums = params.uninitialized_single_output<int>(1);
+
+ for (int64_t i : mask) {
+ int sum = 0;
+ for (int j : IndexRange(vectors.get_vector_size(i))) {
+ sum += vectors.get_vector_element(i, j);
+ }
+ sums[i] = sum;
+ }
+ }
+};
+
+} // namespace blender::fn::tests
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c
index fd94ac92bc3..857c683d95a 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c
@@ -1,6 +1,4 @@
/*
- * ***** BEGIN GPL LICENSE BLOCK *****
- *
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
@@ -12,14 +10,11 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
+ * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2017, Blender Foundation
* This is a new part of Blender
- *
- * ***** END GPL LICENSE BLOCK *****
- *
*/
/** \file
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c
index 680f5ab05ec..7ce731c0dd6 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c
@@ -308,10 +308,7 @@ static void bakeModifier(Main *UNUSED(bmain),
static void freeData(GpencilModifierData *md)
{
TintGpencilModifierData *mmd = (TintGpencilModifierData *)md;
- if (mmd->colorband) {
- MEM_freeN(mmd->colorband);
- mmd->colorband = NULL;
- }
+ MEM_SAFE_FREE(mmd->colorband);
if (mmd->curve_intensity) {
BKE_curvemapping_free(mmd->curve_intensity);
}
diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
index 9ac07b9632d..4b71011b99a 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
+++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
@@ -208,7 +208,7 @@ typedef struct LineartChainRegisterEntry {
enum eLineArtTileRecursiveLimit {
/* If tile gets this small, it's already much smaller than a pixel. No need to continue
* splitting. */
- LRT_TILE_RECURSIVE_PERSPECTIVE = 30,
+ LRT_TILE_RECURSIVE_PERSPECTIVE = 16,
/* This is a tried-and-true safe value for high poly models that also needed ortho rendering. */
LRT_TILE_RECURSIVE_ORTHO = 10,
};
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
index 8762ca1f384..99e3d59a57f 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
@@ -41,6 +41,7 @@
#include "BKE_gpencil.h"
#include "BKE_gpencil_geom.h"
#include "BKE_gpencil_modifier.h"
+#include "BKE_lib_id.h"
#include "BKE_material.h"
#include "BKE_mesh.h"
#include "BKE_object.h"
@@ -1691,8 +1692,7 @@ static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBu
}
if (obi->free_use_mesh) {
- BKE_mesh_free(obi->original_me);
- MEM_freeN(obi->original_me);
+ BKE_id_free(NULL, &obi->original_me);
}
if (rb->remove_doubles) {
@@ -1968,8 +1968,8 @@ static int lineart_usage_check(Collection *c, Object *ob, bool is_render)
if (c->gobject.first) {
if (BKE_collection_has_object(c, (Object *)(ob->id.orig_id))) {
- if ((is_render && (c->flag & COLLECTION_RESTRICT_RENDER)) ||
- ((!is_render) && (c->flag & COLLECTION_RESTRICT_VIEWPORT))) {
+ if ((is_render && (c->flag & COLLECTION_HIDE_RENDER)) ||
+ ((!is_render) && (c->flag & COLLECTION_HIDE_VIEWPORT))) {
return OBJECT_LRT_EXCLUDE;
}
if (ob->lineart.usage == OBJECT_LRT_INHERIT) {
diff --git a/source/blender/gpu/GPU_framebuffer.h b/source/blender/gpu/GPU_framebuffer.h
index 6482d9a9d3e..bf0ab3dc533 100644
--- a/source/blender/gpu/GPU_framebuffer.h
+++ b/source/blender/gpu/GPU_framebuffer.h
@@ -223,7 +223,7 @@ uint GPU_framebuffer_stack_level_get(void);
*/
GPUOffScreen *GPU_offscreen_create(
- int width, int height, bool depth, bool high_bitdepth, char err_out[256]);
+ int width, int height, bool depth, eGPUTextureFormat format, char err_out[256]);
void GPU_offscreen_free(GPUOffScreen *ofs);
void GPU_offscreen_bind(GPUOffScreen *ofs, bool save);
void GPU_offscreen_unbind(GPUOffScreen *ofs, bool restore);
diff --git a/source/blender/gpu/GPU_matrix.h b/source/blender/gpu/GPU_matrix.h
index e073263f352..edf16f04349 100644
--- a/source/blender/gpu/GPU_matrix.h
+++ b/source/blender/gpu/GPU_matrix.h
@@ -131,15 +131,11 @@ void GPU_matrix_project_2fv(const float world[3],
float r_win[2]);
bool GPU_matrix_unproject_3fv(const float win[3],
- const float model[4][4],
+ const float model_inverted[4][4],
const float proj[4][4],
const int view[4],
float r_world[3]);
-void GPU_matrix_unproject_3fv_with_precalc(const struct GPUMatrixUnproject_Precalc *unproj_precalc,
- const float win[3],
- float r_world[3]);
-
/* 2D Projection Matrix */
void GPU_matrix_ortho_2d_set(float left, float right, float bottom, float top);
diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h
index f980c8fdcd7..9a1885160b6 100644
--- a/source/blender/gpu/GPU_texture.h
+++ b/source/blender/gpu/GPU_texture.h
@@ -187,25 +187,30 @@ unsigned int GPU_texture_memory_usage_get(void);
* \a mips is the number of mip level to allocate. It must be >= 1.
*/
GPUTexture *GPU_texture_create_1d(
- const char *name, int w, int mips, eGPUTextureFormat format, const float *data);
+ const char *name, int w, int mip_len, eGPUTextureFormat format, const float *data);
GPUTexture *GPU_texture_create_1d_array(
- const char *name, int w, int h, int mips, eGPUTextureFormat format, const float *data);
+ const char *name, int w, int h, int mip_len, eGPUTextureFormat format, const float *data);
GPUTexture *GPU_texture_create_2d(
- const char *name, int w, int h, int mips, eGPUTextureFormat format, const float *data);
-GPUTexture *GPU_texture_create_2d_array(
- const char *name, int w, int h, int d, int mips, eGPUTextureFormat format, const float *data);
+ const char *name, int w, int h, int mip_len, eGPUTextureFormat format, const float *data);
+GPUTexture *GPU_texture_create_2d_array(const char *name,
+ int w,
+ int h,
+ int d,
+ int mip_len,
+ eGPUTextureFormat format,
+ const float *data);
GPUTexture *GPU_texture_create_3d(const char *name,
int w,
int h,
int d,
- int mips,
+ int mip_len,
eGPUTextureFormat texture_format,
eGPUDataFormat data_format,
const void *data);
GPUTexture *GPU_texture_create_cube(
- const char *name, int w, int mips, eGPUTextureFormat format, const float *data);
+ const char *name, int w, int mip_len, eGPUTextureFormat format, const float *data);
GPUTexture *GPU_texture_create_cube_array(
- const char *name, int w, int d, int mips, eGPUTextureFormat format, const float *data);
+ const char *name, int w, int d, int mip_len, eGPUTextureFormat format, const float *data);
/* Special textures. */
GPUTexture *GPU_texture_create_from_vertbuf(const char *name, struct GPUVertBuf *vert);
diff --git a/source/blender/gpu/intern/gpu_framebuffer.cc b/source/blender/gpu/intern/gpu_framebuffer.cc
index a6f7d43e563..9099a6e4245 100644
--- a/source/blender/gpu/intern/gpu_framebuffer.cc
+++ b/source/blender/gpu/intern/gpu_framebuffer.cc
@@ -591,7 +591,7 @@ static GPUFrameBuffer *gpu_offscreen_fb_get(GPUOffScreen *ofs)
}
GPUOffScreen *GPU_offscreen_create(
- int width, int height, bool depth, bool high_bitdepth, char err_out[256])
+ int width, int height, bool depth, eGPUTextureFormat format, char err_out[256])
{
GPUOffScreen *ofs = (GPUOffScreen *)MEM_callocN(sizeof(GPUOffScreen), __func__);
@@ -600,8 +600,7 @@ GPUOffScreen *GPU_offscreen_create(
height = max_ii(1, height);
width = max_ii(1, width);
- ofs->color = GPU_texture_create_2d(
- "ofs_color", width, height, 1, (high_bitdepth) ? GPU_RGBA16F : GPU_RGBA8, nullptr);
+ ofs->color = GPU_texture_create_2d("ofs_color", width, height, 1, format, nullptr);
if (depth) {
ofs->depth = GPU_texture_create_2d(
diff --git a/source/blender/gpu/intern/gpu_immediate.cc b/source/blender/gpu/intern/gpu_immediate.cc
index 062741a6270..cdd56a117db 100644
--- a/source/blender/gpu/intern/gpu_immediate.cc
+++ b/source/blender/gpu/intern/gpu_immediate.cc
@@ -313,7 +313,7 @@ void immAttr1f(uint attr_id, float x)
setAttrValueBit(attr_id);
float *data = (float *)(imm->vertex_data + attr->offset);
- /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data); */
+ // printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data);
data[0] = x;
}
@@ -329,7 +329,7 @@ void immAttr2f(uint attr_id, float x, float y)
setAttrValueBit(attr_id);
float *data = (float *)(imm->vertex_data + attr->offset);
- /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data); */
+ // printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data);
data[0] = x;
data[1] = y;
@@ -346,7 +346,7 @@ void immAttr3f(uint attr_id, float x, float y, float z)
setAttrValueBit(attr_id);
float *data = (float *)(imm->vertex_data + attr->offset);
- /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data); */
+ // printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data);
data[0] = x;
data[1] = y;
@@ -364,7 +364,7 @@ void immAttr4f(uint attr_id, float x, float y, float z, float w)
setAttrValueBit(attr_id);
float *data = (float *)(imm->vertex_data + attr->offset);
- /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data); */
+ // printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data);
data[0] = x;
data[1] = y;
@@ -445,7 +445,7 @@ void immAttr3ub(uint attr_id, uchar r, uchar g, uchar b)
setAttrValueBit(attr_id);
uchar *data = imm->vertex_data + attr->offset;
- /* printf("%s %td %p\n", __FUNCTION__, data - imm->buffer_data, data); */
+ // printf("%s %td %p\n", __FUNCTION__, data - imm->buffer_data, data);
data[0] = r;
data[1] = g;
@@ -463,7 +463,7 @@ void immAttr4ub(uint attr_id, uchar r, uchar g, uchar b, uchar a)
setAttrValueBit(attr_id);
uchar *data = imm->vertex_data + attr->offset;
- /* printf("%s %td %p\n", __FUNCTION__, data - imm->buffer_data, data); */
+ // printf("%s %td %p\n", __FUNCTION__, data - imm->buffer_data, data);
data[0] = r;
data[1] = g;
diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c
index 37089785e0e..56e72fbeca9 100644
--- a/source/blender/gpu/intern/gpu_material.c
+++ b/source/blender/gpu/intern/gpu_material.c
@@ -758,6 +758,7 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene,
/* 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
diff --git a/source/blender/gpu/intern/gpu_matrix.cc b/source/blender/gpu/intern/gpu_matrix.cc
index 6eb9cb823d5..bbcc241f5e3 100644
--- a/source/blender/gpu/intern/gpu_matrix.cc
+++ b/source/blender/gpu/intern/gpu_matrix.cc
@@ -513,93 +513,55 @@ void GPU_matrix_project_2fv(const float world[3],
win[1] = view[1] + (view[3] * (v[1] + 1)) * 0.5f;
}
-/**
- * The same result could be obtained as follows:
- *
- * \code{.c}
- * float projinv[4][4];
- * invert_m4_m4(projinv, projmat);
- * co[0] = 2 * co[0] - 1;
- * co[1] = 2 * co[1] - 1;
- * co[2] = 2 * co[2] - 1;
- * mul_project_m4_v3(projinv, co);
- * \endcode
- *
- * But that solution loses much precision.
- * Therefore, get the same result without inverting the matrix.
- */
-static void gpu_mul_invert_projmat_m4_unmapped_v3_with_precalc(
- const struct GPUMatrixUnproject_Precalc *precalc, float co[3])
-{
- /* 'precalc->dims' is the result of 'projmat_dimensions(proj, ...)'. */
- co[0] = precalc->dims.xmin + co[0] * (precalc->dims.xmax - precalc->dims.xmin);
- co[1] = precalc->dims.ymin + co[1] * (precalc->dims.ymax - precalc->dims.ymin);
-
- if (precalc->is_persp) {
- co[2] = precalc->dims.zmax * precalc->dims.zmin /
- (precalc->dims.zmax + co[2] * (precalc->dims.zmin - precalc->dims.zmax));
- co[0] *= co[2];
- co[1] *= co[2];
- }
- else {
- co[2] = precalc->dims.zmin + co[2] * (precalc->dims.zmax - precalc->dims.zmin);
- }
- co[2] *= -1;
-}
-
-bool GPU_matrix_unproject_precalc(struct GPUMatrixUnproject_Precalc *precalc,
- const float model[4][4],
- const float proj[4][4],
- const int view[4])
-{
- precalc->is_persp = proj[3][3] == 0.0f;
- projmat_dimensions_db(proj,
- &precalc->dims.xmin,
- &precalc->dims.xmax,
- &precalc->dims.ymin,
- &precalc->dims.ymax,
- &precalc->dims.zmin,
- &precalc->dims.zmax);
- if (isinf(precalc->dims.zmax)) {
- /* We cannot retrieve the actual value of the clip_end.
- * Use `FLT_MAX` to avoid NAN's. */
- precalc->dims.zmax = FLT_MAX;
- }
- for (int i = 0; i < 4; i++) {
- precalc->view[i] = (float)view[i];
- }
- if (!invert_m4_m4(precalc->model_inverted, model)) {
- unit_m4(precalc->model_inverted);
- return false;
- }
- return true;
-}
-
-void GPU_matrix_unproject_3fv_with_precalc(const struct GPUMatrixUnproject_Precalc *precalc,
- const float win[3],
- float r_world[3])
-{
- float in[3] = {
- (win[0] - precalc->view[0]) / precalc->view[2],
- (win[1] - precalc->view[1]) / precalc->view[3],
- win[2],
- };
- gpu_mul_invert_projmat_m4_unmapped_v3_with_precalc(precalc, in);
- mul_v3_m4v3(r_world, precalc->model_inverted, in);
-}
-
bool GPU_matrix_unproject_3fv(const float win[3],
- const float model[4][4],
+ const float model_inverted[4][4],
const float proj[4][4],
const int view[4],
float r_world[3])
{
- struct GPUMatrixUnproject_Precalc precalc;
- if (!GPU_matrix_unproject_precalc(&precalc, model, proj, view)) {
- zero_v3(r_world);
+ zero_v3(r_world);
+ float in[3] = {
+ 2 * ((win[0] - view[0]) / view[2]) - 1.0f,
+ 2 * ((win[1] - view[1]) / view[3]) - 1.0f,
+ 2 * win[2] - 1.0f,
+ };
+
+ /**
+ * The same result could be obtained as follows:
+ *
+ * \code{.c}
+ * float projinv[4][4];
+ * invert_m4_m4(projinv, projview);
+ * copy_v3_v3(r_world, in);
+ * mul_project_m4_v3(projinv, r_world);
+ * \endcode
+ *
+ * But that solution loses much precision.
+ * Therefore, get the same result without inverting the project view matrix.
+ */
+
+ float out[3];
+ const bool is_persp = proj[3][3] == 0.0f;
+ if (is_persp) {
+ out[2] = proj[3][2] / (proj[2][2] + in[2]);
+ if (isinf(out[2])) {
+ out[2] = FLT_MAX;
+ }
+ out[0] = out[2] * ((proj[2][0] + in[0]) / proj[0][0]);
+ out[1] = out[2] * ((proj[2][1] + in[1]) / proj[1][1]);
+ out[2] *= -1;
+ }
+ else {
+ out[0] = (-proj[3][0] + in[0]) / proj[0][0];
+ out[1] = (-proj[3][1] + in[1]) / proj[1][1];
+ out[2] = (-proj[3][2] + in[2]) / proj[2][2];
+ }
+
+ if (!is_finite_v3(out)) {
return false;
}
- GPU_matrix_unproject_3fv_with_precalc(&precalc, win, r_world);
+
+ mul_v3_m4v3(r_world, model_inverted, out);
return true;
}
diff --git a/source/blender/gpu/intern/gpu_texture.cc b/source/blender/gpu/intern/gpu_texture.cc
index 6564cbda694..d5d13ea269f 100644
--- a/source/blender/gpu/intern/gpu_texture.cc
+++ b/source/blender/gpu/intern/gpu_texture.cc
@@ -241,55 +241,61 @@ static inline GPUTexture *gpu_texture_create(const char *name,
}
GPUTexture *GPU_texture_create_1d(
- const char *name, int w, int mips, eGPUTextureFormat format, const float *data)
+ const char *name, int w, int mip_len, eGPUTextureFormat format, const float *data)
{
- return gpu_texture_create(name, w, 0, 0, GPU_TEXTURE_1D, mips, format, GPU_DATA_FLOAT, data);
+ return gpu_texture_create(name, w, 0, 0, GPU_TEXTURE_1D, mip_len, format, GPU_DATA_FLOAT, data);
}
GPUTexture *GPU_texture_create_1d_array(
- const char *name, int w, int h, int mips, eGPUTextureFormat format, const float *data)
+ const char *name, int w, int h, int mip_len, eGPUTextureFormat format, const float *data)
{
return gpu_texture_create(
- name, w, h, 0, GPU_TEXTURE_1D_ARRAY, mips, format, GPU_DATA_FLOAT, data);
+ name, w, h, 0, GPU_TEXTURE_1D_ARRAY, mip_len, format, GPU_DATA_FLOAT, data);
}
GPUTexture *GPU_texture_create_2d(
- const char *name, int w, int h, int mips, eGPUTextureFormat format, const float *data)
+ const char *name, int w, int h, int mip_len, eGPUTextureFormat format, const float *data)
{
- return gpu_texture_create(name, w, h, 0, GPU_TEXTURE_2D, mips, format, GPU_DATA_FLOAT, data);
+ return gpu_texture_create(name, w, h, 0, GPU_TEXTURE_2D, mip_len, format, GPU_DATA_FLOAT, data);
}
-GPUTexture *GPU_texture_create_2d_array(
- const char *name, int w, int h, int d, int mips, eGPUTextureFormat format, const float *data)
+GPUTexture *GPU_texture_create_2d_array(const char *name,
+ int w,
+ int h,
+ int d,
+ int mip_len,
+ eGPUTextureFormat format,
+ const float *data)
{
return gpu_texture_create(
- name, w, h, d, GPU_TEXTURE_2D_ARRAY, mips, format, GPU_DATA_FLOAT, data);
+ name, w, h, d, GPU_TEXTURE_2D_ARRAY, mip_len, format, GPU_DATA_FLOAT, data);
}
GPUTexture *GPU_texture_create_3d(const char *name,
int w,
int h,
int d,
- int mips,
+ int mip_len,
eGPUTextureFormat texture_format,
eGPUDataFormat data_format,
const void *data)
{
return gpu_texture_create(
- name, w, h, d, GPU_TEXTURE_3D, mips, texture_format, data_format, data);
+ name, w, h, d, GPU_TEXTURE_3D, mip_len, texture_format, data_format, data);
}
GPUTexture *GPU_texture_create_cube(
- const char *name, int w, int mips, eGPUTextureFormat format, const float *data)
+ const char *name, int w, int mip_len, eGPUTextureFormat format, const float *data)
{
- return gpu_texture_create(name, w, w, 0, GPU_TEXTURE_CUBE, mips, format, GPU_DATA_FLOAT, data);
+ return gpu_texture_create(
+ name, w, w, 0, GPU_TEXTURE_CUBE, mip_len, format, GPU_DATA_FLOAT, data);
}
GPUTexture *GPU_texture_create_cube_array(
- const char *name, int w, int d, int mips, eGPUTextureFormat format, const float *data)
+ const char *name, int w, int d, int mip_len, eGPUTextureFormat format, const float *data)
{
return gpu_texture_create(
- name, w, w, d, GPU_TEXTURE_CUBE_ARRAY, mips, format, GPU_DATA_FLOAT, data);
+ name, w, w, d, GPU_TEXTURE_CUBE_ARRAY, mip_len, format, GPU_DATA_FLOAT, data);
}
/* DDS texture loading. Return NULL if support is not available. */
diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc
index 42b85da1f93..772fc19d919 100644
--- a/source/blender/gpu/opengl/gl_backend.cc
+++ b/source/blender/gpu/opengl/gl_backend.cc
@@ -283,7 +283,8 @@ static void detect_workarounds()
}
/* We have issues with this specific renderer. (see T74024) */
if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE) &&
- strstr(renderer, "AMD VERDE")) {
+ (strstr(renderer, "AMD VERDE") || strstr(renderer, "AMD KAVERI") ||
+ strstr(renderer, "AMD TAHITI"))) {
GLContext::unused_fb_slot_workaround = true;
GCaps.shader_image_load_store_support = false;
GCaps.broken_amd_driver = true;
diff --git a/source/blender/gpu/shaders/gpu_shader_3D_point_uniform_size_aa_vert.glsl b/source/blender/gpu/shaders/gpu_shader_3D_point_uniform_size_aa_vert.glsl
index f5b6d2ea3ed..5d67658c639 100644
--- a/source/blender/gpu/shaders/gpu_shader_3D_point_uniform_size_aa_vert.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_3D_point_uniform_size_aa_vert.glsl
@@ -14,14 +14,14 @@ void main()
gl_Position = ModelViewProjectionMatrix * pos_4d;
gl_PointSize = size;
- // calculate concentric radii in pixels
+ /* Calculate concentric radii in pixels. */
float radius = 0.5 * size;
- // start at the outside and progress toward the center
+ /* Start at the outside and progress toward the center. */
radii[0] = radius;
radii[1] = radius - 1.0;
- // convert to PointCoord units
+ /* Convert to PointCoord units. */
radii /= size;
#ifdef USE_WORLD_CLIP_PLANES
diff --git a/source/blender/gpu/tests/gpu_shader_test.cc b/source/blender/gpu/tests/gpu_shader_test.cc
index 43ff86ebbd8..dbe336af097 100644
--- a/source/blender/gpu/tests/gpu_shader_test.cc
+++ b/source/blender/gpu/tests/gpu_shader_test.cc
@@ -108,7 +108,8 @@ void main() {
EXPECT_NE(shader, nullptr);
/* Construct Texture. */
- GPUTexture *texture = GPU_texture_create_1d("gpu_shader_compute_1d", SIZE, 0, GPU_RGBA32F, NULL);
+ GPUTexture *texture = GPU_texture_create_1d(
+ "gpu_shader_compute_1d", SIZE, 0, GPU_RGBA32F, nullptr);
EXPECT_NE(texture, nullptr);
GPU_shader_bind(shader);
diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h
index d527aca184c..4ad7aa98484 100644
--- a/source/blender/imbuf/IMB_imbuf.h
+++ b/source/blender/imbuf/IMB_imbuf.h
@@ -367,6 +367,11 @@ void IMB_anim_index_rebuild_finish(struct IndexBuildContext *context, short stop
int IMB_anim_get_duration(struct anim *anim, IMB_Timecode_Type tc);
/**
+ * Return the encoded start offset (in seconds) of the given \a anim.
+ */
+double IMD_anim_get_offset(struct anim *anim);
+
+/**
* Return the fps contained in movie files (function rval is false,
* and frs_sec and frs_sec_base untouched if none available!)
*/
diff --git a/source/blender/imbuf/intern/IMB_anim.h b/source/blender/imbuf/intern/IMB_anim.h
index cfeffcca0ea..c4e2ad9da7f 100644
--- a/source/blender/imbuf/intern/IMB_anim.h
+++ b/source/blender/imbuf/intern/IMB_anim.h
@@ -91,6 +91,7 @@ struct anim {
int duration_in_frames;
int frs_sec;
double frs_sec_base;
+ double start_offset;
int x, y;
/* for number */
diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c
index 47514308ae4..dbca16ca82b 100644
--- a/source/blender/imbuf/intern/anim_movie.c
+++ b/source/blender/imbuf/intern/anim_movie.c
@@ -425,6 +425,7 @@ static int startavi(struct anim *anim)
}
anim->duration_in_frames = anim->avi->header->TotalFrames;
+ anim->start_offset = 0.0f;
anim->params = NULL;
anim->x = anim->avi->header->Width;
@@ -597,6 +598,13 @@ static int startffmpeg(struct anim *anim)
return -1;
}
+ double video_start = 0;
+ double pts_time_base = av_q2d(video_stream->time_base);
+
+ if (video_stream->start_time != AV_NOPTS_VALUE) {
+ video_start = video_stream->start_time * pts_time_base;
+ }
+
frame_rate = av_guess_frame_rate(pFormatCtx, video_stream, NULL);
anim->duration_in_frames = 0;
@@ -616,10 +624,49 @@ static int startffmpeg(struct anim *anim)
}
}
}
- /* Fall back to the container. */
+ /* Fall back to manually estimating the video stream duration.
+ * This is because the video stream duration can be shorter than the pFormatCtx->duration.
+ */
if (anim->duration_in_frames == 0) {
- anim->duration_in_frames = (int)(pFormatCtx->duration * av_q2d(frame_rate) / AV_TIME_BASE +
- 0.5f);
+ double stream_dur;
+
+ if (video_stream->duration != AV_NOPTS_VALUE) {
+ stream_dur = video_stream->duration * pts_time_base;
+ }
+ else {
+ double audio_start = 0;
+
+ /* Find audio stream to guess the duration of the video.
+ * Sometimes the audio AND the video stream have a start offset.
+ * The difference between these is the offset we want to use to
+ * calculate the video duration.
+ */
+ for (i = 0; i < pFormatCtx->nb_streams; i++) {
+ if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
+ AVStream *audio_stream = pFormatCtx->streams[i];
+ if (audio_stream->start_time != AV_NOPTS_VALUE) {
+ audio_start = audio_stream->start_time * av_q2d(audio_stream->time_base);
+ }
+ break;
+ }
+ }
+
+ if (video_start > audio_start) {
+ stream_dur = (double)pFormatCtx->duration / AV_TIME_BASE - (video_start - audio_start);
+ }
+ else {
+ /* The video stream starts before or at the same time as the audio stream!
+ * We have to assume that the video stream is as long as the full pFormatCtx->duration.
+ */
+ stream_dur = (double)pFormatCtx->duration / AV_TIME_BASE;
+ }
+ }
+ anim->duration_in_frames = (int)(stream_dur * av_q2d(frame_rate) + 0.5f);
+ }
+
+ double ctx_start = 0;
+ if (pFormatCtx->start_time != AV_NOPTS_VALUE) {
+ ctx_start = (double)pFormatCtx->start_time / AV_TIME_BASE;
}
frs_num = frame_rate.num;
@@ -634,6 +681,9 @@ static int startffmpeg(struct anim *anim)
anim->frs_sec = frs_num;
anim->frs_sec_base = frs_den;
+ /* Save the relative start time for the video. IE the start time in relation to where playback
+ * starts. */
+ anim->start_offset = video_start - ctx_start;
anim->params = 0;
@@ -1019,33 +1069,21 @@ static int ffmpeg_seek_by_byte(AVFormatContext *pFormatCtx)
return false;
}
-static int64_t ffmpeg_get_seek_pos(struct anim *anim, int position)
+static int64_t ffmpeg_get_seek_pts(struct anim *anim, int64_t pts_to_search)
{
AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream];
- double frame_rate = av_q2d(av_guess_frame_rate(anim->pFormatCtx, v_st, NULL));
- int64_t st_time = anim->pFormatCtx->start_time;
- int64_t pos = (int64_t)(position)*AV_TIME_BASE;
- /* Step back half a time base position to make sure that we get the requested
- * frame and not the one after it.
+ AVRational frame_rate = v_st->r_frame_rate;
+ AVRational time_base = v_st->time_base;
+ double steps_per_frame = (double)(frame_rate.den * time_base.den) /
+ (double)(frame_rate.num * time_base.num);
+ /* Step back half a frame position to make sure that we get the requested
+ * frame and not the one after it. This is a workaround as ffmpeg will
+ * sometimes not seek to a frame after the requested pts even if
+ * AVSEEK_FLAG_BACKWARD is specified.
*/
- pos -= (AV_TIME_BASE / 2);
- pos /= frame_rate;
-
- av_log(anim->pFormatCtx,
- AV_LOG_DEBUG,
- "NO INDEX seek pos = %" PRId64 ", st_time = %" PRId64 "\n",
- pos,
- (st_time != AV_NOPTS_VALUE) ? st_time : 0);
-
- if (pos < 0) {
- pos = 0;
- }
-
- if (st_time != AV_NOPTS_VALUE) {
- pos += st_time;
- }
+ int64_t pts = pts_to_search - (steps_per_frame / 2);
- return pos;
+ return pts;
}
/* This gives us an estimate of which pts our requested frame will have.
@@ -1062,17 +1100,18 @@ static int64_t ffmpeg_get_pts_to_search(struct anim *anim,
pts_to_search = IMB_indexer_get_pts(tc_index, new_frame_index);
}
else {
- int64_t st_time = anim->pFormatCtx->start_time;
AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream];
- AVRational frame_rate = av_guess_frame_rate(anim->pFormatCtx, v_st, NULL);
+ int64_t start_pts = v_st->start_time;
+ AVRational frame_rate = v_st->r_frame_rate;
AVRational time_base = v_st->time_base;
- int64_t steps_per_frame = (frame_rate.den * time_base.den) / (frame_rate.num * time_base.num);
- pts_to_search = position * steps_per_frame;
+ double steps_per_frame = (double)(frame_rate.den * time_base.den) /
+ (double)(frame_rate.num * time_base.num);
- if (st_time != AV_NOPTS_VALUE && st_time != 0) {
- int64_t start_frame = (double)st_time / AV_TIME_BASE * av_q2d(frame_rate);
- pts_to_search += start_frame * steps_per_frame;
+ pts_to_search = round(position * steps_per_frame);
+
+ if (start_pts != AV_NOPTS_VALUE) {
+ pts_to_search += start_pts;
}
}
return pts_to_search;
@@ -1156,23 +1195,29 @@ static void ffmpeg_decode_video_frame_scan(struct anim *anim, int64_t pts_to_sea
* decoded will be read. See https://trac.ffmpeg.org/ticket/1607 and
* https://developer.blender.org/T86944. */
static int ffmpeg_generic_seek_workaround(struct anim *anim,
- int64_t *requested_pos,
+ int64_t *requested_pts,
int64_t pts_to_search)
{
AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream];
- double frame_rate = av_q2d(av_guess_frame_rate(anim->pFormatCtx, v_st, NULL));
- int64_t current_pos = *requested_pos;
+ AVRational frame_rate = v_st->r_frame_rate;
+ AVRational time_base = v_st->time_base;
+
+ double steps_per_frame = (double)(frame_rate.den * time_base.den) /
+ (double)(frame_rate.num * time_base.num);
+
+ int64_t current_pts = *requested_pts;
int64_t offset = 0;
int64_t cur_pts, prev_pts = -1;
/* Step backward frame by frame until we find the key frame we are looking for. */
- while (current_pos != 0) {
- current_pos = *requested_pos - ((int64_t)(offset)*AV_TIME_BASE / frame_rate);
- current_pos = max_ii(current_pos, 0);
+ while (current_pts != 0) {
+ current_pts = *requested_pts - (int64_t)round(offset * steps_per_frame);
+ current_pts = MAX2(current_pts, 0);
/* Seek to timestamp. */
- if (av_seek_frame(anim->pFormatCtx, -1, current_pos, AVSEEK_FLAG_BACKWARD) < 0) {
+ if (av_seek_frame(anim->pFormatCtx, anim->videoStream, current_pts, AVSEEK_FLAG_BACKWARD) <
+ 0) {
break;
}
@@ -1198,21 +1243,22 @@ static int ffmpeg_generic_seek_workaround(struct anim *anim,
/* We found the I-frame we were looking for! */
break;
}
- if (cur_pts == prev_pts) {
- /* We got the same key frame packet twice.
- * This probably means that we have hit the beginning of the stream. */
- break;
- }
+ }
+
+ if (cur_pts == prev_pts) {
+ /* We got the same key frame packet twice.
+ * This probably means that we have hit the beginning of the stream. */
+ break;
}
prev_pts = cur_pts;
offset++;
}
- *requested_pos = current_pos;
+ *requested_pts = current_pts;
/* Re-seek to timestamp that gave I-frame, so it can be read by decode function. */
- return av_seek_frame(anim->pFormatCtx, -1, current_pos, AVSEEK_FLAG_BACKWARD);
+ return av_seek_frame(anim->pFormatCtx, anim->videoStream, current_pts, AVSEEK_FLAG_BACKWARD);
}
/* Seek to last necessary key frame. */
@@ -1260,13 +1306,13 @@ static int ffmpeg_seek_to_key_frame(struct anim *anim,
else {
/* We have to manually seek with ffmpeg to get to the key frame we want to start decoding from.
*/
- pos = ffmpeg_get_seek_pos(anim, position);
+ pos = ffmpeg_get_seek_pts(anim, pts_to_search);
av_log(anim->pFormatCtx, AV_LOG_DEBUG, "NO INDEX final seek pos = %" PRId64 "\n", pos);
AVFormatContext *format_ctx = anim->pFormatCtx;
if (format_ctx->iformat->read_seek2 || format_ctx->iformat->read_seek) {
- ret = av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BACKWARD);
+ ret = av_seek_frame(anim->pFormatCtx, anim->videoStream, pos, AVSEEK_FLAG_BACKWARD);
}
else {
ret = ffmpeg_generic_seek_workaround(anim, &pos, pts_to_search);
@@ -1311,7 +1357,7 @@ static int ffmpeg_seek_to_key_frame(struct anim *anim,
anim->cur_key_frame_pts = gop_pts;
/* Seek back so we are at the correct position after we decoded a frame. */
- av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BACKWARD);
+ av_seek_frame(anim->pFormatCtx, anim->videoStream, pos, AVSEEK_FLAG_BACKWARD);
}
}
@@ -1351,18 +1397,18 @@ static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Typ
struct anim_index *tc_index = IMB_anim_open_index(anim, tc);
int64_t pts_to_search = ffmpeg_get_pts_to_search(anim, tc_index, position);
AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream];
- double frame_rate = av_q2d(av_guess_frame_rate(anim->pFormatCtx, v_st, NULL));
+ double frame_rate = av_q2d(v_st->r_frame_rate);
double pts_time_base = av_q2d(v_st->time_base);
- int64_t st_time = anim->pFormatCtx->start_time;
+ int64_t start_pts = v_st->start_time;
av_log(anim->pFormatCtx,
AV_LOG_DEBUG,
- "FETCH: looking for PTS=%" PRId64 " (pts_timebase=%g, frame_rate=%g, st_time=%" PRId64
+ "FETCH: looking for PTS=%" PRId64 " (pts_timebase=%g, frame_rate=%g, start_pts=%" PRId64
")\n",
(int64_t)pts_to_search,
pts_time_base,
frame_rate,
- st_time);
+ start_pts);
if (ffmpeg_pts_matches_last_frame(anim, pts_to_search)) {
av_log(anim->pFormatCtx,
@@ -1637,6 +1683,11 @@ int IMB_anim_get_duration(struct anim *anim, IMB_Timecode_Type tc)
return IMB_indexer_get_duration(idx);
}
+double IMD_anim_get_offset(struct anim *anim)
+{
+ return anim->start_offset;
+}
+
bool IMB_anim_get_fps(struct anim *anim, short *frs_sec, float *frs_sec_base, bool no_av_base)
{
double frs_sec_base_double;
diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c
index 2cc44ebc67b..8b2ac2ed22d 100644
--- a/source/blender/imbuf/intern/colormanagement.c
+++ b/source/blender/imbuf/intern/colormanagement.c
@@ -759,11 +759,7 @@ static bool colormanage_use_look(const char *look, const char *view_name)
void colormanage_cache_free(ImBuf *ibuf)
{
- if (ibuf->display_buffer_flags) {
- MEM_freeN(ibuf->display_buffer_flags);
-
- ibuf->display_buffer_flags = NULL;
- }
+ MEM_SAFE_FREE(ibuf->display_buffer_flags);
if (ibuf->colormanage_cache) {
ColormanageCacheData *cache_data = colormanage_cachedata_get(ibuf);
diff --git a/source/blender/imbuf/intern/dds/ColorBlock.h b/source/blender/imbuf/intern/dds/ColorBlock.h
index 158695cfbf3..934837bb129 100644
--- a/source/blender/imbuf/intern/dds/ColorBlock.h
+++ b/source/blender/imbuf/intern/dds/ColorBlock.h
@@ -25,7 +25,7 @@
* Original license from NVIDIA follows.
*/
-// This code is in the public domain -- <castanyo@yahoo.es>
+/* This code is in the public domain -- <castanyo@yahoo.es> */
#pragma once
diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c
index 27195b294d6..bbb0f3b5b22 100644
--- a/source/blender/imbuf/intern/indexer.c
+++ b/source/blender/imbuf/intern/indexer.c
@@ -1022,7 +1022,7 @@ static int index_rebuild_ffmpeg(FFmpegIndexBuilderContext *context,
stream_size = avio_size(context->iFormatCtx->pb);
- context->frame_rate = av_q2d(av_guess_frame_rate(context->iFormatCtx, context->iStream, NULL));
+ context->frame_rate = av_q2d(context->iStream->r_frame_rate);
context->pts_time_base = av_q2d(context->iStream->time_base);
while (av_read_frame(context->iFormatCtx, next_packet) >= 0) {
diff --git a/source/blender/imbuf/intern/metadata.c b/source/blender/imbuf/intern/metadata.c
index c59997b34f5..5a01c42cf00 100644
--- a/source/blender/imbuf/intern/metadata.c
+++ b/source/blender/imbuf/intern/metadata.c
@@ -44,7 +44,7 @@ void IMB_metadata_ensure(struct IDProperty **metadata)
return;
}
- IDPropertyTemplate val;
+ IDPropertyTemplate val = {0};
*metadata = IDP_New(IDP_GROUP, &val, "metadata");
}
diff --git a/source/blender/imbuf/intern/moviecache.c b/source/blender/imbuf/intern/moviecache.c
index e395222a214..6cc1932eff6 100644
--- a/source/blender/imbuf/intern/moviecache.c
+++ b/source/blender/imbuf/intern/moviecache.c
@@ -179,10 +179,7 @@ static void IMB_moviecache_destructor(void *p)
item->c_handle = NULL;
/* force cached segments to be updated */
- if (cache->points) {
- MEM_freeN(cache->points);
- cache->points = NULL;
- }
+ MEM_SAFE_FREE(cache->points);
}
}
@@ -355,10 +352,7 @@ static void do_moviecache_put(MovieCache *cache, void *userkey, ImBuf *ibuf, boo
/* cache limiter can't remove unused keys which points to destroyed values */
check_unused_keys(cache);
- if (cache->points) {
- MEM_freeN(cache->points);
- cache->points = NULL;
- }
+ MEM_SAFE_FREE(cache->points);
}
void IMB_moviecache_put(MovieCache *cache, void *userkey, ImBuf *ibuf)
@@ -488,11 +482,7 @@ void IMB_moviecache_get_cache_segments(
}
if (cache->proxy != proxy || cache->render_flags != render_flags) {
- if (cache->points) {
- MEM_freeN(cache->points);
- }
-
- cache->points = NULL;
+ MEM_SAFE_FREE(cache->points);
}
if (cache->points) {
diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp
index d1fa26e1a3e..cd323e72003 100644
--- a/source/blender/imbuf/intern/openexr/openexr_api.cpp
+++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp
@@ -69,7 +69,7 @@
extern "C" {
-// The following prevents a linking error in debug mode for MSVC using the libs in CVS
+/* The following prevents a linking error in debug mode for MSVC using the libs in SVN. */
#if defined(WITH_OPENEXR) && defined(_WIN32) && defined(DEBUG) && _MSC_VER < 1900
_CRTIMP void __cdecl _invalid_parameter_noinfo(void)
{
@@ -1711,7 +1711,7 @@ static const char *exr_rgba_channelname(MultiPartInputFile &file, const char *ch
const ChannelList &channels = file.header(0).channels();
for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) {
- /* const Channel &channel = i.channel(); */ /* Not used yet */
+ // const Channel &channel = i.channel(); /* Not used yet. */
const char *str = i.name();
int len = strlen(str);
if (len) {
diff --git a/source/blender/io/alembic/ABC_alembic.h b/source/blender/io/alembic/ABC_alembic.h
index 3d1391ac2a4..0a3a43bb21f 100644
--- a/source/blender/io/alembic/ABC_alembic.h
+++ b/source/blender/io/alembic/ABC_alembic.h
@@ -25,6 +25,7 @@
extern "C" {
#endif
+struct CacheArchiveHandle;
struct CacheReader;
struct ListBase;
struct Main;
@@ -33,8 +34,6 @@ struct Object;
struct Scene;
struct bContext;
-typedef struct AbcArchiveHandle AbcArchiveHandle;
-
int ABC_get_version(void);
struct AlembicExportParams {
@@ -98,13 +97,14 @@ bool ABC_import(struct bContext *C,
int sequence_len,
int offset,
bool validate_meshes,
+ bool always_add_cache_reader,
bool as_background_job);
-AbcArchiveHandle *ABC_create_handle(struct Main *bmain,
- const char *filename,
- struct ListBase *object_paths);
+struct CacheArchiveHandle *ABC_create_handle(struct Main *bmain,
+ const char *filename,
+ struct ListBase *object_paths);
-void ABC_free_handle(AbcArchiveHandle *handle);
+void ABC_free_handle(struct CacheArchiveHandle *handle);
void ABC_get_transform(struct CacheReader *reader,
float r_mat_world[4][4],
@@ -125,10 +125,10 @@ bool ABC_mesh_topology_changed(struct CacheReader *reader,
const float time,
const char **err_str);
-void CacheReader_incref(struct CacheReader *reader);
-void CacheReader_free(struct CacheReader *reader);
+void ABC_CacheReader_incref(struct CacheReader *reader);
+void ABC_CacheReader_free(struct CacheReader *reader);
-struct CacheReader *CacheReader_open_alembic_object(struct AbcArchiveHandle *handle,
+struct CacheReader *CacheReader_open_alembic_object(struct CacheArchiveHandle *handle,
struct CacheReader *reader,
struct Object *object,
const char *object_path);
diff --git a/source/blender/io/alembic/intern/abc_reader_curves.cc b/source/blender/io/alembic/intern/abc_reader_curves.cc
index 8d6605d6973..27ee35d1b39 100644
--- a/source/blender/io/alembic/intern/abc_reader_curves.cc
+++ b/source/blender/io/alembic/intern/abc_reader_curves.cc
@@ -112,7 +112,7 @@ void AbcCurveReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSele
read_curve_sample(cu, m_curves_schema, sample_sel);
- if (has_animations(m_curves_schema, m_settings)) {
+ if (m_settings->always_add_cache_reader || has_animations(m_curves_schema, m_settings)) {
addCacheModifier();
}
}
diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc
index c05df7f1ff5..77edd4908bd 100644
--- a/source/blender/io/alembic/intern/abc_reader_mesh.cc
+++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc
@@ -578,7 +578,7 @@ void AbcMeshReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelec
readFaceSetsSample(bmain, mesh, sample_sel);
- if (has_animations(m_schema, m_settings)) {
+ if (m_settings->always_add_cache_reader || has_animations(m_schema, m_settings)) {
addCacheModifier();
}
}
@@ -928,7 +928,7 @@ void AbcSubDReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelec
BKE_mesh_validate(mesh, false, false);
}
- if (has_animations(m_schema, m_settings)) {
+ if (m_settings->always_add_cache_reader || has_animations(m_schema, m_settings)) {
addCacheModifier();
}
}
diff --git a/source/blender/io/alembic/intern/abc_reader_nurbs.cc b/source/blender/io/alembic/intern/abc_reader_nurbs.cc
index 2a5f4ecb787..25567aa8c24 100644
--- a/source/blender/io/alembic/intern/abc_reader_nurbs.cc
+++ b/source/blender/io/alembic/intern/abc_reader_nurbs.cc
@@ -90,7 +90,7 @@ static bool set_knots(const FloatArraySamplePtr &knots, float *&nu_knots)
void AbcNurbsReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel)
{
- Curve *cu = static_cast<Curve *>(BKE_curve_add(bmain, "abc_curve", OB_SURF));
+ Curve *cu = static_cast<Curve *>(BKE_curve_add(bmain, m_data_name.c_str(), OB_SURF));
cu->actvert = CU_ACT_NONE;
std::vector<std::pair<INuPatchSchema, IObject>>::iterator it;
@@ -180,8 +180,6 @@ void AbcNurbsReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSele
BLI_addtail(BKE_curve_nurbs_get(cu), nu);
}
- BLI_strncpy(cu->id.name + 2, m_data_name.c_str(), m_data_name.size() + 1);
-
m_object = BKE_object_add_only_object(bmain, OB_SURF, m_object_name.c_str());
m_object->data = cu;
}
diff --git a/source/blender/io/alembic/intern/abc_reader_object.cc b/source/blender/io/alembic/intern/abc_reader_object.cc
index 00b73d29c5c..9a5ffd04bd1 100644
--- a/source/blender/io/alembic/intern/abc_reader_object.cc
+++ b/source/blender/io/alembic/intern/abc_reader_object.cc
@@ -1,4 +1,4 @@
-/*
+/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
@@ -197,7 +197,7 @@ void AbcObjectReader::setupObjectTransform(const float time)
BKE_object_apply_mat4(m_object, transform_from_alembic, true, false);
BKE_object_to_mat4(m_object, m_object->obmat);
- if (!is_constant) {
+ if (!is_constant || m_settings->always_add_cache_reader) {
bConstraint *con = BKE_constraint_add_for_object(
m_object, nullptr, CONSTRAINT_TYPE_TRANSFORM_CACHE);
bTransformCacheConstraint *data = static_cast<bTransformCacheConstraint *>(con->data);
diff --git a/source/blender/io/alembic/intern/abc_reader_object.h b/source/blender/io/alembic/intern/abc_reader_object.h
index dacdcf3f722..89590b26b61 100644
--- a/source/blender/io/alembic/intern/abc_reader_object.h
+++ b/source/blender/io/alembic/intern/abc_reader_object.h
@@ -51,6 +51,7 @@ struct ImportSettings {
int read_flag;
bool validate_meshes;
+ bool always_add_cache_reader;
CacheFile *cache_file;
@@ -65,6 +66,7 @@ struct ImportSettings {
sequence_offset(0),
read_flag(0),
validate_meshes(false),
+ always_add_cache_reader(false),
cache_file(NULL)
{
}
diff --git a/source/blender/io/alembic/intern/abc_reader_points.cc b/source/blender/io/alembic/intern/abc_reader_points.cc
index f7dcba7a0de..3aeacbd14fe 100644
--- a/source/blender/io/alembic/intern/abc_reader_points.cc
+++ b/source/blender/io/alembic/intern/abc_reader_points.cc
@@ -95,7 +95,7 @@ void AbcPointsReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSel
m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str());
m_object->data = mesh;
- if (has_animations(m_schema, m_settings)) {
+ if (m_settings->always_add_cache_reader || has_animations(m_schema, m_settings)) {
addCacheModifier();
}
}
diff --git a/source/blender/io/alembic/intern/abc_util.h b/source/blender/io/alembic/intern/abc_util.h
index 98f4b0376a7..ced9fde0f85 100644
--- a/source/blender/io/alembic/intern/abc_util.h
+++ b/source/blender/io/alembic/intern/abc_util.h
@@ -22,15 +22,6 @@
#include <Alembic/Abc/All.h>
#include <Alembic/AbcGeom/All.h>
-/**
- * \brief The CacheReader struct is only used for anonymous pointers,
- * to interface between C and C++ code. This library only creates
- * pointers to AbcObjectReader (or subclasses thereof).
- */
-struct CacheReader {
- int unused;
-};
-
using Alembic::Abc::chrono_t;
struct ID;
diff --git a/source/blender/io/alembic/intern/alembic_capi.cc b/source/blender/io/alembic/intern/alembic_capi.cc
index e8d70bf3edb..deb945b767c 100644
--- a/source/blender/io/alembic/intern/alembic_capi.cc
+++ b/source/blender/io/alembic/intern/alembic_capi.cc
@@ -19,6 +19,7 @@
*/
#include "../ABC_alembic.h"
+#include "IO_types.h"
#include <Alembic/AbcMaterial/IMaterial.h>
@@ -89,18 +90,14 @@ using Alembic::AbcMaterial::IMaterial;
using namespace blender::io::alembic;
-struct AbcArchiveHandle {
- int unused;
-};
-
-BLI_INLINE ArchiveReader *archive_from_handle(AbcArchiveHandle *handle)
+BLI_INLINE ArchiveReader *archive_from_handle(CacheArchiveHandle *handle)
{
return reinterpret_cast<ArchiveReader *>(handle);
}
-BLI_INLINE AbcArchiveHandle *handle_from_archive(ArchiveReader *archive)
+BLI_INLINE CacheArchiveHandle *handle_from_archive(ArchiveReader *archive)
{
- return reinterpret_cast<AbcArchiveHandle *>(archive);
+ return reinterpret_cast<CacheArchiveHandle *>(archive);
}
//#define USE_NURBS
@@ -150,8 +147,8 @@ static bool gather_objects_paths(const IObject &object, ListBase *object_paths)
}
if (get_path) {
- void *abc_path_void = MEM_callocN(sizeof(AlembicObjectPath), "AlembicObjectPath");
- AlembicObjectPath *abc_path = static_cast<AlembicObjectPath *>(abc_path_void);
+ void *abc_path_void = MEM_callocN(sizeof(CacheObjectPath), "CacheObjectPath");
+ CacheObjectPath *abc_path = static_cast<CacheObjectPath *>(abc_path_void);
BLI_strncpy(abc_path->path, object.getFullName().c_str(), sizeof(abc_path->path));
BLI_addtail(object_paths, abc_path);
@@ -160,9 +157,9 @@ static bool gather_objects_paths(const IObject &object, ListBase *object_paths)
return parent_is_part_of_this_object;
}
-AbcArchiveHandle *ABC_create_handle(struct Main *bmain,
- const char *filename,
- ListBase *object_paths)
+CacheArchiveHandle *ABC_create_handle(struct Main *bmain,
+ const char *filename,
+ ListBase *object_paths)
{
ArchiveReader *archive = new ArchiveReader(bmain, filename);
@@ -178,7 +175,7 @@ AbcArchiveHandle *ABC_create_handle(struct Main *bmain,
return handle_from_archive(archive);
}
-void ABC_free_handle(AbcArchiveHandle *handle)
+void ABC_free_handle(CacheArchiveHandle *handle)
{
delete archive_from_handle(handle);
}
@@ -359,8 +356,8 @@ static std::pair<bool, AbcObjectReader *> visit_object(
readers.push_back(reader);
reader->incref();
- AlembicObjectPath *abc_path = static_cast<AlembicObjectPath *>(
- MEM_callocN(sizeof(AlembicObjectPath), "AlembicObjectPath"));
+ CacheObjectPath *abc_path = static_cast<CacheObjectPath *>(
+ MEM_callocN(sizeof(CacheObjectPath), "CacheObjectPath"));
BLI_strncpy(abc_path->path, full_name.c_str(), sizeof(abc_path->path));
BLI_addtail(&settings.cache_file->object_paths, abc_path);
@@ -666,6 +663,7 @@ bool ABC_import(bContext *C,
int sequence_len,
int offset,
bool validate_meshes,
+ bool always_add_cache_reader,
bool as_background_job)
{
/* Using new here since MEM_* functions do not call constructor to properly initialize data. */
@@ -684,6 +682,7 @@ bool ABC_import(bContext *C,
job->settings.sequence_len = sequence_len;
job->settings.sequence_offset = offset;
job->settings.validate_meshes = validate_meshes;
+ job->settings.always_add_cache_reader = always_add_cache_reader;
job->error_code = ABC_NO_ERROR;
job->was_cancelled = false;
job->archive = nullptr;
@@ -812,7 +811,7 @@ bool ABC_mesh_topology_changed(
/* ************************************************************************** */
-void CacheReader_free(CacheReader *reader)
+void ABC_CacheReader_free(CacheReader *reader)
{
AbcObjectReader *abc_reader = reinterpret_cast<AbcObjectReader *>(reader);
abc_reader->decref();
@@ -822,13 +821,13 @@ void CacheReader_free(CacheReader *reader)
}
}
-void CacheReader_incref(CacheReader *reader)
+void ABC_CacheReader_incref(CacheReader *reader)
{
AbcObjectReader *abc_reader = reinterpret_cast<AbcObjectReader *>(reader);
abc_reader->incref();
}
-CacheReader *CacheReader_open_alembic_object(AbcArchiveHandle *handle,
+CacheReader *CacheReader_open_alembic_object(CacheArchiveHandle *handle,
CacheReader *reader,
Object *object,
const char *object_path)
@@ -847,7 +846,7 @@ CacheReader *CacheReader_open_alembic_object(AbcArchiveHandle *handle,
find_iobject(archive->getTop(), iobject, object_path);
if (reader) {
- CacheReader_free(reader);
+ ABC_CacheReader_free(reader);
}
ImportSettings settings;
diff --git a/source/blender/io/avi/intern/avi_endian.c b/source/blender/io/avi/intern/avi_endian.c
index 146206cd917..36cee7bcadc 100644
--- a/source/blender/io/avi/intern/avi_endian.c
+++ b/source/blender/io/avi/intern/avi_endian.c
@@ -42,7 +42,7 @@
static void invert(int *val)
{
int tval = *val;
- *val = ((tval >> 24)) | ((tval << 8) & 0x00ff0000) | ((tval >> 8) & 0x0000ff00) | ((tval << 24));
+ *val = (tval >> 24) | ((tval << 8) & 0x00ff0000) | ((tval >> 8) & 0x0000ff00) | (tval << 24);
}
static void sinvert(short int *val)
diff --git a/source/blender/io/collada/AnimationImporter.cpp b/source/blender/io/collada/AnimationImporter.cpp
index e52bdca0d87..e54192abc54 100644
--- a/source/blender/io/collada/AnimationImporter.cpp
+++ b/source/blender/io/collada/AnimationImporter.cpp
@@ -1363,7 +1363,7 @@ void AnimationImporter::add_bone_animation_sampled(Object *ob,
calc_joint_parent_mat_rest(par, nullptr, root, node);
mul_m4_m4m4(temp, par, matfra);
- /* evaluate_joint_world_transform_at_frame(temp, NULL, node, fra); */
+ // evaluate_joint_world_transform_at_frame(temp, NULL, node, fra);
/* calc special matrix */
mul_m4_series(mat, irest, temp, irest_dae, rest);
diff --git a/source/blender/io/collada/SceneExporter.cpp b/source/blender/io/collada/SceneExporter.cpp
index 5bbd22b8275..09da2288cfc 100644
--- a/source/blender/io/collada/SceneExporter.cpp
+++ b/source/blender/io/collada/SceneExporter.cpp
@@ -172,7 +172,7 @@ void SceneExporter::writeNode(Object *ob)
else if (ob->type == OB_EMPTY) { /* TODO: handle groups (OB_DUPLICOLLECTION */
if ((ob->transflag & OB_DUPLICOLLECTION) == OB_DUPLICOLLECTION && ob->instance_collection) {
Collection *collection = ob->instance_collection;
- /* printf("group detected '%s'\n", group->id.name + 2); */
+ // printf("group detected '%s'\n", group->id.name + 2);
FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection, object) {
printf("\t%s\n", object->id.name);
}
diff --git a/source/blender/io/collada/collada_internal.cpp b/source/blender/io/collada/collada_internal.cpp
index 355aa5c22f0..bd6f496c8ec 100644
--- a/source/blender/io/collada/collada_internal.cpp
+++ b/source/blender/io/collada/collada_internal.cpp
@@ -162,7 +162,7 @@ void UnitConverter::calculate_scale(Scene &sce)
* Translation map.
* Used to translate every COLLADA id to a valid id, no matter what "wrong" letters may be
* included. Look at the IDREF XSD declaration for more.
- * Follows strictly the COLLADA XSD declaration which explicitly allows non-english chars,
+ * Follows strictly the COLLADA XSD declaration which explicitly allows non-English chars,
* like special chars (e.g. micro sign), umlauts and so on.
* The COLLADA spec also allows additional chars for member access ('.'), these
* must obviously be removed too, otherwise they would be heavily misinterpreted.
diff --git a/source/blender/io/common/CMakeLists.txt b/source/blender/io/common/CMakeLists.txt
index 7e39af32f11..2aaf5d57fd6 100644
--- a/source/blender/io/common/CMakeLists.txt
+++ b/source/blender/io/common/CMakeLists.txt
@@ -37,6 +37,7 @@ set(SRC
IO_abstract_hierarchy_iterator.h
IO_dupli_persistent_id.hh
+ IO_types.h
intern/dupli_parent_finder.hh
)
diff --git a/source/blender/io/common/IO_types.h b/source/blender/io/common/IO_types.h
new file mode 100644
index 00000000000..4570e29f6ed
--- /dev/null
+++ b/source/blender/io/common/IO_types.h
@@ -0,0 +1,34 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2021 Blender Foundation.
+ * All rights reserved.
+ */
+#pragma once
+
+/* The CacheArchiveHandle struct is only used for anonymous pointers,
+ * to interface between C and C++ code. This is currently used
+ * to hide pointers to alembic ArchiveReader and USDStageReader. */
+struct CacheArchiveHandle {
+ int unused;
+};
+
+/* The CacheReader struct is only used for anonymous pointers,
+ * to interface between C and C++ code. This is currently used
+ * to hide pointers to AbcObjectReader and USDPrimReader
+ * (or subclasses thereof). */
+struct CacheReader {
+ int unused;
+};
diff --git a/source/blender/io/gpencil/CMakeLists.txt b/source/blender/io/gpencil/CMakeLists.txt
index fec95be6aa8..4af8b506bd5 100644
--- a/source/blender/io/gpencil/CMakeLists.txt
+++ b/source/blender/io/gpencil/CMakeLists.txt
@@ -33,6 +33,7 @@ set(INC
../../../../intern/clog
../../../../intern/guardedalloc
../../../../intern/utfconv
+ ../../../../extern/nanosvg
)
set(INC_SYS
@@ -44,9 +45,6 @@ set(SRC
intern/gpencil_io_import_base.cc
intern/gpencil_io_import_svg.cc
- # This line must be removed if NanoSVG is moved to extern
- nanosvg/nanosvg.h
-
gpencil_io.h
intern/gpencil_io_base.hh
intern/gpencil_io_export_base.hh
diff --git a/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc b/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc
index db6bbc7768e..941d1137f4d 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc
+++ b/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc
@@ -42,7 +42,7 @@
#define NANOSVG_ALL_COLOR_KEYWORDS
#define NANOSVG_IMPLEMENTATION
-#include "nanosvg/nanosvg.h"
+#include "nanosvg.h"
using blender::MutableSpan;
diff --git a/source/blender/io/gpencil/nanosvg/nanosvg.h b/source/blender/io/gpencil/nanosvg/nanosvg.h
deleted file mode 100644
index 94dad37861a..00000000000
--- a/source/blender/io/gpencil/nanosvg/nanosvg.h
+++ /dev/null
@@ -1,3327 +0,0 @@
-/*
- * Copyright (c) 2013-14 `Mikko Mononen <memon@inside.org>`
- *
- * This software is provided 'as-is', without any express or implied
- * warranty. In no event will the authors be held liable for any damages
- * arising from the use of this software.
- *
- * Permission is granted to anyone to use this software for any purpose,
- * including commercial applications, and to alter it and redistribute it
- * freely, subject to the following restrictions:
- *
- * 1. The origin of this software must not be misrepresented; you must not
- * claim that you wrote the original software. If you use this software
- * in a product, an acknowledgment in the product documentation would be
- * appreciated but is not required.
- * 2. Altered source versions must be plainly marked as such, and must not be
- * misrepresented as being the original software.
- * 3. This notice may not be removed or altered from any source distribution.
- *
- * The SVG parser is based on Anti-Grain Geometry 2.4 SVG example
- * Copyright (C) 2002-2004 Maxim Shemanarev (McSeem) (http://www.antigrain.com/)
- *
- * Arc calculation code based on canvg (https://code.google.com/p/canvg/)
- *
- * Bounding box calculation based on
- * http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
- *
- * This is a modified version for Blender used by importers.
- *
- */
-
-#ifndef NANOSVG_H
-#define NANOSVG_H
-
-#ifndef NANOSVG_CPLUSPLUS
-# ifdef __cplusplus
-extern "C" {
-# endif
-#endif
-
-// NanoSVG is a simple stupid single-header-file SVG parse. The output of the parser is a list of
-// cubic bezier shapes.
-//
-// The library suits well for anything from rendering scalable icons in your editor application to
-// prototyping a game.
-//
-// NanoSVG supports a wide range of SVG features, but something may be missing, feel free to create
-// a pull request!
-//
-// The shapes in the SVG images are transformed by the viewBox and converted to specified units.
-// That is, you should get the same looking data as your designed in your favorite app.
-//
-// NanoSVG can return the paths in few different units. For example if you want to render an image,
-// you may choose to get the paths in pixels, or if you are feeding the data into a CNC-cutter, you
-// may want to use millimeters.
-//
-// The units passed to NanoSVG should be one of: 'px', 'pt', 'pc' 'mm', 'cm', or 'in'.
-// DPI (dots-per-inch) controls how the unit conversion is done.
-//
-// If you don't know or care about the units stuff, "px" and 96 should get you going.
-
-/* Example Usage:
- // Load SVG
- NSVGimage* image;
- image = nsvgParseFromFile("test.svg", "px", 96);
- printf("size: %f x %f\n", image->width, image->height);
- // Use...
- for (NSVGshape *shape = image->shapes; shape != NULL; shape = shape->next) {
- for (NSVGpath *path = shape->paths; path != NULL; path = path->next) {
- for (int i = 0; i < path->npts-1; i += 3) {
- float* p = &path->pts[i*2];
- drawCubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7]);
- }
- }
- }
- // Delete
- nsvgDelete(image);
-*/
-
-enum NSVGpaintType {
- NSVG_PAINT_NONE = 0,
- NSVG_PAINT_COLOR = 1,
- NSVG_PAINT_LINEAR_GRADIENT = 2,
- NSVG_PAINT_RADIAL_GRADIENT = 3
-};
-
-enum NSVGspreadType { NSVG_SPREAD_PAD = 0, NSVG_SPREAD_REFLECT = 1, NSVG_SPREAD_REPEAT = 2 };
-
-enum NSVGlineJoin { NSVG_JOIN_MITER = 0, NSVG_JOIN_ROUND = 1, NSVG_JOIN_BEVEL = 2 };
-
-enum NSVGlineCap { NSVG_CAP_BUTT = 0, NSVG_CAP_ROUND = 1, NSVG_CAP_SQUARE = 2 };
-
-enum NSVGfillRule { NSVG_FILLRULE_NONZERO = 0, NSVG_FILLRULE_EVENODD = 1 };
-
-enum NSVGflags { NSVG_FLAGS_VISIBLE = 0x01 };
-
-typedef struct NSVGgradientStop {
- unsigned int color;
- float offset;
-} NSVGgradientStop;
-
-typedef struct NSVGgradient {
- float xform[6];
- char spread;
- float fx, fy;
- int nstops;
- NSVGgradientStop stops[1];
-} NSVGgradient;
-
-typedef struct NSVGpaint {
- char type;
- union {
- unsigned int color;
- NSVGgradient *gradient;
- };
-} NSVGpaint;
-
-typedef struct NSVGpath {
- float *pts; // Cubic bezier points: x0,y0, [cpx1,cpx1,cpx2,cpy2,x1,y1], ...
- int npts; // Total number of bezier points.
- char closed; // Flag indicating if shapes should be treated as closed.
- float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy].
- struct NSVGpath *next; // Pointer to next path, or NULL if last element.
-} NSVGpath;
-
-typedef struct NSVGshape {
- char id[64]; // Optional 'id' attr of the shape or its group
- /* Blender: Parent ID used for layer creation. */
- char id_parent[64];
- NSVGpaint fill; // Fill paint
- NSVGpaint stroke; // Stroke paint
- float opacity; // Opacity of the shape.
- float strokeWidth; // Stroke width (scaled).
- float strokeDashOffset; // Stroke dash offset (scaled).
- float strokeDashArray[8]; // Stroke dash array (scaled).
- char strokeDashCount; // Number of dash values in dash array.
- char strokeLineJoin; // Stroke join type.
- char strokeLineCap; // Stroke cap type.
- float miterLimit; // Miter limit
- char fillRule; // Fill rule, see NSVGfillRule.
- unsigned char flags; // Logical or of NSVG_FLAGS_* flags
- float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy].
- NSVGpath *paths; // Linked list of paths in the image.
- struct NSVGshape *next; // Pointer to next shape, or NULL if last element.
-} NSVGshape;
-
-typedef struct NSVGimage {
- float width; // Width of the image.
- float height; // Height of the image.
- NSVGshape *shapes; // Linked list of shapes in the image.
-} NSVGimage;
-
-// Parses SVG file from a file, returns SVG image as paths.
-NSVGimage *nsvgParseFromFile(const char *filename, const char *units, float dpi);
-
-// Parses SVG file from a null terminated string, returns SVG image as paths.
-// Important note: changes the string.
-NSVGimage *nsvgParse(char *input, const char *units, float dpi);
-
-// Duplicates a path.
-NSVGpath *nsvgDuplicatePath(NSVGpath *p);
-
-// Deletes an image.
-void nsvgDelete(NSVGimage *image);
-
-#ifndef NANOSVG_CPLUSPLUS
-# ifdef __cplusplus
-}
-# endif
-#endif
-
-#endif // NANOSVG_H
-
-#ifdef NANOSVG_IMPLEMENTATION
-
-#include <math.h>
-#include <stdlib.h>
-#include <string.h>
-
-#define NSVG_PI (3.14159265358979323846264338327f)
-#define NSVG_KAPPA90 \
- (0.5522847493f) // Length proportional to radius of a cubic bezier handle for 90deg arcs.
-
-#define NSVG_ALIGN_MIN 0
-#define NSVG_ALIGN_MID 1
-#define NSVG_ALIGN_MAX 2
-#define NSVG_ALIGN_NONE 0
-#define NSVG_ALIGN_MEET 1
-#define NSVG_ALIGN_SLICE 2
-
-#define NSVG_NOTUSED(v) \
- do { \
- (void)(1 ? (void)0 : ((void)(v))); \
- } while (0)
-#define NSVG_RGB(r, g, b) (((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16))
-
-#ifdef _MSC_VER
-# pragma warning(disable : 4996) // Switch off security warnings
-# pragma warning(disable : 4100) // Switch off unreferenced formal parameter warnings
-# ifdef __cplusplus
-# define NSVG_INLINE inline
-# else
-# define NSVG_INLINE
-# endif
-#else
-# define NSVG_INLINE inline
-#endif
-
-static int nsvg__isspace(char c)
-{
- return strchr(" \t\n\v\f\r", c) != 0;
-}
-
-static int nsvg__isdigit(char c)
-{
- return c >= '0' && c <= '9';
-}
-
-static NSVG_INLINE float nsvg__minf(float a, float b)
-{
- return a < b ? a : b;
-}
-static NSVG_INLINE float nsvg__maxf(float a, float b)
-{
- return a > b ? a : b;
-}
-
-// Simple XML parser
-
-#define NSVG_XML_TAG 1
-#define NSVG_XML_CONTENT 2
-#define NSVG_XML_MAX_ATTRIBS 256
-
-static void nsvg__parseContent(char *s, void (*contentCb)(void *ud, const char *s), void *ud)
-{
- // Trim start white spaces
- while (*s && nsvg__isspace(*s))
- s++;
- if (!*s)
- return;
-
- if (contentCb)
- (*contentCb)(ud, s);
-}
-
-static void nsvg__parseElement(char *s,
- void (*startelCb)(void *ud, const char *el, const char **attr),
- void (*endelCb)(void *ud, const char *el),
- void *ud)
-{
- const char *attr[NSVG_XML_MAX_ATTRIBS];
- int nattr = 0;
- char *name;
- int start = 0;
- int end = 0;
- char quote;
-
- // Skip white space after the '<'
- while (*s && nsvg__isspace(*s))
- s++;
-
- // Check if the tag is end tag
- if (*s == '/') {
- s++;
- end = 1;
- }
- else {
- start = 1;
- }
-
- // Skip comments, data and preprocessor stuff.
- if (!*s || *s == '?' || *s == '!')
- return;
-
- // Get tag name
- name = s;
- while (*s && !nsvg__isspace(*s))
- s++;
- if (*s) {
- *s++ = '\0';
- }
-
- // Get attribs
- while (!end && *s && nattr < NSVG_XML_MAX_ATTRIBS - 3) {
- char *name = NULL;
- char *value = NULL;
-
- // Skip white space before the attrib name
- while (*s && nsvg__isspace(*s))
- s++;
- if (!*s)
- break;
- if (*s == '/') {
- end = 1;
- break;
- }
- name = s;
- // Find end of the attrib name.
- while (*s && !nsvg__isspace(*s) && *s != '=')
- s++;
- if (*s) {
- *s++ = '\0';
- }
- // Skip until the beginning of the value.
- while (*s && *s != '\"' && *s != '\'')
- s++;
- if (!*s)
- break;
- quote = *s;
- s++;
- // Store value and find the end of it.
- value = s;
- while (*s && *s != quote)
- s++;
- if (*s) {
- *s++ = '\0';
- }
-
- // Store only well formed attributes
- if (name && value) {
- attr[nattr++] = name;
- attr[nattr++] = value;
- }
- }
-
- // List terminator
- attr[nattr++] = 0;
- attr[nattr++] = 0;
-
- // Call callbacks.
- if (start && startelCb)
- (*startelCb)(ud, name, attr);
- if (end && endelCb)
- (*endelCb)(ud, name);
-}
-
-static int nsvg__parseXML(char *input,
- void (*startelCb)(void *ud, const char *el, const char **attr),
- void (*endelCb)(void *ud, const char *el),
- void (*contentCb)(void *ud, const char *s),
- void *ud)
-{
- char *s = input;
- char *mark = s;
- int state = NSVG_XML_CONTENT;
- while (*s) {
- if (*s == '<' && state == NSVG_XML_CONTENT) {
- // Start of a tag
- *s++ = '\0';
- nsvg__parseContent(mark, contentCb, ud);
- mark = s;
- state = NSVG_XML_TAG;
- }
- else if (*s == '>' && state == NSVG_XML_TAG) {
- // Start of a content or new tag.
- *s++ = '\0';
- nsvg__parseElement(mark, startelCb, endelCb, ud);
- mark = s;
- state = NSVG_XML_CONTENT;
- }
- else {
- s++;
- }
- }
-
- return 1;
-}
-
-/* Simple SVG parser. */
-
-#define NSVG_MAX_ATTR 128
-#define NSVG_MAX_BREADCRUMB 5
-
-enum NSVGgradientUnits { NSVG_USER_SPACE = 0, NSVG_OBJECT_SPACE = 1 };
-
-#define NSVG_MAX_DASHES 8
-
-enum NSVGunits {
- NSVG_UNITS_USER,
- NSVG_UNITS_PX,
- NSVG_UNITS_PT,
- NSVG_UNITS_PC,
- NSVG_UNITS_MM,
- NSVG_UNITS_CM,
- NSVG_UNITS_IN,
- NSVG_UNITS_PERCENT,
- NSVG_UNITS_EM,
- NSVG_UNITS_EX
-};
-
-typedef struct NSVGcoordinate {
- float value;
- int units;
-} NSVGcoordinate;
-
-typedef struct NSVGlinearData {
- NSVGcoordinate x1, y1, x2, y2;
-} NSVGlinearData;
-
-typedef struct NSVGradialData {
- NSVGcoordinate cx, cy, r, fx, fy;
-} NSVGradialData;
-
-typedef struct NSVGgradientData {
- char id[64];
- char ref[64];
- char type;
- union {
- NSVGlinearData linear;
- NSVGradialData radial;
- };
- char spread;
- char units;
- float xform[6];
- int nstops;
- NSVGgradientStop *stops;
- struct NSVGgradientData *next;
-} NSVGgradientData;
-
-typedef struct NSVGattrib {
- char id[64];
- float xform[6];
- unsigned int fillColor;
- unsigned int strokeColor;
- float opacity;
- float fillOpacity;
- float strokeOpacity;
- char fillGradient[64];
- char strokeGradient[64];
- float strokeWidth;
- float strokeDashOffset;
- float strokeDashArray[NSVG_MAX_DASHES];
- int strokeDashCount;
- char strokeLineJoin;
- char strokeLineCap;
- float miterLimit;
- char fillRule;
- float fontSize;
- unsigned int stopColor;
- float stopOpacity;
- float stopOffset;
- char hasFill;
- char hasStroke;
- char visible;
-} NSVGattrib;
-
-typedef struct NSVGparser {
- NSVGattrib attr[NSVG_MAX_ATTR];
- int attrHead;
- float *pts;
- int npts;
- int cpts;
- NSVGpath *plist;
- NSVGimage *image;
- NSVGgradientData *gradients;
- NSVGshape *shapesTail;
- float viewMinx, viewMiny, viewWidth, viewHeight;
- int alignX, alignY, alignType;
- float dpi;
- char pathFlag;
- char defsFlag;
- /** Blender breadcrumb for layers. */
- char breadcrumb[NSVG_MAX_BREADCRUMB][64];
- /** Blender number of elements in breadcrumb. */
- int breadcrumb_len;
-} NSVGparser;
-
-static void nsvg__xformIdentity(float *t)
-{
- t[0] = 1.0f;
- t[1] = 0.0f;
- t[2] = 0.0f;
- t[3] = 1.0f;
- t[4] = 0.0f;
- t[5] = 0.0f;
-}
-
-static void nsvg__xformSetTranslation(float *t, float tx, float ty)
-{
- t[0] = 1.0f;
- t[1] = 0.0f;
- t[2] = 0.0f;
- t[3] = 1.0f;
- t[4] = tx;
- t[5] = ty;
-}
-
-static void nsvg__xformSetScale(float *t, float sx, float sy)
-{
- t[0] = sx;
- t[1] = 0.0f;
- t[2] = 0.0f;
- t[3] = sy;
- t[4] = 0.0f;
- t[5] = 0.0f;
-}
-
-static void nsvg__xformSetSkewX(float *t, float a)
-{
- t[0] = 1.0f;
- t[1] = 0.0f;
- t[2] = tanf(a);
- t[3] = 1.0f;
- t[4] = 0.0f;
- t[5] = 0.0f;
-}
-
-static void nsvg__xformSetSkewY(float *t, float a)
-{
- t[0] = 1.0f;
- t[1] = tanf(a);
- t[2] = 0.0f;
- t[3] = 1.0f;
- t[4] = 0.0f;
- t[5] = 0.0f;
-}
-
-static void nsvg__xformSetRotation(float *t, float a)
-{
- float cs = cosf(a), sn = sinf(a);
- t[0] = cs;
- t[1] = sn;
- t[2] = -sn;
- t[3] = cs;
- t[4] = 0.0f;
- t[5] = 0.0f;
-}
-
-static void nsvg__xformMultiply(float *t, float *s)
-{
- float t0 = t[0] * s[0] + t[1] * s[2];
- float t2 = t[2] * s[0] + t[3] * s[2];
- float t4 = t[4] * s[0] + t[5] * s[2] + s[4];
- t[1] = t[0] * s[1] + t[1] * s[3];
- t[3] = t[2] * s[1] + t[3] * s[3];
- t[5] = t[4] * s[1] + t[5] * s[3] + s[5];
- t[0] = t0;
- t[2] = t2;
- t[4] = t4;
-}
-
-static void nsvg__xformInverse(float *inv, float *t)
-{
- double invdet, det = (double)t[0] * t[3] - (double)t[2] * t[1];
- if (det > -1e-6 && det < 1e-6) {
- nsvg__xformIdentity(t);
- return;
- }
- invdet = 1.0 / det;
- inv[0] = (float)(t[3] * invdet);
- inv[2] = (float)(-t[2] * invdet);
- inv[4] = (float)(((double)t[2] * t[5] - (double)t[3] * t[4]) * invdet);
- inv[1] = (float)(-t[1] * invdet);
- inv[3] = (float)(t[0] * invdet);
- inv[5] = (float)(((double)t[1] * t[4] - (double)t[0] * t[5]) * invdet);
-}
-
-static void nsvg__xformPremultiply(float *t, float *s)
-{
- float s2[6];
- memcpy(s2, s, sizeof(float) * 6);
- nsvg__xformMultiply(s2, t);
- memcpy(t, s2, sizeof(float) * 6);
-}
-
-static void nsvg__xformPoint(float *dx, float *dy, float x, float y, float *t)
-{
- *dx = x * t[0] + y * t[2] + t[4];
- *dy = x * t[1] + y * t[3] + t[5];
-}
-
-static void nsvg__xformVec(float *dx, float *dy, float x, float y, float *t)
-{
- *dx = x * t[0] + y * t[2];
- *dy = x * t[1] + y * t[3];
-}
-
-#define NSVG_EPSILON (1e-12)
-
-static int nsvg__ptInBounds(float *pt, float *bounds)
-{
- return pt[0] >= bounds[0] && pt[0] <= bounds[2] && pt[1] >= bounds[1] && pt[1] <= bounds[3];
-}
-
-static double nsvg__evalBezier(double t, double p0, double p1, double p2, double p3)
-{
- double it = 1.0 - t;
- return it * it * it * p0 + 3.0 * it * it * t * p1 + 3.0 * it * t * t * p2 + t * t * t * p3;
-}
-
-static void nsvg__curveBounds(float *bounds, float *curve)
-{
- int i, j, count;
- double roots[2], a, b, c, b2ac, t, v;
- float *v0 = &curve[0];
- float *v1 = &curve[2];
- float *v2 = &curve[4];
- float *v3 = &curve[6];
-
- // Start the bounding box by end points
- bounds[0] = nsvg__minf(v0[0], v3[0]);
- bounds[1] = nsvg__minf(v0[1], v3[1]);
- bounds[2] = nsvg__maxf(v0[0], v3[0]);
- bounds[3] = nsvg__maxf(v0[1], v3[1]);
-
- // Bezier curve fits inside the convex hull of it's control points.
- // If control points are inside the bounds, we're done.
- if (nsvg__ptInBounds(v1, bounds) && nsvg__ptInBounds(v2, bounds))
- return;
-
- // Add bezier curve inflection points in X and Y.
- for (i = 0; i < 2; i++) {
- a = -3.0 * v0[i] + 9.0 * v1[i] - 9.0 * v2[i] + 3.0 * v3[i];
- b = 6.0 * v0[i] - 12.0 * v1[i] + 6.0 * v2[i];
- c = 3.0 * v1[i] - 3.0 * v0[i];
- count = 0;
- if (fabs(a) < NSVG_EPSILON) {
- if (fabs(b) > NSVG_EPSILON) {
- t = -c / b;
- if (t > NSVG_EPSILON && t < 1.0 - NSVG_EPSILON)
- roots[count++] = t;
- }
- }
- else {
- b2ac = b * b - 4.0 * c * a;
- if (b2ac > NSVG_EPSILON) {
- t = (-b + sqrt(b2ac)) / (2.0 * a);
- if (t > NSVG_EPSILON && t < 1.0 - NSVG_EPSILON)
- roots[count++] = t;
- t = (-b - sqrt(b2ac)) / (2.0 * a);
- if (t > NSVG_EPSILON && t < 1.0 - NSVG_EPSILON)
- roots[count++] = t;
- }
- }
- for (j = 0; j < count; j++) {
- v = nsvg__evalBezier(roots[j], v0[i], v1[i], v2[i], v3[i]);
- bounds[0 + i] = nsvg__minf(bounds[0 + i], (float)v);
- bounds[2 + i] = nsvg__maxf(bounds[2 + i], (float)v);
- }
- }
-}
-
-static NSVGparser *nsvg__createParser()
-{
- NSVGparser *p;
- p = (NSVGparser *)malloc(sizeof(NSVGparser));
- if (p == NULL)
- goto error;
- memset(p, 0, sizeof(NSVGparser));
-
- p->image = (NSVGimage *)malloc(sizeof(NSVGimage));
- if (p->image == NULL)
- goto error;
- memset(p->image, 0, sizeof(NSVGimage));
-
- // Init style
- nsvg__xformIdentity(p->attr[0].xform);
- memset(p->attr[0].id, 0, sizeof p->attr[0].id);
- p->attr[0].fillColor = NSVG_RGB(0, 0, 0);
- p->attr[0].strokeColor = NSVG_RGB(0, 0, 0);
- p->attr[0].opacity = 1;
- p->attr[0].fillOpacity = 1;
- p->attr[0].strokeOpacity = 1;
- p->attr[0].stopOpacity = 1;
- p->attr[0].strokeWidth = 1;
- p->attr[0].strokeLineJoin = NSVG_JOIN_MITER;
- p->attr[0].strokeLineCap = NSVG_CAP_BUTT;
- p->attr[0].miterLimit = 4;
- p->attr[0].fillRule = NSVG_FILLRULE_NONZERO;
- p->attr[0].hasFill = 1;
- p->attr[0].visible = 1;
-
- return p;
-
-error:
- if (p) {
- if (p->image)
- free(p->image);
- free(p);
- }
- return NULL;
-}
-
-static void nsvg__deletePaths(NSVGpath *path)
-{
- while (path) {
- NSVGpath *next = path->next;
- if (path->pts != NULL)
- free(path->pts);
- free(path);
- path = next;
- }
-}
-
-static void nsvg__deletePaint(NSVGpaint *paint)
-{
- if (paint->type == NSVG_PAINT_LINEAR_GRADIENT || paint->type == NSVG_PAINT_RADIAL_GRADIENT)
- free(paint->gradient);
-}
-
-static void nsvg__deleteGradientData(NSVGgradientData *grad)
-{
- NSVGgradientData *next;
- while (grad != NULL) {
- next = grad->next;
- free(grad->stops);
- free(grad);
- grad = next;
- }
-}
-
-static void nsvg__deleteParser(NSVGparser *p)
-{
- if (p != NULL) {
- nsvg__deletePaths(p->plist);
- nsvg__deleteGradientData(p->gradients);
- nsvgDelete(p->image);
- free(p->pts);
- free(p);
- }
-}
-
-static void nsvg__resetPath(NSVGparser *p)
-{
- p->npts = 0;
-}
-
-static void nsvg__addPoint(NSVGparser *p, float x, float y)
-{
- if (p->npts + 1 > p->cpts) {
- p->cpts = p->cpts ? p->cpts * 2 : 8;
- p->pts = (float *)realloc(p->pts, p->cpts * 2 * sizeof(float));
- if (!p->pts)
- return;
- }
- p->pts[p->npts * 2 + 0] = x;
- p->pts[p->npts * 2 + 1] = y;
- p->npts++;
-}
-
-static void nsvg__moveTo(NSVGparser *p, float x, float y)
-{
- if (p->npts > 0) {
- p->pts[(p->npts - 1) * 2 + 0] = x;
- p->pts[(p->npts - 1) * 2 + 1] = y;
- }
- else {
- nsvg__addPoint(p, x, y);
- }
-}
-
-static void nsvg__lineTo(NSVGparser *p, float x, float y)
-{
- float px, py, dx, dy;
- if (p->npts > 0) {
- px = p->pts[(p->npts - 1) * 2 + 0];
- py = p->pts[(p->npts - 1) * 2 + 1];
- dx = x - px;
- dy = y - py;
- nsvg__addPoint(p, px + dx / 3.0f, py + dy / 3.0f);
- nsvg__addPoint(p, x - dx / 3.0f, y - dy / 3.0f);
- nsvg__addPoint(p, x, y);
- }
-}
-
-static void nsvg__cubicBezTo(
- NSVGparser *p, float cpx1, float cpy1, float cpx2, float cpy2, float x, float y)
-{
- if (p->npts > 0) {
- nsvg__addPoint(p, cpx1, cpy1);
- nsvg__addPoint(p, cpx2, cpy2);
- nsvg__addPoint(p, x, y);
- }
-}
-
-static NSVGattrib *nsvg__getAttr(NSVGparser *p)
-{
- return &p->attr[p->attrHead];
-}
-
-static void nsvg__pushAttr(NSVGparser *p)
-{
- if (p->attrHead < NSVG_MAX_ATTR - 1) {
- p->attrHead++;
- memcpy(&p->attr[p->attrHead], &p->attr[p->attrHead - 1], sizeof(NSVGattrib));
- }
-}
-
-static void nsvg__popAttr(NSVGparser *p)
-{
- if (p->attrHead > 0)
- p->attrHead--;
-}
-
-static float nsvg__actualOrigX(NSVGparser *p)
-{
- return p->viewMinx;
-}
-
-static float nsvg__actualOrigY(NSVGparser *p)
-{
- return p->viewMiny;
-}
-
-static float nsvg__actualWidth(NSVGparser *p)
-{
- return p->viewWidth;
-}
-
-static float nsvg__actualHeight(NSVGparser *p)
-{
- return p->viewHeight;
-}
-
-static float nsvg__actualLength(NSVGparser *p)
-{
- float w = nsvg__actualWidth(p), h = nsvg__actualHeight(p);
- return sqrtf(w * w + h * h) / sqrtf(2.0f);
-}
-
-static float nsvg__convertToPixels(NSVGparser *p, NSVGcoordinate c, float orig, float length)
-{
- NSVGattrib *attr = nsvg__getAttr(p);
- switch (c.units) {
- case NSVG_UNITS_USER:
- return c.value;
- case NSVG_UNITS_PX:
- return c.value;
- case NSVG_UNITS_PT:
- return c.value / 72.0f * p->dpi;
- case NSVG_UNITS_PC:
- return c.value / 6.0f * p->dpi;
- case NSVG_UNITS_MM:
- return c.value / 25.4f * p->dpi;
- case NSVG_UNITS_CM:
- return c.value / 2.54f * p->dpi;
- case NSVG_UNITS_IN:
- return c.value * p->dpi;
- case NSVG_UNITS_EM:
- return c.value * attr->fontSize;
- case NSVG_UNITS_EX:
- return c.value * attr->fontSize * 0.52f; // x-height of Helvetica.
- case NSVG_UNITS_PERCENT:
- return orig + c.value / 100.0f * length;
- default:
- return c.value;
- }
- return c.value;
-}
-
-static NSVGgradientData *nsvg__findGradientData(NSVGparser *p, const char *id)
-{
- NSVGgradientData *grad = p->gradients;
- if (id == NULL || *id == '\0')
- return NULL;
- while (grad != NULL) {
- if (strcmp(grad->id, id) == 0)
- return grad;
- grad = grad->next;
- }
- return NULL;
-}
-
-static NSVGgradient *nsvg__createGradient(NSVGparser *p,
- const char *id,
- const float *localBounds,
- char *paintType)
-{
- NSVGattrib *attr = nsvg__getAttr(p);
- NSVGgradientData *data = NULL;
- NSVGgradientData *ref = NULL;
- NSVGgradientStop *stops = NULL;
- NSVGgradient *grad;
- float ox, oy, sw, sh, sl;
- int nstops = 0;
- int refIter;
-
- data = nsvg__findGradientData(p, id);
- if (data == NULL)
- return NULL;
-
- // TODO: use ref to fill in all unset values too.
- ref = data;
- refIter = 0;
- while (ref != NULL) {
- NSVGgradientData *nextRef = NULL;
- if (stops == NULL && ref->stops != NULL) {
- stops = ref->stops;
- nstops = ref->nstops;
- break;
- }
- nextRef = nsvg__findGradientData(p, ref->ref);
- if (nextRef == ref)
- break; // prevent infite loops on malformed data
- ref = nextRef;
- refIter++;
- if (refIter > 32)
- break; // prevent infite loops on malformed data
- }
- if (stops == NULL)
- return NULL;
-
- grad = (NSVGgradient *)malloc(sizeof(NSVGgradient) + sizeof(NSVGgradientStop) * (nstops - 1));
- if (grad == NULL)
- return NULL;
-
- // The shape width and height.
- if (data->units == NSVG_OBJECT_SPACE) {
- ox = localBounds[0];
- oy = localBounds[1];
- sw = localBounds[2] - localBounds[0];
- sh = localBounds[3] - localBounds[1];
- }
- else {
- ox = nsvg__actualOrigX(p);
- oy = nsvg__actualOrigY(p);
- sw = nsvg__actualWidth(p);
- sh = nsvg__actualHeight(p);
- }
- sl = sqrtf(sw * sw + sh * sh) / sqrtf(2.0f);
-
- if (data->type == NSVG_PAINT_LINEAR_GRADIENT) {
- float x1, y1, x2, y2, dx, dy;
- x1 = nsvg__convertToPixels(p, data->linear.x1, ox, sw);
- y1 = nsvg__convertToPixels(p, data->linear.y1, oy, sh);
- x2 = nsvg__convertToPixels(p, data->linear.x2, ox, sw);
- y2 = nsvg__convertToPixels(p, data->linear.y2, oy, sh);
- // Calculate transform aligned to the line
- dx = x2 - x1;
- dy = y2 - y1;
- grad->xform[0] = dy;
- grad->xform[1] = -dx;
- grad->xform[2] = dx;
- grad->xform[3] = dy;
- grad->xform[4] = x1;
- grad->xform[5] = y1;
- }
- else {
- float cx, cy, fx, fy, r;
- cx = nsvg__convertToPixels(p, data->radial.cx, ox, sw);
- cy = nsvg__convertToPixels(p, data->radial.cy, oy, sh);
- fx = nsvg__convertToPixels(p, data->radial.fx, ox, sw);
- fy = nsvg__convertToPixels(p, data->radial.fy, oy, sh);
- r = nsvg__convertToPixels(p, data->radial.r, 0, sl);
- // Calculate transform aligned to the circle
- grad->xform[0] = r;
- grad->xform[1] = 0;
- grad->xform[2] = 0;
- grad->xform[3] = r;
- grad->xform[4] = cx;
- grad->xform[5] = cy;
- grad->fx = fx / r;
- grad->fy = fy / r;
- }
-
- nsvg__xformMultiply(grad->xform, data->xform);
- nsvg__xformMultiply(grad->xform, attr->xform);
-
- grad->spread = data->spread;
- memcpy(grad->stops, stops, nstops * sizeof(NSVGgradientStop));
- grad->nstops = nstops;
-
- *paintType = data->type;
-
- return grad;
-}
-
-static float nsvg__getAverageScale(float *t)
-{
- float sx = sqrtf(t[0] * t[0] + t[2] * t[2]);
- float sy = sqrtf(t[1] * t[1] + t[3] * t[3]);
- return (sx + sy) * 0.5f;
-}
-
-static void nsvg__getLocalBounds(float *bounds, NSVGshape *shape, float *xform)
-{
- NSVGpath *path;
- float curve[4 * 2], curveBounds[4];
- int i, first = 1;
- for (path = shape->paths; path != NULL; path = path->next) {
- nsvg__xformPoint(&curve[0], &curve[1], path->pts[0], path->pts[1], xform);
- for (i = 0; i < path->npts - 1; i += 3) {
- nsvg__xformPoint(
- &curve[2], &curve[3], path->pts[(i + 1) * 2], path->pts[(i + 1) * 2 + 1], xform);
- nsvg__xformPoint(
- &curve[4], &curve[5], path->pts[(i + 2) * 2], path->pts[(i + 2) * 2 + 1], xform);
- nsvg__xformPoint(
- &curve[6], &curve[7], path->pts[(i + 3) * 2], path->pts[(i + 3) * 2 + 1], xform);
- nsvg__curveBounds(curveBounds, curve);
- if (first) {
- bounds[0] = curveBounds[0];
- bounds[1] = curveBounds[1];
- bounds[2] = curveBounds[2];
- bounds[3] = curveBounds[3];
- first = 0;
- }
- else {
- bounds[0] = nsvg__minf(bounds[0], curveBounds[0]);
- bounds[1] = nsvg__minf(bounds[1], curveBounds[1]);
- bounds[2] = nsvg__maxf(bounds[2], curveBounds[2]);
- bounds[3] = nsvg__maxf(bounds[3], curveBounds[3]);
- }
- curve[0] = curve[6];
- curve[1] = curve[7];
- }
- }
-}
-
-static void nsvg__addShape(NSVGparser *p)
-{
- NSVGattrib *attr = nsvg__getAttr(p);
- float scale = 1.0f;
- NSVGshape *shape;
- NSVGpath *path;
- int i;
-
- if (p->plist == NULL)
- return;
-
- shape = (NSVGshape *)malloc(sizeof(NSVGshape));
- if (shape == NULL)
- goto error;
- memset(shape, 0, sizeof(NSVGshape));
-
- memcpy(shape->id, attr->id, sizeof shape->id);
- /* Copy parent id from breadcrumb. */
- if (p->breadcrumb_len > 0) {
- memcpy(shape->id_parent, p->breadcrumb[0], sizeof shape->id_parent);
- }
- else {
- memcpy(shape->id_parent, attr->id, sizeof shape->id_parent);
- }
-
- scale = nsvg__getAverageScale(attr->xform);
- shape->strokeWidth = attr->strokeWidth * scale;
- shape->strokeDashOffset = attr->strokeDashOffset * scale;
- shape->strokeDashCount = (char)attr->strokeDashCount;
- for (i = 0; i < attr->strokeDashCount; i++)
- shape->strokeDashArray[i] = attr->strokeDashArray[i] * scale;
- shape->strokeLineJoin = attr->strokeLineJoin;
- shape->strokeLineCap = attr->strokeLineCap;
- shape->miterLimit = attr->miterLimit;
- shape->fillRule = attr->fillRule;
- shape->opacity = attr->opacity;
-
- shape->paths = p->plist;
- p->plist = NULL;
-
- // Calculate shape bounds
- shape->bounds[0] = shape->paths->bounds[0];
- shape->bounds[1] = shape->paths->bounds[1];
- shape->bounds[2] = shape->paths->bounds[2];
- shape->bounds[3] = shape->paths->bounds[3];
- for (path = shape->paths->next; path != NULL; path = path->next) {
- shape->bounds[0] = nsvg__minf(shape->bounds[0], path->bounds[0]);
- shape->bounds[1] = nsvg__minf(shape->bounds[1], path->bounds[1]);
- shape->bounds[2] = nsvg__maxf(shape->bounds[2], path->bounds[2]);
- shape->bounds[3] = nsvg__maxf(shape->bounds[3], path->bounds[3]);
- }
-
- // Set fill
- if (attr->hasFill == 0) {
- shape->fill.type = NSVG_PAINT_NONE;
- }
- else if (attr->hasFill == 1) {
- shape->fill.type = NSVG_PAINT_COLOR;
- shape->fill.color = attr->fillColor;
- shape->fill.color |= (unsigned int)(attr->fillOpacity * 255) << 24;
- }
- else if (attr->hasFill == 2) {
- float inv[6], localBounds[4];
- nsvg__xformInverse(inv, attr->xform);
- nsvg__getLocalBounds(localBounds, shape, inv);
- shape->fill.gradient = nsvg__createGradient(
- p, attr->fillGradient, localBounds, &shape->fill.type);
- if (shape->fill.gradient == NULL) {
- shape->fill.type = NSVG_PAINT_NONE;
- }
- }
-
- // Set stroke
- if (attr->hasStroke == 0) {
- shape->stroke.type = NSVG_PAINT_NONE;
- }
- else if (attr->hasStroke == 1) {
- shape->stroke.type = NSVG_PAINT_COLOR;
- shape->stroke.color = attr->strokeColor;
- shape->stroke.color |= (unsigned int)(attr->strokeOpacity * 255) << 24;
- }
- else if (attr->hasStroke == 2) {
- float inv[6], localBounds[4];
- nsvg__xformInverse(inv, attr->xform);
- nsvg__getLocalBounds(localBounds, shape, inv);
- shape->stroke.gradient = nsvg__createGradient(
- p, attr->strokeGradient, localBounds, &shape->stroke.type);
- if (shape->stroke.gradient == NULL)
- shape->stroke.type = NSVG_PAINT_NONE;
- }
-
- // Set flags
- shape->flags = (attr->visible ? NSVG_FLAGS_VISIBLE : 0x00);
-
- // Add to tail
- if (p->image->shapes == NULL)
- p->image->shapes = shape;
- else
- p->shapesTail->next = shape;
- p->shapesTail = shape;
-
- return;
-
-error:
- if (shape)
- free(shape);
-}
-
-static void nsvg__addPath(NSVGparser *p, char closed)
-{
- NSVGattrib *attr = nsvg__getAttr(p);
- NSVGpath *path = NULL;
- float bounds[4];
- float *curve;
- int i;
-
- if (p->npts < 4)
- return;
-
- if (closed)
- nsvg__lineTo(p, p->pts[0], p->pts[1]);
-
- // Expect 1 + N*3 points (N = number of cubic bezier segments).
- if ((p->npts % 3) != 1)
- return;
-
- path = (NSVGpath *)malloc(sizeof(NSVGpath));
- if (path == NULL)
- goto error;
- memset(path, 0, sizeof(NSVGpath));
-
- path->pts = (float *)malloc(p->npts * 2 * sizeof(float));
- if (path->pts == NULL)
- goto error;
- path->closed = closed;
- path->npts = p->npts;
-
- // Transform path.
- for (i = 0; i < p->npts; ++i)
- nsvg__xformPoint(
- &path->pts[i * 2], &path->pts[i * 2 + 1], p->pts[i * 2], p->pts[i * 2 + 1], attr->xform);
-
- // Find bounds
- for (i = 0; i < path->npts - 1; i += 3) {
- curve = &path->pts[i * 2];
- nsvg__curveBounds(bounds, curve);
- if (i == 0) {
- path->bounds[0] = bounds[0];
- path->bounds[1] = bounds[1];
- path->bounds[2] = bounds[2];
- path->bounds[3] = bounds[3];
- }
- else {
- path->bounds[0] = nsvg__minf(path->bounds[0], bounds[0]);
- path->bounds[1] = nsvg__minf(path->bounds[1], bounds[1]);
- path->bounds[2] = nsvg__maxf(path->bounds[2], bounds[2]);
- path->bounds[3] = nsvg__maxf(path->bounds[3], bounds[3]);
- }
- }
-
- path->next = p->plist;
- p->plist = path;
-
- return;
-
-error:
- if (path != NULL) {
- if (path->pts != NULL)
- free(path->pts);
- free(path);
- }
-}
-
-// We roll our own string to float because the std library one uses locale and messes things up.
-static double nsvg__atof(const char *s)
-{
- char *cur = (char *)s;
- char *end = NULL;
- double res = 0.0, sign = 1.0;
- long long intPart = 0, fracPart = 0;
- char hasIntPart = 0, hasFracPart = 0;
-
- // Parse optional sign
- if (*cur == '+') {
- cur++;
- }
- else if (*cur == '-') {
- sign = -1;
- cur++;
- }
-
- // Parse integer part
- if (nsvg__isdigit(*cur)) {
- // Parse digit sequence
- intPart = strtoll(cur, &end, 10);
- if (cur != end) {
- res = (double)intPart;
- hasIntPart = 1;
- cur = end;
- }
- }
-
- // Parse fractional part.
- if (*cur == '.') {
- cur++; // Skip '.'
- if (nsvg__isdigit(*cur)) {
- // Parse digit sequence
- fracPart = strtoll(cur, &end, 10);
- if (cur != end) {
- res += (double)fracPart / pow(10.0, (double)(end - cur));
- hasFracPart = 1;
- cur = end;
- }
- }
- }
-
- // A valid number should have integer or fractional part.
- if (!hasIntPart && !hasFracPart)
- return 0.0;
-
- // Parse optional exponent
- if (*cur == 'e' || *cur == 'E') {
- long expPart = 0;
- cur++; // skip 'E'
- expPart = strtol(cur, &end, 10); // Parse digit sequence with sign
- if (cur != end) {
- res *= pow(10.0, (double)expPart);
- }
- }
-
- return res * sign;
-}
-
-static const char *nsvg__parseNumber(const char *s, char *it, const int size)
-{
- const int last = size - 1;
- int i = 0;
-
- // sign
- if (*s == '-' || *s == '+') {
- if (i < last)
- it[i++] = *s;
- s++;
- }
- // integer part
- while (*s && nsvg__isdigit(*s)) {
- if (i < last)
- it[i++] = *s;
- s++;
- }
- if (*s == '.') {
- // decimal point
- if (i < last)
- it[i++] = *s;
- s++;
- // fraction part
- while (*s && nsvg__isdigit(*s)) {
- if (i < last)
- it[i++] = *s;
- s++;
- }
- }
- // exponent
- if ((*s == 'e' || *s == 'E') && (s[1] != 'm' && s[1] != 'x')) {
- if (i < last)
- it[i++] = *s;
- s++;
- if (*s == '-' || *s == '+') {
- if (i < last)
- it[i++] = *s;
- s++;
- }
- while (*s && nsvg__isdigit(*s)) {
- if (i < last)
- it[i++] = *s;
- s++;
- }
- }
- it[i] = '\0';
-
- return s;
-}
-
-static const char *nsvg__getNextPathItem(const char *s, char *it, char cmd, int nargs)
-{
- it[0] = '\0';
- // Skip white spaces and commas
- while (*s && (nsvg__isspace(*s) || *s == ','))
- s++;
- if (!*s)
- return s;
-
- /* Blender: Special case for arc command's 4th and 5th arguments. */
- if (ELEM(cmd, 'a', 'A') && ELEM(nargs, 3, 4)) {
- it[0] = s[0];
- it[1] = '\0';
- s++;
- return s;
- }
-
- if (*s == '-' || *s == '+' || *s == '.' || nsvg__isdigit(*s)) {
- s = nsvg__parseNumber(s, it, 64);
- }
- else {
- // Parse command
- it[0] = *s++;
- it[1] = '\0';
- return s;
- }
-
- return s;
-}
-
-static unsigned int nsvg__parseColorHex(const char *str)
-{
- unsigned int c = 0, r = 0, g = 0, b = 0;
- int n = 0;
- str++; // skip #
- // Calculate number of characters.
- while (str[n] && !nsvg__isspace(str[n]))
- n++;
- if (n == 6) {
- sscanf(str, "%x", &c);
- }
- else if (n == 3) {
- sscanf(str, "%x", &c);
- c = (c & 0xf) | ((c & 0xf0) << 4) | ((c & 0xf00) << 8);
- c |= c << 4;
- }
- r = (c >> 16) & 0xff;
- g = (c >> 8) & 0xff;
- b = c & 0xff;
- return NSVG_RGB(r, g, b);
-}
-
-static unsigned int nsvg__parseColorRGB(const char *str)
-{
- int r = -1, g = -1, b = -1;
- char s1[32] = "", s2[32] = "";
- sscanf(str + 4, "%d%[%%, \t]%d%[%%, \t]%d", &r, s1, &g, s2, &b);
- if (strchr(s1, '%')) {
- return NSVG_RGB((r * 255) / 100, (g * 255) / 100, (b * 255) / 100);
- }
- else {
- return NSVG_RGB(r, g, b);
- }
-}
-
-typedef struct NSVGNamedColor {
- const char *name;
- unsigned int color;
-} NSVGNamedColor;
-
-NSVGNamedColor nsvg__colors[] = {
-
- {"red", NSVG_RGB(255, 0, 0)},
- {"green", NSVG_RGB(0, 128, 0)},
- {"blue", NSVG_RGB(0, 0, 255)},
- {"yellow", NSVG_RGB(255, 255, 0)},
- {"cyan", NSVG_RGB(0, 255, 255)},
- {"magenta", NSVG_RGB(255, 0, 255)},
- {"black", NSVG_RGB(0, 0, 0)},
- {"grey", NSVG_RGB(128, 128, 128)},
- {"gray", NSVG_RGB(128, 128, 128)},
- {"white", NSVG_RGB(255, 255, 255)},
-
-#ifdef NANOSVG_ALL_COLOR_KEYWORDS
- {"aliceblue", NSVG_RGB(240, 248, 255)},
- {"antiquewhite", NSVG_RGB(250, 235, 215)},
- {"aqua", NSVG_RGB(0, 255, 255)},
- {"aquamarine", NSVG_RGB(127, 255, 212)},
- {"azure", NSVG_RGB(240, 255, 255)},
- {"beige", NSVG_RGB(245, 245, 220)},
- {"bisque", NSVG_RGB(255, 228, 196)},
- {"blanchedalmond", NSVG_RGB(255, 235, 205)},
- {"blueviolet", NSVG_RGB(138, 43, 226)},
- {"brown", NSVG_RGB(165, 42, 42)},
- {"burlywood", NSVG_RGB(222, 184, 135)},
- {"cadetblue", NSVG_RGB(95, 158, 160)},
- {"chartreuse", NSVG_RGB(127, 255, 0)},
- {"chocolate", NSVG_RGB(210, 105, 30)},
- {"coral", NSVG_RGB(255, 127, 80)},
- {"cornflowerblue", NSVG_RGB(100, 149, 237)},
- {"cornsilk", NSVG_RGB(255, 248, 220)},
- {"crimson", NSVG_RGB(220, 20, 60)},
- {"darkblue", NSVG_RGB(0, 0, 139)},
- {"darkcyan", NSVG_RGB(0, 139, 139)},
- {"darkgoldenrod", NSVG_RGB(184, 134, 11)},
- {"darkgray", NSVG_RGB(169, 169, 169)},
- {"darkgreen", NSVG_RGB(0, 100, 0)},
- {"darkgrey", NSVG_RGB(169, 169, 169)},
- {"darkkhaki", NSVG_RGB(189, 183, 107)},
- {"darkmagenta", NSVG_RGB(139, 0, 139)},
- {"darkolivegreen", NSVG_RGB(85, 107, 47)},
- {"darkorange", NSVG_RGB(255, 140, 0)},
- {"darkorchid", NSVG_RGB(153, 50, 204)},
- {"darkred", NSVG_RGB(139, 0, 0)},
- {"darksalmon", NSVG_RGB(233, 150, 122)},
- {"darkseagreen", NSVG_RGB(143, 188, 143)},
- {"darkslateblue", NSVG_RGB(72, 61, 139)},
- {"darkslategray", NSVG_RGB(47, 79, 79)},
- {"darkslategrey", NSVG_RGB(47, 79, 79)},
- {"darkturquoise", NSVG_RGB(0, 206, 209)},
- {"darkviolet", NSVG_RGB(148, 0, 211)},
- {"deeppink", NSVG_RGB(255, 20, 147)},
- {"deepskyblue", NSVG_RGB(0, 191, 255)},
- {"dimgray", NSVG_RGB(105, 105, 105)},
- {"dimgrey", NSVG_RGB(105, 105, 105)},
- {"dodgerblue", NSVG_RGB(30, 144, 255)},
- {"firebrick", NSVG_RGB(178, 34, 34)},
- {"floralwhite", NSVG_RGB(255, 250, 240)},
- {"forestgreen", NSVG_RGB(34, 139, 34)},
- {"fuchsia", NSVG_RGB(255, 0, 255)},
- {"gainsboro", NSVG_RGB(220, 220, 220)},
- {"ghostwhite", NSVG_RGB(248, 248, 255)},
- {"gold", NSVG_RGB(255, 215, 0)},
- {"goldenrod", NSVG_RGB(218, 165, 32)},
- {"greenyellow", NSVG_RGB(173, 255, 47)},
- {"honeydew", NSVG_RGB(240, 255, 240)},
- {"hotpink", NSVG_RGB(255, 105, 180)},
- {"indianred", NSVG_RGB(205, 92, 92)},
- {"indigo", NSVG_RGB(75, 0, 130)},
- {"ivory", NSVG_RGB(255, 255, 240)},
- {"khaki", NSVG_RGB(240, 230, 140)},
- {"lavender", NSVG_RGB(230, 230, 250)},
- {"lavenderblush", NSVG_RGB(255, 240, 245)},
- {"lawngreen", NSVG_RGB(124, 252, 0)},
- {"lemonchiffon", NSVG_RGB(255, 250, 205)},
- {"lightblue", NSVG_RGB(173, 216, 230)},
- {"lightcoral", NSVG_RGB(240, 128, 128)},
- {"lightcyan", NSVG_RGB(224, 255, 255)},
- {"lightgoldenrodyellow", NSVG_RGB(250, 250, 210)},
- {"lightgray", NSVG_RGB(211, 211, 211)},
- {"lightgreen", NSVG_RGB(144, 238, 144)},
- {"lightgrey", NSVG_RGB(211, 211, 211)},
- {"lightpink", NSVG_RGB(255, 182, 193)},
- {"lightsalmon", NSVG_RGB(255, 160, 122)},
- {"lightseagreen", NSVG_RGB(32, 178, 170)},
- {"lightskyblue", NSVG_RGB(135, 206, 250)},
- {"lightslategray", NSVG_RGB(119, 136, 153)},
- {"lightslategrey", NSVG_RGB(119, 136, 153)},
- {"lightsteelblue", NSVG_RGB(176, 196, 222)},
- {"lightyellow", NSVG_RGB(255, 255, 224)},
- {"lime", NSVG_RGB(0, 255, 0)},
- {"limegreen", NSVG_RGB(50, 205, 50)},
- {"linen", NSVG_RGB(250, 240, 230)},
- {"maroon", NSVG_RGB(128, 0, 0)},
- {"mediumaquamarine", NSVG_RGB(102, 205, 170)},
- {"mediumblue", NSVG_RGB(0, 0, 205)},
- {"mediumorchid", NSVG_RGB(186, 85, 211)},
- {"mediumpurple", NSVG_RGB(147, 112, 219)},
- {"mediumseagreen", NSVG_RGB(60, 179, 113)},
- {"mediumslateblue", NSVG_RGB(123, 104, 238)},
- {"mediumspringgreen", NSVG_RGB(0, 250, 154)},
- {"mediumturquoise", NSVG_RGB(72, 209, 204)},
- {"mediumvioletred", NSVG_RGB(199, 21, 133)},
- {"midnightblue", NSVG_RGB(25, 25, 112)},
- {"mintcream", NSVG_RGB(245, 255, 250)},
- {"mistyrose", NSVG_RGB(255, 228, 225)},
- {"moccasin", NSVG_RGB(255, 228, 181)},
- {"navajowhite", NSVG_RGB(255, 222, 173)},
- {"navy", NSVG_RGB(0, 0, 128)},
- {"oldlace", NSVG_RGB(253, 245, 230)},
- {"olive", NSVG_RGB(128, 128, 0)},
- {"olivedrab", NSVG_RGB(107, 142, 35)},
- {"orange", NSVG_RGB(255, 165, 0)},
- {"orangered", NSVG_RGB(255, 69, 0)},
- {"orchid", NSVG_RGB(218, 112, 214)},
- {"palegoldenrod", NSVG_RGB(238, 232, 170)},
- {"palegreen", NSVG_RGB(152, 251, 152)},
- {"paleturquoise", NSVG_RGB(175, 238, 238)},
- {"palevioletred", NSVG_RGB(219, 112, 147)},
- {"papayawhip", NSVG_RGB(255, 239, 213)},
- {"peachpuff", NSVG_RGB(255, 218, 185)},
- {"peru", NSVG_RGB(205, 133, 63)},
- {"pink", NSVG_RGB(255, 192, 203)},
- {"plum", NSVG_RGB(221, 160, 221)},
- {"powderblue", NSVG_RGB(176, 224, 230)},
- {"purple", NSVG_RGB(128, 0, 128)},
- {"rosybrown", NSVG_RGB(188, 143, 143)},
- {"royalblue", NSVG_RGB(65, 105, 225)},
- {"saddlebrown", NSVG_RGB(139, 69, 19)},
- {"salmon", NSVG_RGB(250, 128, 114)},
- {"sandybrown", NSVG_RGB(244, 164, 96)},
- {"seagreen", NSVG_RGB(46, 139, 87)},
- {"seashell", NSVG_RGB(255, 245, 238)},
- {"sienna", NSVG_RGB(160, 82, 45)},
- {"silver", NSVG_RGB(192, 192, 192)},
- {"skyblue", NSVG_RGB(135, 206, 235)},
- {"slateblue", NSVG_RGB(106, 90, 205)},
- {"slategray", NSVG_RGB(112, 128, 144)},
- {"slategrey", NSVG_RGB(112, 128, 144)},
- {"snow", NSVG_RGB(255, 250, 250)},
- {"springgreen", NSVG_RGB(0, 255, 127)},
- {"steelblue", NSVG_RGB(70, 130, 180)},
- {"tan", NSVG_RGB(210, 180, 140)},
- {"teal", NSVG_RGB(0, 128, 128)},
- {"thistle", NSVG_RGB(216, 191, 216)},
- {"tomato", NSVG_RGB(255, 99, 71)},
- {"turquoise", NSVG_RGB(64, 224, 208)},
- {"violet", NSVG_RGB(238, 130, 238)},
- {"wheat", NSVG_RGB(245, 222, 179)},
- {"whitesmoke", NSVG_RGB(245, 245, 245)},
- {"yellowgreen", NSVG_RGB(154, 205, 50)},
-#endif
-};
-
-static unsigned int nsvg__parseColorName(const char *str)
-{
- int i, ncolors = sizeof(nsvg__colors) / sizeof(NSVGNamedColor);
-
- for (i = 0; i < ncolors; i++) {
- if (strcmp(nsvg__colors[i].name, str) == 0) {
- return nsvg__colors[i].color;
- }
- }
-
- return NSVG_RGB(128, 128, 128);
-}
-
-static unsigned int nsvg__parseColor(const char *str)
-{
- size_t len = 0;
- while (*str == ' ')
- ++str;
- len = strlen(str);
- if (len >= 1 && *str == '#')
- return nsvg__parseColorHex(str);
- else if (len >= 4 && str[0] == 'r' && str[1] == 'g' && str[2] == 'b' && str[3] == '(')
- return nsvg__parseColorRGB(str);
- return nsvg__parseColorName(str);
-}
-
-static float nsvg__parseOpacity(const char *str)
-{
- float val = nsvg__atof(str);
- if (val < 0.0f)
- val = 0.0f;
- if (val > 1.0f)
- val = 1.0f;
- return val;
-}
-
-static float nsvg__parseMiterLimit(const char *str)
-{
- float val = nsvg__atof(str);
- if (val < 0.0f)
- val = 0.0f;
- return val;
-}
-
-static int nsvg__parseUnits(const char *units)
-{
- if (units[0] == 'p' && units[1] == 'x')
- return NSVG_UNITS_PX;
- else if (units[0] == 'p' && units[1] == 't')
- return NSVG_UNITS_PT;
- else if (units[0] == 'p' && units[1] == 'c')
- return NSVG_UNITS_PC;
- else if (units[0] == 'm' && units[1] == 'm')
- return NSVG_UNITS_MM;
- else if (units[0] == 'c' && units[1] == 'm')
- return NSVG_UNITS_CM;
- else if (units[0] == 'i' && units[1] == 'n')
- return NSVG_UNITS_IN;
- else if (units[0] == '%')
- return NSVG_UNITS_PERCENT;
- else if (units[0] == 'e' && units[1] == 'm')
- return NSVG_UNITS_EM;
- else if (units[0] == 'e' && units[1] == 'x')
- return NSVG_UNITS_EX;
- return NSVG_UNITS_USER;
-}
-
-static int nsvg__isCoordinate(const char *s)
-{
- // optional sign
- if (*s == '-' || *s == '+')
- s++;
- // must have at least one digit, or start by a dot
- return (nsvg__isdigit(*s) || *s == '.');
-}
-
-static NSVGcoordinate nsvg__parseCoordinateRaw(const char *str)
-{
- NSVGcoordinate coord = {0, NSVG_UNITS_USER};
- char buf[64];
- coord.units = nsvg__parseUnits(nsvg__parseNumber(str, buf, 64));
- coord.value = nsvg__atof(buf);
- return coord;
-}
-
-static NSVGcoordinate nsvg__coord(float v, int units)
-{
- NSVGcoordinate coord = {v, units};
- return coord;
-}
-
-static float nsvg__parseCoordinate(NSVGparser *p, const char *str, float orig, float length)
-{
- NSVGcoordinate coord = nsvg__parseCoordinateRaw(str);
- return nsvg__convertToPixels(p, coord, orig, length);
-}
-
-static int nsvg__parseTransformArgs(const char *str, float *args, int maxNa, int *na)
-{
- const char *end;
- const char *ptr;
- char it[64];
-
- *na = 0;
- ptr = str;
- while (*ptr && *ptr != '(')
- ++ptr;
- if (*ptr == 0)
- return 1;
- end = ptr;
- while (*end && *end != ')')
- ++end;
- if (*end == 0)
- return 1;
-
- while (ptr < end) {
- if (*ptr == '-' || *ptr == '+' || *ptr == '.' || nsvg__isdigit(*ptr)) {
- if (*na >= maxNa)
- return 0;
- ptr = nsvg__parseNumber(ptr, it, 64);
- args[(*na)++] = (float)nsvg__atof(it);
- }
- else {
- ++ptr;
- }
- }
- return (int)(end - str);
-}
-
-static int nsvg__parseMatrix(float *xform, const char *str)
-{
- float t[6];
- int na = 0;
- int len = nsvg__parseTransformArgs(str, t, 6, &na);
- if (na != 6)
- return len;
- memcpy(xform, t, sizeof(float) * 6);
- return len;
-}
-
-static int nsvg__parseTranslate(float *xform, const char *str)
-{
- float args[2];
- float t[6];
- int na = 0;
- int len = nsvg__parseTransformArgs(str, args, 2, &na);
- if (na == 1)
- args[1] = 0.0;
-
- nsvg__xformSetTranslation(t, args[0], args[1]);
- memcpy(xform, t, sizeof(float) * 6);
- return len;
-}
-
-static int nsvg__parseScale(float *xform, const char *str)
-{
- float args[2];
- int na = 0;
- float t[6];
- int len = nsvg__parseTransformArgs(str, args, 2, &na);
- if (na == 1)
- args[1] = args[0];
- nsvg__xformSetScale(t, args[0], args[1]);
- memcpy(xform, t, sizeof(float) * 6);
- return len;
-}
-
-static int nsvg__parseSkewX(float *xform, const char *str)
-{
- float args[1];
- int na = 0;
- float t[6];
- int len = nsvg__parseTransformArgs(str, args, 1, &na);
- nsvg__xformSetSkewX(t, args[0] / 180.0f * NSVG_PI);
- memcpy(xform, t, sizeof(float) * 6);
- return len;
-}
-
-static int nsvg__parseSkewY(float *xform, const char *str)
-{
- float args[1];
- int na = 0;
- float t[6];
- int len = nsvg__parseTransformArgs(str, args, 1, &na);
- nsvg__xformSetSkewY(t, args[0] / 180.0f * NSVG_PI);
- memcpy(xform, t, sizeof(float) * 6);
- return len;
-}
-
-static int nsvg__parseRotate(float *xform, const char *str)
-{
- float args[3];
- int na = 0;
- float m[6];
- float t[6];
- int len = nsvg__parseTransformArgs(str, args, 3, &na);
- if (na == 1)
- args[1] = args[2] = 0.0f;
- nsvg__xformIdentity(m);
-
- if (na > 1) {
- nsvg__xformSetTranslation(t, -args[1], -args[2]);
- nsvg__xformMultiply(m, t);
- }
-
- nsvg__xformSetRotation(t, args[0] / 180.0f * NSVG_PI);
- nsvg__xformMultiply(m, t);
-
- if (na > 1) {
- nsvg__xformSetTranslation(t, args[1], args[2]);
- nsvg__xformMultiply(m, t);
- }
-
- memcpy(xform, m, sizeof(float) * 6);
-
- return len;
-}
-
-static void nsvg__parseTransform(float *xform, const char *str)
-{
- float t[6];
- int len;
- nsvg__xformIdentity(xform);
- while (*str) {
- if (strncmp(str, "matrix", 6) == 0)
- len = nsvg__parseMatrix(t, str);
- else if (strncmp(str, "translate", 9) == 0)
- len = nsvg__parseTranslate(t, str);
- else if (strncmp(str, "scale", 5) == 0)
- len = nsvg__parseScale(t, str);
- else if (strncmp(str, "rotate", 6) == 0)
- len = nsvg__parseRotate(t, str);
- else if (strncmp(str, "skewX", 5) == 0)
- len = nsvg__parseSkewX(t, str);
- else if (strncmp(str, "skewY", 5) == 0)
- len = nsvg__parseSkewY(t, str);
- else {
- ++str;
- continue;
- }
- if (len != 0) {
- str += len;
- }
- else {
- ++str;
- continue;
- }
-
- nsvg__xformPremultiply(xform, t);
- }
-}
-
-static void nsvg__parseUrl(char *id, const char *str)
-{
- int i = 0;
- str += 4; // "url(";
- if (*str == '#')
- str++;
- while (i < 63 && *str != ')') {
- id[i] = *str++;
- i++;
- }
- id[i] = '\0';
-}
-
-static char nsvg__parseLineCap(const char *str)
-{
- if (strcmp(str, "butt") == 0)
- return NSVG_CAP_BUTT;
- else if (strcmp(str, "round") == 0)
- return NSVG_CAP_ROUND;
- else if (strcmp(str, "square") == 0)
- return NSVG_CAP_SQUARE;
- // TODO: handle inherit.
- return NSVG_CAP_BUTT;
-}
-
-static char nsvg__parseLineJoin(const char *str)
-{
- if (strcmp(str, "miter") == 0)
- return NSVG_JOIN_MITER;
- else if (strcmp(str, "round") == 0)
- return NSVG_JOIN_ROUND;
- else if (strcmp(str, "bevel") == 0)
- return NSVG_JOIN_BEVEL;
- // TODO: handle inherit.
- return NSVG_JOIN_MITER;
-}
-
-static char nsvg__parseFillRule(const char *str)
-{
- if (strcmp(str, "nonzero") == 0)
- return NSVG_FILLRULE_NONZERO;
- else if (strcmp(str, "evenodd") == 0)
- return NSVG_FILLRULE_EVENODD;
- // TODO: handle inherit.
- return NSVG_FILLRULE_NONZERO;
-}
-
-static const char *nsvg__getNextDashItem(const char *s, char *it)
-{
- int n = 0;
- it[0] = '\0';
- // Skip white spaces and commas
- while (*s && (nsvg__isspace(*s) || *s == ','))
- s++;
- // Advance until whitespace, comma or end.
- while (*s && (!nsvg__isspace(*s) && *s != ',')) {
- if (n < 63)
- it[n++] = *s;
- s++;
- }
- it[n++] = '\0';
- return s;
-}
-
-static int nsvg__parseStrokeDashArray(NSVGparser *p, const char *str, float *strokeDashArray)
-{
- char item[64];
- int count = 0, i;
- float sum = 0.0f;
-
- // Handle "none"
- if (str[0] == 'n')
- return 0;
-
- // Parse dashes
- while (*str) {
- str = nsvg__getNextDashItem(str, item);
- if (!*item)
- break;
- if (count < NSVG_MAX_DASHES)
- strokeDashArray[count++] = fabsf(
- nsvg__parseCoordinate(p, item, 0.0f, nsvg__actualLength(p)));
- }
-
- for (i = 0; i < count; i++)
- sum += strokeDashArray[i];
- if (sum <= 1e-6f)
- count = 0;
-
- return count;
-}
-
-static void nsvg__parseStyle(NSVGparser *p, const char *str);
-
-static int nsvg__parseAttr(NSVGparser *p, const char *name, const char *value)
-{
- float xform[6];
- NSVGattrib *attr = nsvg__getAttr(p);
- if (!attr)
- return 0;
-
- if (strcmp(name, "style") == 0) {
- nsvg__parseStyle(p, value);
- }
- else if (strcmp(name, "display") == 0) {
- if (strcmp(value, "none") == 0)
- attr->visible = 0;
- // Don't reset ->visible on display:inline, one display:none hides the whole subtree
- }
- else if (strcmp(name, "fill") == 0) {
- if (strcmp(value, "none") == 0) {
- attr->hasFill = 0;
- }
- else if (strncmp(value, "url(", 4) == 0) {
- attr->hasFill = 2;
- nsvg__parseUrl(attr->fillGradient, value);
- }
- else {
- attr->hasFill = 1;
- attr->fillColor = nsvg__parseColor(value);
- }
- }
- else if (strcmp(name, "opacity") == 0) {
- attr->opacity = nsvg__parseOpacity(value);
- }
- else if (strcmp(name, "fill-opacity") == 0) {
- attr->fillOpacity = nsvg__parseOpacity(value);
- }
- else if (strcmp(name, "stroke") == 0) {
- if (strcmp(value, "none") == 0) {
- attr->hasStroke = 0;
- }
- else if (strncmp(value, "url(", 4) == 0) {
- attr->hasStroke = 2;
- nsvg__parseUrl(attr->strokeGradient, value);
- }
- else {
- attr->hasStroke = 1;
- attr->strokeColor = nsvg__parseColor(value);
- }
- }
- else if (strcmp(name, "stroke-width") == 0) {
- attr->strokeWidth = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p));
- }
- else if (strcmp(name, "stroke-dasharray") == 0) {
- attr->strokeDashCount = nsvg__parseStrokeDashArray(p, value, attr->strokeDashArray);
- }
- else if (strcmp(name, "stroke-dashoffset") == 0) {
- attr->strokeDashOffset = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p));
- }
- else if (strcmp(name, "stroke-opacity") == 0) {
- attr->strokeOpacity = nsvg__parseOpacity(value);
- }
- else if (strcmp(name, "stroke-linecap") == 0) {
- attr->strokeLineCap = nsvg__parseLineCap(value);
- }
- else if (strcmp(name, "stroke-linejoin") == 0) {
- attr->strokeLineJoin = nsvg__parseLineJoin(value);
- }
- else if (strcmp(name, "stroke-miterlimit") == 0) {
- attr->miterLimit = nsvg__parseMiterLimit(value);
- }
- else if (strcmp(name, "fill-rule") == 0) {
- attr->fillRule = nsvg__parseFillRule(value);
- }
- else if (strcmp(name, "font-size") == 0) {
- attr->fontSize = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p));
- }
- else if (strcmp(name, "transform") == 0) {
- nsvg__parseTransform(xform, value);
- nsvg__xformPremultiply(attr->xform, xform);
- }
- else if (strcmp(name, "stop-color") == 0) {
- attr->stopColor = nsvg__parseColor(value);
- }
- else if (strcmp(name, "stop-opacity") == 0) {
- attr->stopOpacity = nsvg__parseOpacity(value);
- }
- else if (strcmp(name, "offset") == 0) {
- attr->stopOffset = nsvg__parseCoordinate(p, value, 0.0f, 1.0f);
- }
- else if (strcmp(name, "id") == 0) {
- strncpy(attr->id, value, 63);
- attr->id[63] = '\0';
- }
- else {
- return 0;
- }
- return 1;
-}
-
-static int nsvg__parseNameValue(NSVGparser *p, const char *start, const char *end)
-{
- const char *str;
- const char *val;
- char name[512];
- char value[512];
- int n;
-
- str = start;
- while (str < end && *str != ':')
- ++str;
-
- val = str;
-
- // Right Trim
- while (str > start && (*str == ':' || nsvg__isspace(*str)))
- --str;
- ++str;
-
- n = (int)(str - start);
- if (n > 511)
- n = 511;
- if (n)
- memcpy(name, start, n);
- name[n] = 0;
-
- while (val < end && (*val == ':' || nsvg__isspace(*val)))
- ++val;
-
- n = (int)(end - val);
- if (n > 511)
- n = 511;
- if (n)
- memcpy(value, val, n);
- value[n] = 0;
-
- return nsvg__parseAttr(p, name, value);
-}
-
-static void nsvg__parseStyle(NSVGparser *p, const char *str)
-{
- const char *start;
- const char *end;
-
- while (*str) {
- // Left Trim
- while (*str && nsvg__isspace(*str))
- ++str;
- start = str;
- while (*str && *str != ';')
- ++str;
- end = str;
-
- // Right Trim
- while (end > start && (*end == ';' || nsvg__isspace(*end)))
- --end;
- ++end;
-
- nsvg__parseNameValue(p, start, end);
- if (*str)
- ++str;
- }
-}
-
-static void nsvg__parseAttribs(NSVGparser *p, const char **attr)
-{
- int i;
- for (i = 0; attr[i]; i += 2) {
- if (strcmp(attr[i], "style") == 0)
- nsvg__parseStyle(p, attr[i + 1]);
- else
- nsvg__parseAttr(p, attr[i], attr[i + 1]);
- }
-}
-
-static int nsvg__getArgsPerElement(char cmd)
-{
- switch (cmd) {
- case 'v':
- case 'V':
- case 'h':
- case 'H':
- return 1;
- case 'm':
- case 'M':
- case 'l':
- case 'L':
- case 't':
- case 'T':
- return 2;
- case 'q':
- case 'Q':
- case 's':
- case 'S':
- return 4;
- case 'c':
- case 'C':
- return 6;
- case 'a':
- case 'A':
- return 7;
- case 'z':
- case 'Z':
- return 0;
- }
- return -1;
-}
-
-static void nsvg__pathMoveTo(NSVGparser *p, float *cpx, float *cpy, float *args, int rel)
-{
- if (rel) {
- *cpx += args[0];
- *cpy += args[1];
- }
- else {
- *cpx = args[0];
- *cpy = args[1];
- }
- nsvg__moveTo(p, *cpx, *cpy);
-}
-
-static void nsvg__pathLineTo(NSVGparser *p, float *cpx, float *cpy, float *args, int rel)
-{
- if (rel) {
- *cpx += args[0];
- *cpy += args[1];
- }
- else {
- *cpx = args[0];
- *cpy = args[1];
- }
- nsvg__lineTo(p, *cpx, *cpy);
-}
-
-static void nsvg__pathHLineTo(NSVGparser *p, float *cpx, float *cpy, float *args, int rel)
-{
- if (rel)
- *cpx += args[0];
- else
- *cpx = args[0];
- nsvg__lineTo(p, *cpx, *cpy);
-}
-
-static void nsvg__pathVLineTo(NSVGparser *p, float *cpx, float *cpy, float *args, int rel)
-{
- if (rel)
- *cpy += args[0];
- else
- *cpy = args[0];
- nsvg__lineTo(p, *cpx, *cpy);
-}
-
-static void nsvg__pathCubicBezTo(
- NSVGparser *p, float *cpx, float *cpy, float *cpx2, float *cpy2, float *args, int rel)
-{
- float x2, y2, cx1, cy1, cx2, cy2;
-
- if (rel) {
- cx1 = *cpx + args[0];
- cy1 = *cpy + args[1];
- cx2 = *cpx + args[2];
- cy2 = *cpy + args[3];
- x2 = *cpx + args[4];
- y2 = *cpy + args[5];
- }
- else {
- cx1 = args[0];
- cy1 = args[1];
- cx2 = args[2];
- cy2 = args[3];
- x2 = args[4];
- y2 = args[5];
- }
-
- nsvg__cubicBezTo(p, cx1, cy1, cx2, cy2, x2, y2);
-
- *cpx2 = cx2;
- *cpy2 = cy2;
- *cpx = x2;
- *cpy = y2;
-}
-
-static void nsvg__pathCubicBezShortTo(
- NSVGparser *p, float *cpx, float *cpy, float *cpx2, float *cpy2, float *args, int rel)
-{
- float x1, y1, x2, y2, cx1, cy1, cx2, cy2;
-
- x1 = *cpx;
- y1 = *cpy;
- if (rel) {
- cx2 = *cpx + args[0];
- cy2 = *cpy + args[1];
- x2 = *cpx + args[2];
- y2 = *cpy + args[3];
- }
- else {
- cx2 = args[0];
- cy2 = args[1];
- x2 = args[2];
- y2 = args[3];
- }
-
- cx1 = 2 * x1 - *cpx2;
- cy1 = 2 * y1 - *cpy2;
-
- nsvg__cubicBezTo(p, cx1, cy1, cx2, cy2, x2, y2);
-
- *cpx2 = cx2;
- *cpy2 = cy2;
- *cpx = x2;
- *cpy = y2;
-}
-
-static void nsvg__pathQuadBezTo(
- NSVGparser *p, float *cpx, float *cpy, float *cpx2, float *cpy2, float *args, int rel)
-{
- float x1, y1, x2, y2, cx, cy;
- float cx1, cy1, cx2, cy2;
-
- x1 = *cpx;
- y1 = *cpy;
- if (rel) {
- cx = *cpx + args[0];
- cy = *cpy + args[1];
- x2 = *cpx + args[2];
- y2 = *cpy + args[3];
- }
- else {
- cx = args[0];
- cy = args[1];
- x2 = args[2];
- y2 = args[3];
- }
-
- // Convert to cubic bezier
- cx1 = x1 + 2.0f / 3.0f * (cx - x1);
- cy1 = y1 + 2.0f / 3.0f * (cy - y1);
- cx2 = x2 + 2.0f / 3.0f * (cx - x2);
- cy2 = y2 + 2.0f / 3.0f * (cy - y2);
-
- nsvg__cubicBezTo(p, cx1, cy1, cx2, cy2, x2, y2);
-
- *cpx2 = cx;
- *cpy2 = cy;
- *cpx = x2;
- *cpy = y2;
-}
-
-static void nsvg__pathQuadBezShortTo(
- NSVGparser *p, float *cpx, float *cpy, float *cpx2, float *cpy2, float *args, int rel)
-{
- float x1, y1, x2, y2, cx, cy;
- float cx1, cy1, cx2, cy2;
-
- x1 = *cpx;
- y1 = *cpy;
- if (rel) {
- x2 = *cpx + args[0];
- y2 = *cpy + args[1];
- }
- else {
- x2 = args[0];
- y2 = args[1];
- }
-
- cx = 2 * x1 - *cpx2;
- cy = 2 * y1 - *cpy2;
-
- // Convert to cubix bezier
- cx1 = x1 + 2.0f / 3.0f * (cx - x1);
- cy1 = y1 + 2.0f / 3.0f * (cy - y1);
- cx2 = x2 + 2.0f / 3.0f * (cx - x2);
- cy2 = y2 + 2.0f / 3.0f * (cy - y2);
-
- nsvg__cubicBezTo(p, cx1, cy1, cx2, cy2, x2, y2);
-
- *cpx2 = cx;
- *cpy2 = cy;
- *cpx = x2;
- *cpy = y2;
-}
-
-static float nsvg__sqr(float x)
-{
- return x * x;
-}
-static float nsvg__vmag(float x, float y)
-{
- return sqrtf(x * x + y * y);
-}
-
-static float nsvg__vecrat(float ux, float uy, float vx, float vy)
-{
- return (ux * vx + uy * vy) / (nsvg__vmag(ux, uy) * nsvg__vmag(vx, vy));
-}
-
-static float nsvg__vecang(float ux, float uy, float vx, float vy)
-{
- float r = nsvg__vecrat(ux, uy, vx, vy);
- if (r < -1.0f)
- r = -1.0f;
- if (r > 1.0f)
- r = 1.0f;
- return ((ux * vy < uy * vx) ? -1.0f : 1.0f) * acosf(r);
-}
-
-static void nsvg__pathArcTo(NSVGparser *p, float *cpx, float *cpy, float *args, int rel)
-{
- // Ported from canvg (https://code.google.com/p/canvg/)
- float rx, ry, rotx;
- float x1, y1, x2, y2, cx, cy, dx, dy, d;
- float x1p, y1p, cxp, cyp, s, sa, sb;
- float ux, uy, vx, vy, a1, da;
- float x, y, tanx, tany, a, px = 0, py = 0, ptanx = 0, ptany = 0, t[6];
- float sinrx, cosrx;
- int fa, fs;
- int i, ndivs;
- float hda, kappa;
-
- rx = fabsf(args[0]); // y radius
- ry = fabsf(args[1]); // x radius
- rotx = args[2] / 180.0f * NSVG_PI; // x rotation angle
- fa = fabsf(args[3]) > 1e-6 ? 1 : 0; // Large arc
- fs = fabsf(args[4]) > 1e-6 ? 1 : 0; // Sweep direction
- x1 = *cpx; // start point
- y1 = *cpy;
- if (rel) { // end point
- x2 = *cpx + args[5];
- y2 = *cpy + args[6];
- }
- else {
- x2 = args[5];
- y2 = args[6];
- }
-
- dx = x1 - x2;
- dy = y1 - y2;
- d = sqrtf(dx * dx + dy * dy);
- if (d < 1e-6f || rx < 1e-6f || ry < 1e-6f) {
- // The arc degenerates to a line
- nsvg__lineTo(p, x2, y2);
- *cpx = x2;
- *cpy = y2;
- return;
- }
-
- sinrx = sinf(rotx);
- cosrx = cosf(rotx);
-
- // Convert to center point parameterization.
- // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
- // 1) Compute x1', y1'
- x1p = cosrx * dx / 2.0f + sinrx * dy / 2.0f;
- y1p = -sinrx * dx / 2.0f + cosrx * dy / 2.0f;
- d = nsvg__sqr(x1p) / nsvg__sqr(rx) + nsvg__sqr(y1p) / nsvg__sqr(ry);
- if (d > 1) {
- d = sqrtf(d);
- rx *= d;
- ry *= d;
- }
- // 2) Compute cx', cy'
- s = 0.0f;
- sa = nsvg__sqr(rx) * nsvg__sqr(ry) - nsvg__sqr(rx) * nsvg__sqr(y1p) -
- nsvg__sqr(ry) * nsvg__sqr(x1p);
- sb = nsvg__sqr(rx) * nsvg__sqr(y1p) + nsvg__sqr(ry) * nsvg__sqr(x1p);
- if (sa < 0.0f)
- sa = 0.0f;
- if (sb > 0.0f)
- s = sqrtf(sa / sb);
- if (fa == fs)
- s = -s;
- cxp = s * rx * y1p / ry;
- cyp = s * -ry * x1p / rx;
-
- // 3) Compute cx,cy from cx',cy'
- cx = (x1 + x2) / 2.0f + cosrx * cxp - sinrx * cyp;
- cy = (y1 + y2) / 2.0f + sinrx * cxp + cosrx * cyp;
-
- // 4) Calculate theta1, and delta theta.
- ux = (x1p - cxp) / rx;
- uy = (y1p - cyp) / ry;
- vx = (-x1p - cxp) / rx;
- vy = (-y1p - cyp) / ry;
- a1 = nsvg__vecang(1.0f, 0.0f, ux, uy); // Initial angle
- da = nsvg__vecang(ux, uy, vx, vy); // Delta angle
-
- // if (vecrat(ux,uy,vx,vy) <= -1.0f) da = NSVG_PI;
- // if (vecrat(ux,uy,vx,vy) >= 1.0f) da = 0;
-
- if (fs == 0 && da > 0)
- da -= 2 * NSVG_PI;
- else if (fs == 1 && da < 0)
- da += 2 * NSVG_PI;
-
- // Approximate the arc using cubic spline segments.
- t[0] = cosrx;
- t[1] = sinrx;
- t[2] = -sinrx;
- t[3] = cosrx;
- t[4] = cx;
- t[5] = cy;
-
- // Split arc into max 90 degree segments.
- // The loop assumes an iteration per end point (including start and end), this +1.
- ndivs = (int)(fabsf(da) / (NSVG_PI * 0.5f) + 1.0f);
- hda = (da / (float)ndivs) / 2.0f;
- // Fix for ticket #179: division by 0: avoid cotangens around 0 (infinite)
- if ((hda < 1e-3f) && (hda > -1e-3f))
- hda *= 0.5f;
- else
- hda = (1.0f - cosf(hda)) / sinf(hda);
- kappa = fabsf(4.0f / 3.0f * hda);
- if (da < 0.0f)
- kappa = -kappa;
-
- for (i = 0; i <= ndivs; i++) {
- a = a1 + da * ((float)i / (float)ndivs);
- dx = cosf(a);
- dy = sinf(a);
- nsvg__xformPoint(&x, &y, dx * rx, dy * ry, t); // position
- nsvg__xformVec(&tanx, &tany, -dy * rx * kappa, dx * ry * kappa, t); // tangent
- if (i > 0)
- nsvg__cubicBezTo(p, px + ptanx, py + ptany, x - tanx, y - tany, x, y);
- px = x;
- py = y;
- ptanx = tanx;
- ptany = tany;
- }
-
- *cpx = x2;
- *cpy = y2;
-}
-
-static void nsvg__parsePath(NSVGparser *p, const char **attr)
-{
- const char *s = NULL;
- char cmd = '\0';
- float args[10];
- int nargs;
- int rargs = 0;
- char initPoint;
- float cpx, cpy, cpx2, cpy2;
- const char *tmp[4];
- char closedFlag;
- int i;
- char item[64];
-
- for (i = 0; attr[i]; i += 2) {
- if (strcmp(attr[i], "d") == 0) {
- s = attr[i + 1];
- }
- else {
- tmp[0] = attr[i];
- tmp[1] = attr[i + 1];
- tmp[2] = 0;
- tmp[3] = 0;
- nsvg__parseAttribs(p, tmp);
- }
- }
-
- if (s) {
- nsvg__resetPath(p);
- cpx = 0;
- cpy = 0;
- cpx2 = 0;
- cpy2 = 0;
- initPoint = 0;
- closedFlag = 0;
- nargs = 0;
-
- while (*s) {
- s = nsvg__getNextPathItem(s, item, cmd, nargs);
- if (!*item)
- break;
- if (cmd != '\0' && nsvg__isCoordinate(item)) {
- if (nargs < 10)
- args[nargs++] = (float)nsvg__atof(item);
- if (nargs >= rargs) {
- switch (cmd) {
- case 'm':
- case 'M':
- nsvg__pathMoveTo(p, &cpx, &cpy, args, cmd == 'm' ? 1 : 0);
- // Moveto can be followed by multiple coordinate pairs,
- // which should be treated as linetos.
- cmd = (cmd == 'm') ? 'l' : 'L';
- rargs = nsvg__getArgsPerElement(cmd);
- cpx2 = cpx;
- cpy2 = cpy;
- initPoint = 1;
- break;
- case 'l':
- case 'L':
- nsvg__pathLineTo(p, &cpx, &cpy, args, cmd == 'l' ? 1 : 0);
- cpx2 = cpx;
- cpy2 = cpy;
- break;
- case 'H':
- case 'h':
- nsvg__pathHLineTo(p, &cpx, &cpy, args, cmd == 'h' ? 1 : 0);
- cpx2 = cpx;
- cpy2 = cpy;
- break;
- case 'V':
- case 'v':
- nsvg__pathVLineTo(p, &cpx, &cpy, args, cmd == 'v' ? 1 : 0);
- cpx2 = cpx;
- cpy2 = cpy;
- break;
- case 'C':
- case 'c':
- nsvg__pathCubicBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'c' ? 1 : 0);
- break;
- case 'S':
- case 's':
- nsvg__pathCubicBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 's' ? 1 : 0);
- break;
- case 'Q':
- case 'q':
- nsvg__pathQuadBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'q' ? 1 : 0);
- break;
- case 'T':
- case 't':
- nsvg__pathQuadBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 't' ? 1 : 0);
- break;
- case 'A':
- case 'a':
- nsvg__pathArcTo(p, &cpx, &cpy, args, cmd == 'a' ? 1 : 0);
- cpx2 = cpx;
- cpy2 = cpy;
- break;
- default:
- if (nargs >= 2) {
- cpx = args[nargs - 2];
- cpy = args[nargs - 1];
- cpx2 = cpx;
- cpy2 = cpy;
- }
- break;
- }
- nargs = 0;
- }
- }
- else {
- cmd = item[0];
- if (cmd == 'M' || cmd == 'm') {
- // Commit path.
- if (p->npts > 0)
- nsvg__addPath(p, closedFlag);
- // Start new subpath.
- nsvg__resetPath(p);
- closedFlag = 0;
- nargs = 0;
- }
- else if (initPoint == 0) {
- // Do not allow other commands until initial point has been set (moveTo called once).
- cmd = '\0';
- }
- if (cmd == 'Z' || cmd == 'z') {
- closedFlag = 1;
- // Commit path.
- if (p->npts > 0) {
- // Move current point to first point
- cpx = p->pts[0];
- cpy = p->pts[1];
- cpx2 = cpx;
- cpy2 = cpy;
- nsvg__addPath(p, closedFlag);
- }
- // Start new subpath.
- nsvg__resetPath(p);
- nsvg__moveTo(p, cpx, cpy);
- closedFlag = 0;
- nargs = 0;
- }
- rargs = nsvg__getArgsPerElement(cmd);
- if (rargs == -1) {
- // Command not recognized
- cmd = '\0';
- rargs = 0;
- }
- }
- }
- // Commit path.
- if (p->npts)
- nsvg__addPath(p, closedFlag);
- }
-
- nsvg__addShape(p);
-}
-
-static void nsvg__parseRect(NSVGparser *p, const char **attr)
-{
- float x = 0.0f;
- float y = 0.0f;
- float w = 0.0f;
- float h = 0.0f;
- float rx = -1.0f; // marks not set
- float ry = -1.0f;
- int i;
-
- for (i = 0; attr[i]; i += 2) {
- if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
- if (strcmp(attr[i], "x") == 0)
- x = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
- if (strcmp(attr[i], "y") == 0)
- y = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
- if (strcmp(attr[i], "width") == 0)
- w = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, nsvg__actualWidth(p));
- if (strcmp(attr[i], "height") == 0)
- h = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, nsvg__actualHeight(p));
- if (strcmp(attr[i], "rx") == 0)
- rx = fabsf(nsvg__parseCoordinate(p, attr[i + 1], 0.0f, nsvg__actualWidth(p)));
- if (strcmp(attr[i], "ry") == 0)
- ry = fabsf(nsvg__parseCoordinate(p, attr[i + 1], 0.0f, nsvg__actualHeight(p)));
- }
- }
-
- if (rx < 0.0f && ry > 0.0f)
- rx = ry;
- if (ry < 0.0f && rx > 0.0f)
- ry = rx;
- if (rx < 0.0f)
- rx = 0.0f;
- if (ry < 0.0f)
- ry = 0.0f;
- if (rx > w / 2.0f)
- rx = w / 2.0f;
- if (ry > h / 2.0f)
- ry = h / 2.0f;
-
- if (w != 0.0f && h != 0.0f) {
- nsvg__resetPath(p);
-
- if (rx < 0.00001f || ry < 0.0001f) {
- nsvg__moveTo(p, x, y);
- nsvg__lineTo(p, x + w, y);
- nsvg__lineTo(p, x + w, y + h);
- nsvg__lineTo(p, x, y + h);
- }
- else {
- // Rounded rectangle
- nsvg__moveTo(p, x + rx, y);
- nsvg__lineTo(p, x + w - rx, y);
- nsvg__cubicBezTo(p,
- x + w - rx * (1 - NSVG_KAPPA90),
- y,
- x + w,
- y + ry * (1 - NSVG_KAPPA90),
- x + w,
- y + ry);
- nsvg__lineTo(p, x + w, y + h - ry);
- nsvg__cubicBezTo(p,
- x + w,
- y + h - ry * (1 - NSVG_KAPPA90),
- x + w - rx * (1 - NSVG_KAPPA90),
- y + h,
- x + w - rx,
- y + h);
- nsvg__lineTo(p, x + rx, y + h);
- nsvg__cubicBezTo(p,
- x + rx * (1 - NSVG_KAPPA90),
- y + h,
- x,
- y + h - ry * (1 - NSVG_KAPPA90),
- x,
- y + h - ry);
- nsvg__lineTo(p, x, y + ry);
- nsvg__cubicBezTo(
- p, x, y + ry * (1 - NSVG_KAPPA90), x + rx * (1 - NSVG_KAPPA90), y, x + rx, y);
- }
-
- nsvg__addPath(p, 1);
-
- nsvg__addShape(p);
- }
-}
-
-static void nsvg__parseCircle(NSVGparser *p, const char **attr)
-{
- float cx = 0.0f;
- float cy = 0.0f;
- float r = 0.0f;
- int i;
-
- for (i = 0; attr[i]; i += 2) {
- if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
- if (strcmp(attr[i], "cx") == 0)
- cx = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
- if (strcmp(attr[i], "cy") == 0)
- cy = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
- if (strcmp(attr[i], "r") == 0)
- r = fabsf(nsvg__parseCoordinate(p, attr[i + 1], 0.0f, nsvg__actualLength(p)));
- }
- }
-
- if (r > 0.0f) {
- nsvg__resetPath(p);
-
- nsvg__moveTo(p, cx + r, cy);
- nsvg__cubicBezTo(p, cx + r, cy + r * NSVG_KAPPA90, cx + r * NSVG_KAPPA90, cy + r, cx, cy + r);
- nsvg__cubicBezTo(p, cx - r * NSVG_KAPPA90, cy + r, cx - r, cy + r * NSVG_KAPPA90, cx - r, cy);
- nsvg__cubicBezTo(p, cx - r, cy - r * NSVG_KAPPA90, cx - r * NSVG_KAPPA90, cy - r, cx, cy - r);
- nsvg__cubicBezTo(p, cx + r * NSVG_KAPPA90, cy - r, cx + r, cy - r * NSVG_KAPPA90, cx + r, cy);
-
- nsvg__addPath(p, 1);
-
- nsvg__addShape(p);
- }
-}
-
-static void nsvg__parseEllipse(NSVGparser *p, const char **attr)
-{
- float cx = 0.0f;
- float cy = 0.0f;
- float rx = 0.0f;
- float ry = 0.0f;
- int i;
-
- for (i = 0; attr[i]; i += 2) {
- if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
- if (strcmp(attr[i], "cx") == 0)
- cx = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
- if (strcmp(attr[i], "cy") == 0)
- cy = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
- if (strcmp(attr[i], "rx") == 0)
- rx = fabsf(nsvg__parseCoordinate(p, attr[i + 1], 0.0f, nsvg__actualWidth(p)));
- if (strcmp(attr[i], "ry") == 0)
- ry = fabsf(nsvg__parseCoordinate(p, attr[i + 1], 0.0f, nsvg__actualHeight(p)));
- }
- }
-
- if (rx > 0.0f && ry > 0.0f) {
-
- nsvg__resetPath(p);
-
- nsvg__moveTo(p, cx + rx, cy);
- nsvg__cubicBezTo(
- p, cx + rx, cy + ry * NSVG_KAPPA90, cx + rx * NSVG_KAPPA90, cy + ry, cx, cy + ry);
- nsvg__cubicBezTo(
- p, cx - rx * NSVG_KAPPA90, cy + ry, cx - rx, cy + ry * NSVG_KAPPA90, cx - rx, cy);
- nsvg__cubicBezTo(
- p, cx - rx, cy - ry * NSVG_KAPPA90, cx - rx * NSVG_KAPPA90, cy - ry, cx, cy - ry);
- nsvg__cubicBezTo(
- p, cx + rx * NSVG_KAPPA90, cy - ry, cx + rx, cy - ry * NSVG_KAPPA90, cx + rx, cy);
-
- nsvg__addPath(p, 1);
-
- nsvg__addShape(p);
- }
-}
-
-static void nsvg__parseLine(NSVGparser *p, const char **attr)
-{
- float x1 = 0.0;
- float y1 = 0.0;
- float x2 = 0.0;
- float y2 = 0.0;
- int i;
-
- for (i = 0; attr[i]; i += 2) {
- if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
- if (strcmp(attr[i], "x1") == 0)
- x1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
- if (strcmp(attr[i], "y1") == 0)
- y1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
- if (strcmp(attr[i], "x2") == 0)
- x2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p));
- if (strcmp(attr[i], "y2") == 0)
- y2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p));
- }
- }
-
- nsvg__resetPath(p);
-
- nsvg__moveTo(p, x1, y1);
- nsvg__lineTo(p, x2, y2);
-
- nsvg__addPath(p, 0);
-
- nsvg__addShape(p);
-}
-
-static void nsvg__parsePoly(NSVGparser *p, const char **attr, int closeFlag)
-{
- int i;
- const char *s;
- float args[2];
- int nargs, npts = 0;
- char item[64];
-
- nsvg__resetPath(p);
-
- for (i = 0; attr[i]; i += 2) {
- if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
- if (strcmp(attr[i], "points") == 0) {
- s = attr[i + 1];
- nargs = 0;
- while (*s) {
- s = nsvg__getNextPathItem(s, item, '\0', nargs);
- args[nargs++] = (float)nsvg__atof(item);
- if (nargs >= 2) {
- if (npts == 0)
- nsvg__moveTo(p, args[0], args[1]);
- else
- nsvg__lineTo(p, args[0], args[1]);
- nargs = 0;
- npts++;
- }
- }
- }
- }
- }
-
- nsvg__addPath(p, (char)closeFlag);
-
- nsvg__addShape(p);
-}
-
-static void nsvg__parseSVG(NSVGparser *p, const char **attr)
-{
- int i;
- for (i = 0; attr[i]; i += 2) {
- if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
- if (strcmp(attr[i], "width") == 0) {
- p->image->width = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f);
- }
- else if (strcmp(attr[i], "height") == 0) {
- p->image->height = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f);
- }
- else if (strcmp(attr[i], "viewBox") == 0) {
- const char *s = attr[i + 1];
- char buf[64];
- s = nsvg__parseNumber(s, buf, 64);
- p->viewMinx = nsvg__atof(buf);
- while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ','))
- s++;
- if (!*s)
- return;
- s = nsvg__parseNumber(s, buf, 64);
- p->viewMiny = nsvg__atof(buf);
- while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ','))
- s++;
- if (!*s)
- return;
- s = nsvg__parseNumber(s, buf, 64);
- p->viewWidth = nsvg__atof(buf);
- while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ','))
- s++;
- if (!*s)
- return;
- s = nsvg__parseNumber(s, buf, 64);
- p->viewHeight = nsvg__atof(buf);
- }
- else if (strcmp(attr[i], "preserveAspectRatio") == 0) {
- if (strstr(attr[i + 1], "none") != 0) {
- // No uniform scaling
- p->alignType = NSVG_ALIGN_NONE;
- }
- else {
- // Parse X align
- if (strstr(attr[i + 1], "xMin") != 0)
- p->alignX = NSVG_ALIGN_MIN;
- else if (strstr(attr[i + 1], "xMid") != 0)
- p->alignX = NSVG_ALIGN_MID;
- else if (strstr(attr[i + 1], "xMax") != 0)
- p->alignX = NSVG_ALIGN_MAX;
- // Parse X align
- if (strstr(attr[i + 1], "yMin") != 0)
- p->alignY = NSVG_ALIGN_MIN;
- else if (strstr(attr[i + 1], "yMid") != 0)
- p->alignY = NSVG_ALIGN_MID;
- else if (strstr(attr[i + 1], "yMax") != 0)
- p->alignY = NSVG_ALIGN_MAX;
- // Parse meet/slice
- p->alignType = NSVG_ALIGN_MEET;
- if (strstr(attr[i + 1], "slice") != 0)
- p->alignType = NSVG_ALIGN_SLICE;
- }
- }
- }
- }
-}
-
-static void nsvg__parseGradient(NSVGparser *p, const char **attr, char type)
-{
- int i;
- NSVGgradientData *grad = (NSVGgradientData *)malloc(sizeof(NSVGgradientData));
- if (grad == NULL)
- return;
- memset(grad, 0, sizeof(NSVGgradientData));
- grad->units = NSVG_OBJECT_SPACE;
- grad->type = type;
- if (grad->type == NSVG_PAINT_LINEAR_GRADIENT) {
- grad->linear.x1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT);
- grad->linear.y1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT);
- grad->linear.x2 = nsvg__coord(100.0f, NSVG_UNITS_PERCENT);
- grad->linear.y2 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT);
- }
- else if (grad->type == NSVG_PAINT_RADIAL_GRADIENT) {
- grad->radial.cx = nsvg__coord(50.0f, NSVG_UNITS_PERCENT);
- grad->radial.cy = nsvg__coord(50.0f, NSVG_UNITS_PERCENT);
- grad->radial.r = nsvg__coord(50.0f, NSVG_UNITS_PERCENT);
- }
-
- nsvg__xformIdentity(grad->xform);
-
- for (i = 0; attr[i]; i += 2) {
- if (strcmp(attr[i], "id") == 0) {
- strncpy(grad->id, attr[i + 1], 63);
- grad->id[63] = '\0';
- }
- else if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) {
- if (strcmp(attr[i], "gradientUnits") == 0) {
- if (strcmp(attr[i + 1], "objectBoundingBox") == 0)
- grad->units = NSVG_OBJECT_SPACE;
- else
- grad->units = NSVG_USER_SPACE;
- }
- else if (strcmp(attr[i], "gradientTransform") == 0) {
- nsvg__parseTransform(grad->xform, attr[i + 1]);
- }
- else if (strcmp(attr[i], "cx") == 0) {
- grad->radial.cx = nsvg__parseCoordinateRaw(attr[i + 1]);
- }
- else if (strcmp(attr[i], "cy") == 0) {
- grad->radial.cy = nsvg__parseCoordinateRaw(attr[i + 1]);
- }
- else if (strcmp(attr[i], "r") == 0) {
- grad->radial.r = nsvg__parseCoordinateRaw(attr[i + 1]);
- }
- else if (strcmp(attr[i], "fx") == 0) {
- grad->radial.fx = nsvg__parseCoordinateRaw(attr[i + 1]);
- }
- else if (strcmp(attr[i], "fy") == 0) {
- grad->radial.fy = nsvg__parseCoordinateRaw(attr[i + 1]);
- }
- else if (strcmp(attr[i], "x1") == 0) {
- grad->linear.x1 = nsvg__parseCoordinateRaw(attr[i + 1]);
- }
- else if (strcmp(attr[i], "y1") == 0) {
- grad->linear.y1 = nsvg__parseCoordinateRaw(attr[i + 1]);
- }
- else if (strcmp(attr[i], "x2") == 0) {
- grad->linear.x2 = nsvg__parseCoordinateRaw(attr[i + 1]);
- }
- else if (strcmp(attr[i], "y2") == 0) {
- grad->linear.y2 = nsvg__parseCoordinateRaw(attr[i + 1]);
- }
- else if (strcmp(attr[i], "spreadMethod") == 0) {
- if (strcmp(attr[i + 1], "pad") == 0)
- grad->spread = NSVG_SPREAD_PAD;
- else if (strcmp(attr[i + 1], "reflect") == 0)
- grad->spread = NSVG_SPREAD_REFLECT;
- else if (strcmp(attr[i + 1], "repeat") == 0)
- grad->spread = NSVG_SPREAD_REPEAT;
- }
- else if (strcmp(attr[i], "xlink:href") == 0) {
- const char *href = attr[i + 1];
- strncpy(grad->ref, href + 1, 62);
- grad->ref[62] = '\0';
- }
- }
- }
-
- grad->next = p->gradients;
- p->gradients = grad;
-}
-
-static void nsvg__parseGradientStop(NSVGparser *p, const char **attr)
-{
- NSVGattrib *curAttr = nsvg__getAttr(p);
- NSVGgradientData *grad;
- NSVGgradientStop *stop;
- int i, idx;
-
- curAttr->stopOffset = 0;
- curAttr->stopColor = 0;
- curAttr->stopOpacity = 1.0f;
-
- for (i = 0; attr[i]; i += 2) {
- nsvg__parseAttr(p, attr[i], attr[i + 1]);
- }
-
- // Add stop to the last gradient.
- grad = p->gradients;
- if (grad == NULL)
- return;
-
- grad->nstops++;
- grad->stops = (NSVGgradientStop *)realloc(grad->stops, sizeof(NSVGgradientStop) * grad->nstops);
- if (grad->stops == NULL)
- return;
-
- // Insert
- idx = grad->nstops - 1;
- for (i = 0; i < grad->nstops - 1; i++) {
- if (curAttr->stopOffset < grad->stops[i].offset) {
- idx = i;
- break;
- }
- }
- if (idx != grad->nstops - 1) {
- for (i = grad->nstops - 1; i > idx; i--)
- grad->stops[i] = grad->stops[i - 1];
- }
-
- stop = &grad->stops[idx];
- stop->color = curAttr->stopColor;
- stop->color |= (unsigned int)(curAttr->stopOpacity * 255) << 24;
- stop->offset = curAttr->stopOffset;
-}
-
-static void nsvg__startElement(void *ud, const char *el, const char **attr)
-{
- NSVGparser *p = (NSVGparser *)ud;
-
- if (p->defsFlag) {
- // Skip everything but gradients in defs
- if (strcmp(el, "linearGradient") == 0) {
- nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT);
- }
- else if (strcmp(el, "radialGradient") == 0) {
- nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT);
- }
- else if (strcmp(el, "stop") == 0) {
- nsvg__parseGradientStop(p, attr);
- }
- return;
- }
-
- if (strcmp(el, "g") == 0) {
- nsvg__pushAttr(p);
- nsvg__parseAttribs(p, attr);
-
- /* Save the breadcrumb of groups. */
- if (p->breadcrumb_len < NSVG_MAX_BREADCRUMB) {
- NSVGattrib *attr_id = nsvg__getAttr(p);
- memcpy(
- p->breadcrumb[p->breadcrumb_len], attr_id->id, sizeof(p->breadcrumb[p->breadcrumb_len]));
- p->breadcrumb_len++;
- }
- }
- else if (strcmp(el, "path") == 0) {
- if (p->pathFlag) // Do not allow nested paths.
- return;
- nsvg__pushAttr(p);
- nsvg__parsePath(p, attr);
- nsvg__popAttr(p);
- }
- else if (strcmp(el, "rect") == 0) {
- nsvg__pushAttr(p);
- nsvg__parseRect(p, attr);
- nsvg__popAttr(p);
- }
- else if (strcmp(el, "circle") == 0) {
- nsvg__pushAttr(p);
- nsvg__parseCircle(p, attr);
- nsvg__popAttr(p);
- }
- else if (strcmp(el, "ellipse") == 0) {
- nsvg__pushAttr(p);
- nsvg__parseEllipse(p, attr);
- nsvg__popAttr(p);
- }
- else if (strcmp(el, "line") == 0) {
- nsvg__pushAttr(p);
- nsvg__parseLine(p, attr);
- nsvg__popAttr(p);
- }
- else if (strcmp(el, "polyline") == 0) {
- nsvg__pushAttr(p);
- nsvg__parsePoly(p, attr, 0);
- nsvg__popAttr(p);
- }
- else if (strcmp(el, "polygon") == 0) {
- nsvg__pushAttr(p);
- nsvg__parsePoly(p, attr, 1);
- nsvg__popAttr(p);
- }
- else if (strcmp(el, "linearGradient") == 0) {
- nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT);
- }
- else if (strcmp(el, "radialGradient") == 0) {
- nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT);
- }
- else if (strcmp(el, "stop") == 0) {
- nsvg__parseGradientStop(p, attr);
- }
- else if (strcmp(el, "defs") == 0) {
- p->defsFlag = 1;
- }
- else if (strcmp(el, "svg") == 0) {
- nsvg__parseSVG(p, attr);
- }
-}
-
-static void nsvg__endElement(void *ud, const char *el)
-{
- NSVGparser *p = (NSVGparser *)ud;
-
- if (strcmp(el, "g") == 0) {
- /* Remove the breadcrumb level. */
- if (p->breadcrumb_len > 0) {
- p->breadcrumb[p->breadcrumb_len - 1][0] = '\0';
- p->breadcrumb_len--;
- }
-
- nsvg__popAttr(p);
- }
- else if (strcmp(el, "path") == 0) {
- p->pathFlag = 0;
- }
- else if (strcmp(el, "defs") == 0) {
- p->defsFlag = 0;
- }
-}
-
-static void nsvg__content(void *ud, const char *s)
-{
- NSVG_NOTUSED(ud);
- NSVG_NOTUSED(s);
- // empty
-}
-
-static void nsvg__imageBounds(NSVGparser *p, float *bounds)
-{
- NSVGshape *shape;
- shape = p->image->shapes;
- if (shape == NULL) {
- bounds[0] = bounds[1] = bounds[2] = bounds[3] = 0.0;
- return;
- }
- bounds[0] = shape->bounds[0];
- bounds[1] = shape->bounds[1];
- bounds[2] = shape->bounds[2];
- bounds[3] = shape->bounds[3];
- for (shape = shape->next; shape != NULL; shape = shape->next) {
- bounds[0] = nsvg__minf(bounds[0], shape->bounds[0]);
- bounds[1] = nsvg__minf(bounds[1], shape->bounds[1]);
- bounds[2] = nsvg__maxf(bounds[2], shape->bounds[2]);
- bounds[3] = nsvg__maxf(bounds[3], shape->bounds[3]);
- }
-}
-
-static float nsvg__viewAlign(float content, float container, int type)
-{
- if (type == NSVG_ALIGN_MIN)
- return 0;
- else if (type == NSVG_ALIGN_MAX)
- return container - content;
- // mid
- return (container - content) * 0.5f;
-}
-
-static void nsvg__scaleGradient(NSVGgradient *grad, float tx, float ty, float sx, float sy)
-{
- float t[6];
- nsvg__xformSetTranslation(t, tx, ty);
- nsvg__xformMultiply(grad->xform, t);
-
- nsvg__xformSetScale(t, sx, sy);
- nsvg__xformMultiply(grad->xform, t);
-}
-
-static void nsvg__scaleToViewbox(NSVGparser *p, const char *units)
-{
- NSVGshape *shape;
- NSVGpath *path;
- float tx, ty, sx, sy, us, bounds[4], t[6], avgs;
- int i;
- float *pt;
-
- // Guess image size if not set completely.
- nsvg__imageBounds(p, bounds);
-
- if (p->viewWidth == 0) {
- if (p->image->width > 0) {
- p->viewWidth = p->image->width;
- }
- else {
- p->viewMinx = bounds[0];
- p->viewWidth = bounds[2] - bounds[0];
- }
- }
- if (p->viewHeight == 0) {
- if (p->image->height > 0) {
- p->viewHeight = p->image->height;
- }
- else {
- p->viewMiny = bounds[1];
- p->viewHeight = bounds[3] - bounds[1];
- }
- }
- if (p->image->width == 0)
- p->image->width = p->viewWidth;
- if (p->image->height == 0)
- p->image->height = p->viewHeight;
-
- tx = -p->viewMinx;
- ty = -p->viewMiny;
- sx = p->viewWidth > 0 ? p->image->width / p->viewWidth : 0;
- sy = p->viewHeight > 0 ? p->image->height / p->viewHeight : 0;
- // Unit scaling
- us = 1.0f / nsvg__convertToPixels(p, nsvg__coord(1.0f, nsvg__parseUnits(units)), 0.0f, 1.0f);
-
- // Fix aspect ratio
- if (p->alignType == NSVG_ALIGN_MEET) {
- // fit whole image into viewbox
- sx = sy = nsvg__minf(sx, sy);
- tx += nsvg__viewAlign(p->viewWidth * sx, p->image->width, p->alignX) / sx;
- ty += nsvg__viewAlign(p->viewHeight * sy, p->image->height, p->alignY) / sy;
- }
- else if (p->alignType == NSVG_ALIGN_SLICE) {
- // fill whole viewbox with image
- sx = sy = nsvg__maxf(sx, sy);
- tx += nsvg__viewAlign(p->viewWidth * sx, p->image->width, p->alignX) / sx;
- ty += nsvg__viewAlign(p->viewHeight * sy, p->image->height, p->alignY) / sy;
- }
-
- // Transform
- sx *= us;
- sy *= us;
- avgs = (sx + sy) / 2.0f;
- for (shape = p->image->shapes; shape != NULL; shape = shape->next) {
- shape->bounds[0] = (shape->bounds[0] + tx) * sx;
- shape->bounds[1] = (shape->bounds[1] + ty) * sy;
- shape->bounds[2] = (shape->bounds[2] + tx) * sx;
- shape->bounds[3] = (shape->bounds[3] + ty) * sy;
- for (path = shape->paths; path != NULL; path = path->next) {
- path->bounds[0] = (path->bounds[0] + tx) * sx;
- path->bounds[1] = (path->bounds[1] + ty) * sy;
- path->bounds[2] = (path->bounds[2] + tx) * sx;
- path->bounds[3] = (path->bounds[3] + ty) * sy;
- for (i = 0; i < path->npts; i++) {
- pt = &path->pts[i * 2];
- pt[0] = (pt[0] + tx) * sx;
- pt[1] = (pt[1] + ty) * sy;
- }
- }
-
- if (shape->fill.type == NSVG_PAINT_LINEAR_GRADIENT ||
- shape->fill.type == NSVG_PAINT_RADIAL_GRADIENT) {
- nsvg__scaleGradient(shape->fill.gradient, tx, ty, sx, sy);
- memcpy(t, shape->fill.gradient->xform, sizeof(float) * 6);
- nsvg__xformInverse(shape->fill.gradient->xform, t);
- }
- if (shape->stroke.type == NSVG_PAINT_LINEAR_GRADIENT ||
- shape->stroke.type == NSVG_PAINT_RADIAL_GRADIENT) {
- nsvg__scaleGradient(shape->stroke.gradient, tx, ty, sx, sy);
- memcpy(t, shape->stroke.gradient->xform, sizeof(float) * 6);
- nsvg__xformInverse(shape->stroke.gradient->xform, t);
- }
-
- shape->strokeWidth *= avgs;
- shape->strokeDashOffset *= avgs;
- for (i = 0; i < shape->strokeDashCount; i++)
- shape->strokeDashArray[i] *= avgs;
- }
-}
-
-NSVGimage *nsvgParse(char *input, const char *units, float dpi)
-{
- NSVGparser *p;
- NSVGimage *ret = 0;
-
- p = nsvg__createParser();
- if (p == NULL) {
- return NULL;
- }
- p->dpi = dpi;
-
- nsvg__parseXML(input, nsvg__startElement, nsvg__endElement, nsvg__content, p);
-
- // Scale to viewBox
- nsvg__scaleToViewbox(p, units);
-
- ret = p->image;
- p->image = NULL;
-
- nsvg__deleteParser(p);
-
- return ret;
-}
-
-NSVGimage *nsvgParseFromFile(const char *filename, const char *units, float dpi)
-{
- FILE *fp = NULL;
- size_t size;
- char *data = NULL;
- NSVGimage *image = NULL;
-
- fp = fopen(filename, "rb");
- if (!fp)
- goto error;
- fseek(fp, 0, SEEK_END);
- size = ftell(fp);
- fseek(fp, 0, SEEK_SET);
- data = (char *)malloc(size + 1);
- if (data == NULL)
- goto error;
- if (fread(data, 1, size, fp) != size)
- goto error;
- data[size] = '\0'; // Must be null terminated.
- fclose(fp);
- image = nsvgParse(data, units, dpi);
- free(data);
-
- return image;
-
-error:
- if (fp)
- fclose(fp);
- if (data)
- free(data);
- if (image)
- nsvgDelete(image);
- return NULL;
-}
-
-NSVGpath *nsvgDuplicatePath(NSVGpath *p)
-{
- NSVGpath *res = NULL;
-
- if (p == NULL)
- return NULL;
-
- res = (NSVGpath *)malloc(sizeof(NSVGpath));
- if (res == NULL)
- goto error;
- memset(res, 0, sizeof(NSVGpath));
-
- res->pts = (float *)malloc(p->npts * 2 * sizeof(float));
- if (res->pts == NULL)
- goto error;
- memcpy(res->pts, p->pts, p->npts * sizeof(float) * 2);
- res->npts = p->npts;
-
- memcpy(res->bounds, p->bounds, sizeof(p->bounds));
-
- res->closed = p->closed;
-
- return res;
-
-error:
- if (res != NULL) {
- free(res->pts);
- free(res);
- }
- return NULL;
-}
-
-void nsvgDelete(NSVGimage *image)
-{
- NSVGshape *snext, *shape;
- if (image == NULL)
- return;
- shape = image->shapes;
- while (shape != NULL) {
- snext = shape->next;
- nsvg__deletePaths(shape->paths);
- nsvg__deletePaint(&shape->fill);
- nsvg__deletePaint(&shape->stroke);
- free(shape);
- shape = snext;
- }
- free(image);
-}
-
-#endif
diff --git a/source/blender/io/usd/CMakeLists.txt b/source/blender/io/usd/CMakeLists.txt
index 6ea30f48a13..5499fe36898 100644
--- a/source/blender/io/usd/CMakeLists.txt
+++ b/source/blender/io/usd/CMakeLists.txt
@@ -56,7 +56,9 @@ set(INC_SYS
)
set(SRC
- intern/usd_capi.cc
+ intern/usd_capi_export.cc
+ intern/usd_capi_import.cc
+ intern/usd_common.cc
intern/usd_hierarchy_iterator.cc
intern/usd_writer_abstract.cc
intern/usd_writer_camera.cc
@@ -66,7 +68,21 @@ set(SRC
intern/usd_writer_metaball.cc
intern/usd_writer_transform.cc
+ intern/usd_reader_camera.cc
+ intern/usd_reader_curve.cc
+ intern/usd_reader_geom.cc
+ intern/usd_reader_light.cc
+ intern/usd_reader_material.cc
+ intern/usd_reader_mesh.cc
+ intern/usd_reader_nurbs.cc
+ intern/usd_reader_prim.cc
+ intern/usd_reader_stage.cc
+ intern/usd_reader_xform.cc
+ intern/usd_reader_volume.cc
+
usd.h
+
+ intern/usd_common.h
intern/usd_exporter_context.h
intern/usd_hierarchy_iterator.h
intern/usd_writer_abstract.h
@@ -76,6 +92,18 @@ set(SRC
intern/usd_writer_mesh.h
intern/usd_writer_metaball.h
intern/usd_writer_transform.h
+
+ intern/usd_reader_camera.h
+ intern/usd_reader_curve.h
+ intern/usd_reader_geom.h
+ intern/usd_reader_light.h
+ intern/usd_reader_material.h
+ intern/usd_reader_mesh.h
+ intern/usd_reader_nurbs.h
+ intern/usd_reader_prim.h
+ intern/usd_reader_stage.h
+ intern/usd_reader_xform.h
+ intern/usd_reader_volume.h
)
set(LIB
diff --git a/source/blender/io/usd/intern/usd_capi.cc b/source/blender/io/usd/intern/usd_capi_export.cc
index dc2b46e5cea..efa31df25c1 100644
--- a/source/blender/io/usd/intern/usd_capi.cc
+++ b/source/blender/io/usd/intern/usd_capi_export.cc
@@ -18,6 +18,7 @@
*/
#include "usd.h"
+#include "usd_common.h"
#include "usd_hierarchy_iterator.h"
#include <pxr/base/plug/registry.h>
@@ -59,21 +60,6 @@ struct ExportJobData {
bool export_ok;
};
-static void ensure_usd_plugin_path_registered()
-{
- static bool plugin_path_registered = false;
- if (plugin_path_registered) {
- return;
- }
- plugin_path_registered = true;
-
- /* Tell USD which directory to search for its JSON files. If 'datafiles/usd'
- * does not exist, the USD library will not be able to read or write any files. */
- const std::string blender_usd_datafiles = BKE_appdir_folder_id(BLENDER_DATAFILES, "usd");
- /* The trailing slash indicates to the USD library that the path is a directory. */
- pxr::PlugRegistry::GetInstance().RegisterPlugins(blender_usd_datafiles + "/");
-}
-
static void export_startjob(void *customdata,
/* Cannot be const, this function implements wm_jobs_start_callback.
* NOLINTNEXTLINE: readability-non-const-parameter. */
@@ -116,7 +102,7 @@ static void export_startjob(void *customdata,
usd_stage->SetMetadata(pxr::UsdGeomTokens->upAxis, pxr::VtValue(pxr::UsdGeomTokens->z));
usd_stage->SetMetadata(pxr::UsdGeomTokens->metersPerUnit,
- pxr::VtValue(scene->unit.scale_length));
+ static_cast<double>(scene->unit.scale_length));
usd_stage->GetRootLayer()->SetDocumentation(std::string("Blender v") +
BKE_blender_version_string());
diff --git a/source/blender/io/usd/intern/usd_capi_import.cc b/source/blender/io/usd/intern/usd_capi_import.cc
new file mode 100644
index 00000000000..789ff20ba82
--- /dev/null
+++ b/source/blender/io/usd/intern/usd_capi_import.cc
@@ -0,0 +1,577 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2019 Blender Foundation.
+ * All rights reserved.
+ */
+
+#include "IO_types.h"
+#include "usd.h"
+#include "usd_common.h"
+#include "usd_hierarchy_iterator.h"
+#include "usd_reader_geom.h"
+#include "usd_reader_prim.h"
+#include "usd_reader_stage.h"
+
+#include "BKE_appdir.h"
+#include "BKE_blender_version.h"
+#include "BKE_cachefile.h"
+#include "BKE_cdderivedmesh.h"
+#include "BKE_context.h"
+#include "BKE_global.h"
+#include "BKE_layer.h"
+#include "BKE_lib_id.h"
+#include "BKE_library.h"
+#include "BKE_main.h"
+#include "BKE_node.h"
+#include "BKE_object.h"
+#include "BKE_scene.h"
+#include "BKE_world.h"
+
+#include "BLI_fileops.h"
+#include "BLI_listbase.h"
+#include "BLI_math_matrix.h"
+#include "BLI_math_rotation.h"
+#include "BLI_path_util.h"
+#include "BLI_string.h"
+
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_build.h"
+#include "DEG_depsgraph_query.h"
+
+#include "DNA_cachefile_types.h"
+#include "DNA_collection_types.h"
+#include "DNA_node_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_world_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include <pxr/usd/usd/stage.h>
+#include <pxr/usd/usdGeom/metrics.h>
+#include <pxr/usd/usdGeom/scope.h>
+#include <pxr/usd/usdGeom/tokens.h>
+#include <pxr/usd/usdGeom/xformCommonAPI.h>
+
+#include <iostream>
+
+namespace blender::io::usd {
+
+static CacheArchiveHandle *handle_from_stage_reader(USDStageReader *reader)
+{
+ return reinterpret_cast<CacheArchiveHandle *>(reader);
+}
+
+static USDStageReader *stage_reader_from_handle(CacheArchiveHandle *handle)
+{
+ return reinterpret_cast<USDStageReader *>(handle);
+}
+
+static bool gather_objects_paths(const pxr::UsdPrim &object, ListBase *object_paths)
+{
+ if (!object.IsValid()) {
+ return false;
+ }
+
+ for (const pxr::UsdPrim &childPrim : object.GetChildren()) {
+ gather_objects_paths(childPrim, object_paths);
+ }
+
+ void *usd_path_void = MEM_callocN(sizeof(CacheObjectPath), "CacheObjectPath");
+ CacheObjectPath *usd_path = static_cast<CacheObjectPath *>(usd_path_void);
+
+ BLI_strncpy(usd_path->path, object.GetPrimPath().GetString().c_str(), sizeof(usd_path->path));
+ BLI_addtail(object_paths, usd_path);
+
+ return true;
+}
+
+/* Update the given import settings with the global rotation matrix to orient
+ * imported objects with Z-up, if necessary */
+static void convert_to_z_up(pxr::UsdStageRefPtr stage, ImportSettings *r_settings)
+{
+ if (!stage || pxr::UsdGeomGetStageUpAxis(stage) == pxr::UsdGeomTokens->z) {
+ return;
+ }
+
+ if (!r_settings) {
+ return;
+ }
+
+ r_settings->do_convert_mat = true;
+
+ /* Rotate 90 degrees about the X-axis. */
+ float rmat[3][3];
+ float axis[3] = {1.0f, 0.0f, 0.0f};
+ axis_angle_normalized_to_mat3(rmat, axis, M_PI / 2.0f);
+
+ unit_m4(r_settings->conversion_mat);
+ copy_m4_m3(r_settings->conversion_mat, rmat);
+}
+
+enum {
+ USD_NO_ERROR = 0,
+ USD_ARCHIVE_FAIL,
+};
+
+struct ImportJobData {
+ Main *bmain;
+ Scene *scene;
+ ViewLayer *view_layer;
+ wmWindowManager *wm;
+
+ char filename[1024];
+ USDImportParams params;
+ ImportSettings settings;
+
+ USDStageReader *archive;
+
+ short *stop;
+ short *do_update;
+ float *progress;
+
+ char error_code;
+ bool was_canceled;
+ bool import_ok;
+};
+
+static void import_startjob(void *customdata, short *stop, short *do_update, float *progress)
+{
+ ImportJobData *data = static_cast<ImportJobData *>(customdata);
+
+ data->stop = stop;
+ data->do_update = do_update;
+ data->progress = progress;
+ data->was_canceled = false;
+ data->archive = nullptr;
+
+ WM_set_locked_interface(data->wm, true);
+ G.is_break = false;
+
+ if (data->params.create_collection) {
+ char display_name[1024];
+ BLI_path_to_display_name(
+ display_name, strlen(data->filename), BLI_path_basename(data->filename));
+ Collection *import_collection = BKE_collection_add(
+ data->bmain, data->scene->master_collection, display_name);
+ id_fake_user_set(&import_collection->id);
+
+ DEG_id_tag_update(&import_collection->id, ID_RECALC_COPY_ON_WRITE);
+ DEG_relations_tag_update(data->bmain);
+
+ WM_main_add_notifier(NC_SCENE | ND_LAYER, nullptr);
+
+ data->view_layer->active_collection = BKE_layer_collection_first_from_scene_collection(
+ data->view_layer, import_collection);
+ }
+
+ BLI_path_abs(data->filename, BKE_main_blendfile_path_from_global());
+
+ CacheFile *cache_file = static_cast<CacheFile *>(
+ BKE_cachefile_add(data->bmain, BLI_path_basename(data->filename)));
+
+ /* Decrement the ID ref-count because it is going to be incremented for each
+ * modifier and constraint that it will be attached to, so since currently
+ * it is not used by anyone, its use count will off by one. */
+ id_us_min(&cache_file->id);
+
+ cache_file->is_sequence = data->params.is_sequence;
+ cache_file->scale = data->params.scale;
+ STRNCPY(cache_file->filepath, data->filename);
+
+ data->settings.cache_file = cache_file;
+
+ *data->do_update = true;
+ *data->progress = 0.05f;
+
+ if (G.is_break) {
+ data->was_canceled = true;
+ return;
+ }
+
+ *data->do_update = true;
+ *data->progress = 0.1f;
+
+ pxr::UsdStageRefPtr stage = pxr::UsdStage::Open(data->filename);
+
+ if (!stage) {
+ WM_reportf(RPT_ERROR, "USD Import: unable to open stage to read %s", data->filename);
+ data->import_ok = false;
+ return;
+ }
+
+ convert_to_z_up(stage, &data->settings);
+
+ /* Set up the stage for animated data. */
+ if (data->params.set_frame_range) {
+ data->scene->r.sfra = stage->GetStartTimeCode();
+ data->scene->r.efra = stage->GetEndTimeCode();
+ }
+
+ *data->progress = 0.15f;
+
+ USDStageReader *archive = new USDStageReader(stage, data->params, data->settings);
+
+ data->archive = archive;
+
+ archive->collect_readers(data->bmain);
+
+ *data->progress = 0.2f;
+
+ const float size = static_cast<float>(archive->readers().size());
+ size_t i = 0;
+
+ /* Setup parenthood */
+
+ for (USDPrimReader *reader : archive->readers()) {
+
+ if (!reader) {
+ continue;
+ }
+
+ Object *ob = reader->object();
+
+ reader->read_object_data(data->bmain, 0.0);
+
+ USDPrimReader *parent = reader->parent();
+
+ if (parent == nullptr) {
+ ob->parent = nullptr;
+ }
+ else {
+ ob->parent = parent->object();
+ }
+
+ *data->progress = 0.2f + 0.8f * (++i / size);
+ *data->do_update = true;
+
+ if (G.is_break) {
+ data->was_canceled = true;
+ return;
+ }
+ }
+
+ data->import_ok = !data->was_canceled;
+
+ *progress = 1.0f;
+ *do_update = true;
+}
+
+static void import_endjob(void *customdata)
+{
+ ImportJobData *data = static_cast<ImportJobData *>(customdata);
+
+ /* Delete objects on cancellation. */
+ if (data->was_canceled && data->archive) {
+
+ for (USDPrimReader *reader : data->archive->readers()) {
+
+ if (!reader) {
+ continue;
+ }
+
+ /* It's possible that cancellation occurred between the creation of
+ * the reader and the creation of the Blender object. */
+ if (Object *ob = reader->object()) {
+ BKE_id_free_us(data->bmain, ob);
+ }
+ }
+ }
+ else if (data->archive) {
+ /* Add object to scene. */
+ Base *base;
+ LayerCollection *lc;
+ ViewLayer *view_layer = data->view_layer;
+
+ BKE_view_layer_base_deselect_all(view_layer);
+
+ lc = BKE_layer_collection_get_active(view_layer);
+
+ for (USDPrimReader *reader : data->archive->readers()) {
+
+ if (!reader) {
+ continue;
+ }
+
+ Object *ob = reader->object();
+
+ if (!ob) {
+ continue;
+ }
+
+ BKE_collection_object_add(data->bmain, lc->collection, ob);
+
+ base = BKE_view_layer_base_find(view_layer, ob);
+ /* TODO: is setting active needed? */
+ BKE_view_layer_base_select_and_set_active(view_layer, base);
+
+ DEG_id_tag_update(&lc->collection->id, ID_RECALC_COPY_ON_WRITE);
+ DEG_id_tag_update_ex(data->bmain,
+ &ob->id,
+ ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION |
+ ID_RECALC_BASE_FLAGS);
+ }
+
+ DEG_id_tag_update(&data->scene->id, ID_RECALC_BASE_FLAGS);
+ DEG_relations_tag_update(data->bmain);
+ }
+
+ WM_set_locked_interface(data->wm, false);
+
+ switch (data->error_code) {
+ default:
+ case USD_NO_ERROR:
+ data->import_ok = !data->was_canceled;
+ break;
+ case USD_ARCHIVE_FAIL:
+ WM_report(RPT_ERROR, "Could not open USD archive for reading! See console for detail.");
+ break;
+ }
+
+ WM_main_add_notifier(NC_SCENE | ND_FRAME, data->scene);
+}
+
+static void import_freejob(void *user_data)
+{
+ ImportJobData *data = static_cast<ImportJobData *>(user_data);
+
+ delete data->archive;
+ delete data;
+}
+
+} // namespace blender::io::usd
+
+using namespace blender::io::usd;
+
+bool USD_import(struct bContext *C,
+ const char *filepath,
+ const USDImportParams *params,
+ bool as_background_job)
+{
+ blender::io::usd::ensure_usd_plugin_path_registered();
+
+ /* Using new here since `MEM_*` functions do not call constructor to properly initialize data. */
+ ImportJobData *job = new ImportJobData();
+ job->bmain = CTX_data_main(C);
+ job->scene = CTX_data_scene(C);
+ job->view_layer = CTX_data_view_layer(C);
+ job->wm = CTX_wm_manager(C);
+ job->import_ok = false;
+ BLI_strncpy(job->filename, filepath, 1024);
+
+ job->settings.scale = params->scale;
+ job->settings.sequence_offset = params->offset;
+ job->settings.is_sequence = params->is_sequence;
+ job->settings.sequence_len = params->sequence_len;
+ job->settings.validate_meshes = params->validate_meshes;
+ job->settings.sequence_len = params->sequence_len;
+ job->error_code = USD_NO_ERROR;
+ job->was_canceled = false;
+ job->archive = nullptr;
+
+ job->params = *params;
+
+ G.is_break = false;
+
+ bool import_ok = false;
+ if (as_background_job) {
+ wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
+ CTX_wm_window(C),
+ job->scene,
+ "USD Import",
+ WM_JOB_PROGRESS,
+ WM_JOB_TYPE_ALEMBIC);
+
+ /* setup job */
+ WM_jobs_customdata_set(wm_job, job, import_freejob);
+ WM_jobs_timer(wm_job, 0.1, NC_SCENE, NC_SCENE);
+ WM_jobs_callbacks(wm_job, import_startjob, nullptr, nullptr, import_endjob);
+
+ WM_jobs_start(CTX_wm_manager(C), wm_job);
+ }
+ else {
+ /* Fake a job context, so that we don't need NULL pointer checks while importing. */
+ short stop = 0, do_update = 0;
+ float progress = 0.f;
+
+ import_startjob(job, &stop, &do_update, &progress);
+ import_endjob(job);
+ import_ok = job->import_ok;
+
+ import_freejob(job);
+ }
+
+ return import_ok;
+}
+
+/* TODO(makowalski): Extend this function with basic validation that the
+ * USD reader is compatible with the type of the given (currently unused) 'ob'
+ * Object parameter, similar to the logic in get_abc_reader() in the
+ * Alembic importer code. */
+static USDPrimReader *get_usd_reader(CacheReader *reader, Object * /* ob */, const char **err_str)
+{
+ USDPrimReader *usd_reader = reinterpret_cast<USDPrimReader *>(reader);
+ pxr::UsdPrim iobject = usd_reader->prim();
+
+ if (!iobject.IsValid()) {
+ *err_str = "Invalid object: verify object path";
+ return nullptr;
+ }
+
+ return usd_reader;
+}
+
+struct Mesh *USD_read_mesh(struct CacheReader *reader,
+ struct Object *ob,
+ struct Mesh *existing_mesh,
+ const float time,
+ const char **err_str,
+ const int read_flag)
+{
+ USDGeomReader *usd_reader = dynamic_cast<USDGeomReader *>(get_usd_reader(reader, ob, err_str));
+
+ if (usd_reader == nullptr) {
+ return nullptr;
+ }
+
+ return usd_reader->read_mesh(existing_mesh, time, read_flag, err_str);
+}
+
+bool USD_mesh_topology_changed(
+ CacheReader *reader, Object *ob, Mesh *existing_mesh, const float time, const char **err_str)
+{
+ USDGeomReader *usd_reader = dynamic_cast<USDGeomReader *>(get_usd_reader(reader, ob, err_str));
+
+ if (usd_reader == nullptr) {
+ return false;
+ }
+
+ return usd_reader->topology_changed(existing_mesh, time);
+}
+
+void USD_CacheReader_incref(CacheReader *reader)
+{
+ USDPrimReader *usd_reader = reinterpret_cast<USDPrimReader *>(reader);
+ usd_reader->incref();
+}
+
+CacheReader *CacheReader_open_usd_object(CacheArchiveHandle *handle,
+ CacheReader *reader,
+ Object *object,
+ const char *object_path)
+{
+ if (object_path[0] == '\0') {
+ return reader;
+ }
+
+ USDStageReader *archive = stage_reader_from_handle(handle);
+
+ if (!archive || !archive->valid()) {
+ return reader;
+ }
+
+ pxr::UsdPrim prim = archive->stage()->GetPrimAtPath(pxr::SdfPath(object_path));
+
+ if (reader) {
+ USD_CacheReader_free(reader);
+ }
+
+ /* TODO(makowalski): The handle does not have the proper import params or settings. */
+ USDPrimReader *usd_reader = archive->create_reader(prim);
+
+ if (usd_reader == nullptr) {
+ /* This object is not supported. */
+ return nullptr;
+ }
+ usd_reader->object(object);
+ usd_reader->incref();
+
+ return reinterpret_cast<CacheReader *>(usd_reader);
+}
+
+void USD_CacheReader_free(CacheReader *reader)
+{
+ USDPrimReader *usd_reader = reinterpret_cast<USDPrimReader *>(reader);
+ usd_reader->decref();
+
+ if (usd_reader->refcount() == 0) {
+ delete usd_reader;
+ }
+}
+
+CacheArchiveHandle *USD_create_handle(struct Main * /*bmain*/,
+ const char *filename,
+ ListBase *object_paths)
+{
+ pxr::UsdStageRefPtr stage = pxr::UsdStage::Open(filename);
+
+ if (!stage) {
+ return nullptr;
+ }
+
+ USDImportParams params{};
+
+ blender::io::usd::ImportSettings settings{};
+ convert_to_z_up(stage, &settings);
+
+ USDStageReader *stage_reader = new USDStageReader(stage, params, settings);
+
+ if (object_paths) {
+ gather_objects_paths(stage->GetPseudoRoot(), object_paths);
+ }
+
+ return handle_from_stage_reader(stage_reader);
+}
+
+void USD_free_handle(CacheArchiveHandle *handle)
+{
+ USDStageReader *stage_reader = stage_reader_from_handle(handle);
+ delete stage_reader;
+}
+
+void USD_get_transform(struct CacheReader *reader,
+ float r_mat_world[4][4],
+ float time,
+ float scale)
+{
+ if (!reader) {
+ return;
+ }
+ USDXformReader *usd_reader = reinterpret_cast<USDXformReader *>(reader);
+
+ bool is_constant = false;
+
+ /* Convert from the local matrix we obtain from USD to world coordinates
+ * for Blender. This conversion is done here rather than by Blender due to
+ * work around the non-standard interpretation of CONSTRAINT_SPACE_LOCAL in
+ * BKE_constraint_mat_convertspace(). */
+ Object *object = usd_reader->object();
+ if (object->parent == nullptr) {
+ /* No parent, so local space is the same as world space. */
+ usd_reader->read_matrix(r_mat_world, time, scale, &is_constant);
+ return;
+ }
+
+ float mat_parent[4][4];
+ BKE_object_get_parent_matrix(object, object->parent, mat_parent);
+
+ float mat_local[4][4];
+ usd_reader->read_matrix(mat_local, time, scale, &is_constant);
+ mul_m4_m4m4(r_mat_world, mat_parent, object->parentinv);
+ mul_m4_m4m4(r_mat_world, r_mat_world, mat_local);
+}
diff --git a/source/blender/io/usd/intern/usd_common.cc b/source/blender/io/usd/intern/usd_common.cc
new file mode 100644
index 00000000000..0cd9c3019ef
--- /dev/null
+++ b/source/blender/io/usd/intern/usd_common.cc
@@ -0,0 +1,43 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2021 Blender Foundation.
+ * All rights reserved.
+ */
+
+#include "usd_common.h"
+
+#include <pxr/base/plug/registry.h>
+
+#include "BKE_appdir.h"
+
+namespace blender::io::usd {
+
+void ensure_usd_plugin_path_registered()
+{
+ static bool plugin_path_registered = false;
+ if (plugin_path_registered) {
+ return;
+ }
+ plugin_path_registered = true;
+
+ /* Tell USD which directory to search for its JSON files. If 'datafiles/usd'
+ * does not exist, the USD library will not be able to read or write any files. */
+ const std::string blender_usd_datafiles = BKE_appdir_folder_id(BLENDER_DATAFILES, "usd");
+ /* The trailing slash indicates to the USD library that the path is a directory. */
+ pxr::PlugRegistry::GetInstance().RegisterPlugins(blender_usd_datafiles + "/");
+}
+
+} // namespace blender::io::usd
diff --git a/source/blender/nodes/NOD_type_callbacks.hh b/source/blender/io/usd/intern/usd_common.h
index d1a4bd3ad7a..36667bbc6b1 100644
--- a/source/blender/nodes/NOD_type_callbacks.hh
+++ b/source/blender/io/usd/intern/usd_common.h
@@ -12,25 +12,14 @@
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2021 Blender Foundation.
+ * All rights reserved.
*/
-
#pragma once
-#include <optional>
-
-#include "BKE_node.h"
-
-#include "FN_multi_function_data_type.hh"
-
-namespace blender::nodes {
-
-using fn::CPPType;
-using fn::MFDataType;
+namespace blender::io::usd {
-const CPPType *socket_cpp_type_get(const bNodeSocketType &stype);
-std::optional<MFDataType> socket_mf_type_get(const bNodeSocketType &stype);
-bool socket_is_mf_data_socket(const bNodeSocketType &stype);
-bool socket_cpp_value_get(const bNodeSocket &socket, void *r_value);
-void socket_expand_in_mf_network(SocketMFNetworkBuilder &builder);
+void ensure_usd_plugin_path_registered();
-} // namespace blender::nodes
+} // namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_reader_camera.cc b/source/blender/io/usd/intern/usd_reader_camera.cc
new file mode 100644
index 00000000000..2732ed5770d
--- /dev/null
+++ b/source/blender/io/usd/intern/usd_reader_camera.cc
@@ -0,0 +1,100 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Adapted from the Blender Alembic importer implementation.
+ *
+ * Modifications Copyright (C) 2021 Tangent Animation.
+ * All rights reserved.
+ */
+
+#include "usd_reader_camera.h"
+
+#include "DNA_camera_types.h"
+#include "DNA_object_types.h"
+
+#include "BKE_camera.h"
+#include "BKE_object.h"
+
+#include "BLI_math.h"
+
+#include <pxr/pxr.h>
+#include <pxr/usd/usdGeom/camera.h>
+
+namespace blender::io::usd {
+
+void USDCameraReader::create_object(Main *bmain, const double /* motionSampleTime */)
+{
+ Camera *bcam = static_cast<Camera *>(BKE_camera_add(bmain, name_.c_str()));
+
+ object_ = BKE_object_add_only_object(bmain, OB_CAMERA, name_.c_str());
+ object_->data = bcam;
+}
+
+void USDCameraReader::read_object_data(Main *bmain, const double motionSampleTime)
+{
+ Camera *bcam = (Camera *)object_->data;
+
+ pxr::UsdGeomCamera cam_prim(prim_);
+
+ if (!cam_prim) {
+ return;
+ }
+
+ pxr::VtValue val;
+ cam_prim.GetFocalLengthAttr().Get(&val, motionSampleTime);
+ pxr::VtValue verApOffset;
+ cam_prim.GetVerticalApertureOffsetAttr().Get(&verApOffset, motionSampleTime);
+ pxr::VtValue horApOffset;
+ cam_prim.GetHorizontalApertureOffsetAttr().Get(&horApOffset, motionSampleTime);
+ pxr::VtValue clippingRangeVal;
+ cam_prim.GetClippingRangeAttr().Get(&clippingRangeVal, motionSampleTime);
+ pxr::VtValue focalDistanceVal;
+ cam_prim.GetFocusDistanceAttr().Get(&focalDistanceVal, motionSampleTime);
+ pxr::VtValue fstopVal;
+ cam_prim.GetFStopAttr().Get(&fstopVal, motionSampleTime);
+ pxr::VtValue projectionVal;
+ cam_prim.GetProjectionAttr().Get(&projectionVal, motionSampleTime);
+ pxr::VtValue verAp;
+ cam_prim.GetVerticalApertureAttr().Get(&verAp, motionSampleTime);
+ pxr::VtValue horAp;
+ cam_prim.GetHorizontalApertureAttr().Get(&horAp, motionSampleTime);
+
+ bcam->lens = val.Get<float>();
+ /* TODO(makowalski) */
+#if 0
+ bcam->sensor_x = 0.0f;
+ bcam->sensor_y = 0.0f;
+#endif
+ bcam->shiftx = verApOffset.Get<float>();
+ bcam->shifty = horApOffset.Get<float>();
+
+ bcam->type = (projectionVal.Get<pxr::TfToken>().GetString() == "perspective") ? CAM_PERSP :
+ CAM_ORTHO;
+
+ /* Calling UncheckedGet() to silence compiler warnings. */
+ bcam->clip_start = max_ff(0.1f, clippingRangeVal.UncheckedGet<pxr::GfVec2f>()[0]);
+ bcam->clip_end = clippingRangeVal.UncheckedGet<pxr::GfVec2f>()[1];
+
+ bcam->dof.focus_distance = focalDistanceVal.Get<float>();
+ bcam->dof.aperture_fstop = static_cast<float>(fstopVal.Get<float>());
+
+ if (bcam->type == CAM_ORTHO) {
+ bcam->ortho_scale = max_ff(verAp.Get<float>(), horAp.Get<float>());
+ }
+
+ USDXformReader::read_object_data(bmain, motionSampleTime);
+}
+
+} // namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_reader_camera.h b/source/blender/io/usd/intern/usd_reader_camera.h
new file mode 100644
index 00000000000..a4156aa8be2
--- /dev/null
+++ b/source/blender/io/usd/intern/usd_reader_camera.h
@@ -0,0 +1,42 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Adapted from the Blender Alembic importer implementation.
+ *
+ * Modifications Copyright (C) 2021 Tangent Animation.
+ * All rights reserved.
+ */
+#pragma once
+
+#include "usd.h"
+#include "usd_reader_xform.h"
+
+namespace blender::io::usd {
+
+class USDCameraReader : public USDXformReader {
+
+ public:
+ USDCameraReader(const pxr::UsdPrim &object,
+ const USDImportParams &import_params,
+ const ImportSettings &settings)
+ : USDXformReader(object, import_params, settings)
+ {
+ }
+
+ void create_object(Main *bmain, double motionSampleTime) override;
+ void read_object_data(Main *bmain, double motionSampleTime) override;
+};
+
+} // namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_reader_curve.cc b/source/blender/io/usd/intern/usd_reader_curve.cc
new file mode 100644
index 00000000000..31ecf27cf7e
--- /dev/null
+++ b/source/blender/io/usd/intern/usd_reader_curve.cc
@@ -0,0 +1,256 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Adapted from the Blender Alembic importer implementation,
+ * Copyright (C) 2016 Kévin Dietrich.
+ *
+ * Modifications Copyright (C) 2021 Tangent Animation.
+ * All rights reserved.
+ */
+
+#include "usd_reader_curve.h"
+
+#include "BKE_curve.h"
+#include "BKE_mesh.h"
+#include "BKE_object.h"
+
+#include "BLI_listbase.h"
+
+#include "DNA_curve_types.h"
+#include "DNA_object_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include <pxr/base/vt/array.h>
+#include <pxr/base/vt/types.h>
+#include <pxr/base/vt/value.h>
+
+#include <pxr/usd/usdGeom/basisCurves.h>
+#include <pxr/usd/usdGeom/curves.h>
+
+namespace blender::io::usd {
+
+void USDCurvesReader::create_object(Main *bmain, const double /* motionSampleTime */)
+{
+ curve_ = BKE_curve_add(bmain, name_.c_str(), OB_CURVE);
+
+ curve_->flag |= CU_DEFORM_FILL | CU_3D;
+ curve_->actvert = CU_ACT_NONE;
+ curve_->resolu = 2;
+
+ object_ = BKE_object_add_only_object(bmain, OB_CURVE, name_.c_str());
+ object_->data = curve_;
+}
+
+void USDCurvesReader::read_object_data(Main *bmain, double motionSampleTime)
+{
+ Curve *cu = (Curve *)object_->data;
+ read_curve_sample(cu, motionSampleTime);
+
+ if (curve_prim_.GetPointsAttr().ValueMightBeTimeVarying()) {
+ add_cache_modifier();
+ }
+
+ USDXformReader::read_object_data(bmain, motionSampleTime);
+}
+
+void USDCurvesReader::read_curve_sample(Curve *cu, const double motionSampleTime)
+{
+ curve_prim_ = pxr::UsdGeomBasisCurves(prim_);
+
+ if (!curve_prim_) {
+ return;
+ }
+
+ pxr::UsdAttribute widthsAttr = curve_prim_.GetWidthsAttr();
+ pxr::UsdAttribute vertexAttr = curve_prim_.GetCurveVertexCountsAttr();
+ pxr::UsdAttribute pointsAttr = curve_prim_.GetPointsAttr();
+
+ pxr::VtIntArray usdCounts;
+
+ vertexAttr.Get(&usdCounts, motionSampleTime);
+ int num_subcurves = usdCounts.size();
+
+ pxr::VtVec3fArray usdPoints;
+ pointsAttr.Get(&usdPoints, motionSampleTime);
+
+ pxr::VtFloatArray usdWidths;
+ widthsAttr.Get(&usdWidths, motionSampleTime);
+
+ pxr::UsdAttribute basisAttr = curve_prim_.GetBasisAttr();
+ pxr::TfToken basis;
+ basisAttr.Get(&basis, motionSampleTime);
+
+ pxr::UsdAttribute typeAttr = curve_prim_.GetTypeAttr();
+ pxr::TfToken type;
+ typeAttr.Get(&type, motionSampleTime);
+
+ pxr::UsdAttribute wrapAttr = curve_prim_.GetWrapAttr();
+ pxr::TfToken wrap;
+ wrapAttr.Get(&wrap, motionSampleTime);
+
+ pxr::VtVec3fArray usdNormals;
+ curve_prim_.GetNormalsAttr().Get(&usdNormals, motionSampleTime);
+
+ /* If normals, extrude, else bevel.
+ * Perhaps to be replaced by Blender/USD Schema. */
+ if (!usdNormals.empty()) {
+ /* Set extrusion to 1.0f. */
+ curve_->ext1 = 1.0f;
+ }
+ else {
+ /* Set bevel depth to 1.0f. */
+ curve_->ext2 = 1.0f;
+ }
+
+ size_t idx = 0;
+ for (size_t i = 0; i < num_subcurves; i++) {
+ const int num_verts = usdCounts[i];
+ Nurb *nu = static_cast<Nurb *>(MEM_callocN(sizeof(Nurb), __func__));
+
+ if (basis == pxr::UsdGeomTokens->bspline) {
+ nu->flag = CU_SMOOTH;
+ nu->type = CU_NURBS;
+ }
+ else if (basis == pxr::UsdGeomTokens->bezier) {
+ /* TODO(makowalski): Beziers are not properly imported as beziers. */
+ nu->type = CU_POLY;
+ }
+ else if (basis.IsEmpty()) {
+ nu->type = CU_POLY;
+ }
+ nu->resolu = cu->resolu;
+ nu->resolv = cu->resolv;
+
+ nu->pntsu = num_verts;
+ nu->pntsv = 1;
+
+ if (type == pxr::UsdGeomTokens->cubic) {
+ nu->orderu = 4;
+ }
+ else if (type == pxr::UsdGeomTokens->linear) {
+ nu->orderu = 2;
+ }
+
+ if (wrap == pxr::UsdGeomTokens->periodic) {
+ nu->flagu |= CU_NURB_CYCLIC;
+ }
+ else if (wrap == pxr::UsdGeomTokens->pinned) {
+ nu->flagu |= CU_NURB_ENDPOINT;
+ }
+
+ float weight = 1.0f;
+
+ nu->bp = static_cast<BPoint *>(MEM_callocN(sizeof(BPoint) * nu->pntsu, __func__));
+ BPoint *bp = nu->bp;
+
+ for (int j = 0; j < nu->pntsu; j++, bp++, idx++) {
+ bp->vec[0] = (float)usdPoints[idx][0];
+ bp->vec[1] = (float)usdPoints[idx][1];
+ bp->vec[2] = (float)usdPoints[idx][2];
+ bp->vec[3] = weight;
+ bp->f1 = SELECT;
+ bp->weight = weight;
+
+ float radius = curve_->width;
+ if (idx < usdWidths.size()) {
+ radius = usdWidths[idx];
+ }
+
+ bp->radius = radius;
+ }
+
+ BKE_nurb_knot_calc_u(nu);
+ BKE_nurb_knot_calc_v(nu);
+
+ BLI_addtail(BKE_curve_nurbs_get(cu), nu);
+ }
+}
+
+Mesh *USDCurvesReader::read_mesh(struct Mesh *existing_mesh,
+ const double motionSampleTime,
+ const int /* read_flag */,
+ const char ** /* err_str */)
+{
+ if (!curve_prim_) {
+ return existing_mesh;
+ }
+
+ pxr::UsdAttribute widthsAttr = curve_prim_.GetWidthsAttr();
+ pxr::UsdAttribute vertexAttr = curve_prim_.GetCurveVertexCountsAttr();
+ pxr::UsdAttribute pointsAttr = curve_prim_.GetPointsAttr();
+
+ pxr::VtIntArray usdCounts;
+
+ vertexAttr.Get(&usdCounts, motionSampleTime);
+ int num_subcurves = usdCounts.size();
+
+ pxr::VtVec3fArray usdPoints;
+ pointsAttr.Get(&usdPoints, motionSampleTime);
+
+ int vertex_idx = 0;
+ int curve_idx;
+ Curve *curve = static_cast<Curve *>(object_->data);
+
+ const int curve_count = BLI_listbase_count(&curve->nurb);
+ bool same_topology = curve_count == num_subcurves;
+
+ if (same_topology) {
+ Nurb *nurbs = static_cast<Nurb *>(curve->nurb.first);
+ for (curve_idx = 0; nurbs; nurbs = nurbs->next, curve_idx++) {
+ const int num_in_usd = usdCounts[curve_idx];
+ const int num_in_blender = nurbs->pntsu;
+
+ if (num_in_usd != num_in_blender) {
+ same_topology = false;
+ break;
+ }
+ }
+ }
+
+ if (!same_topology) {
+ BKE_nurbList_free(&curve->nurb);
+ read_curve_sample(curve, motionSampleTime);
+ }
+ else {
+ Nurb *nurbs = static_cast<Nurb *>(curve->nurb.first);
+ for (curve_idx = 0; nurbs; nurbs = nurbs->next, curve_idx++) {
+ const int totpoint = usdCounts[curve_idx];
+
+ if (nurbs->bp) {
+ BPoint *point = nurbs->bp;
+
+ for (int i = 0; i < totpoint; i++, point++, vertex_idx++) {
+ point->vec[0] = usdPoints[vertex_idx][0];
+ point->vec[1] = usdPoints[vertex_idx][1];
+ point->vec[2] = usdPoints[vertex_idx][2];
+ }
+ }
+ else if (nurbs->bezt) {
+ BezTriple *bezier = nurbs->bezt;
+
+ for (int i = 0; i < totpoint; i++, bezier++, vertex_idx++) {
+ bezier->vec[1][0] = usdPoints[vertex_idx][0];
+ bezier->vec[1][1] = usdPoints[vertex_idx][1];
+ bezier->vec[1][2] = usdPoints[vertex_idx][2];
+ }
+ }
+ }
+ }
+
+ return BKE_mesh_new_nomain_from_curve(object_);
+}
+
+} // namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_reader_curve.h b/source/blender/io/usd/intern/usd_reader_curve.h
new file mode 100644
index 00000000000..1e676bbbd02
--- /dev/null
+++ b/source/blender/io/usd/intern/usd_reader_curve.h
@@ -0,0 +1,62 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Adapted from the Blender Alembic importer implementation,
+ * Copyright (C) 2016 Kévin Dietrich.
+ *
+ * Modifications Copyright (C) 2021 Tangent Animation.
+ * All rights reserved.
+ */
+#pragma once
+
+#include "usd.h"
+#include "usd_reader_geom.h"
+
+#include "pxr/usd/usdGeom/basisCurves.h"
+
+struct Curve;
+
+namespace blender::io::usd {
+
+class USDCurvesReader : public USDGeomReader {
+ protected:
+ pxr::UsdGeomBasisCurves curve_prim_;
+ Curve *curve_;
+
+ public:
+ USDCurvesReader(const pxr::UsdPrim &prim,
+ const USDImportParams &import_params,
+ const ImportSettings &settings)
+ : USDGeomReader(prim, import_params, settings), curve_prim_(prim), curve_(nullptr)
+ {
+ }
+
+ bool valid() const override
+ {
+ return static_cast<bool>(curve_prim_);
+ }
+
+ void create_object(Main *bmain, double motionSampleTime) override;
+ void read_object_data(Main *bmain, double motionSampleTime) override;
+
+ void read_curve_sample(Curve *cu, double motionSampleTime);
+
+ Mesh *read_mesh(struct Mesh *existing_mesh,
+ double motionSampleTime,
+ int read_flag,
+ const char **err_str) override;
+};
+
+} // namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_reader_geom.cc b/source/blender/io/usd/intern/usd_reader_geom.cc
new file mode 100644
index 00000000000..23c5f57120c
--- /dev/null
+++ b/source/blender/io/usd/intern/usd_reader_geom.cc
@@ -0,0 +1,59 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2021 Tangent Animation.
+ * All rights reserved.
+ */
+
+#include "usd_reader_geom.h"
+
+#include "BKE_lib_id.h"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_math_geom.h"
+#include "BLI_string.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_cachefile_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_types.h"
+#include "DNA_space_types.h" /* for FILE_MAX */
+
+namespace blender::io::usd {
+
+void USDGeomReader::add_cache_modifier()
+{
+ ModifierData *md = BKE_modifier_new(eModifierType_MeshSequenceCache);
+ BLI_addtail(&object_->modifiers, md);
+
+ MeshSeqCacheModifierData *mcmd = reinterpret_cast<MeshSeqCacheModifierData *>(md);
+
+ mcmd->cache_file = settings_->cache_file;
+ id_us_plus(&mcmd->cache_file->id);
+ mcmd->read_flag = import_params_.mesh_read_flag;
+
+ BLI_strncpy(mcmd->object_path, prim_.GetPath().GetString().c_str(), FILE_MAX);
+}
+
+void USDGeomReader::add_subdiv_modifier()
+{
+ ModifierData *md = BKE_modifier_new(eModifierType_Subsurf);
+ BLI_addtail(&object_->modifiers, md);
+}
+
+} // namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_reader_geom.h b/source/blender/io/usd/intern/usd_reader_geom.h
new file mode 100644
index 00000000000..99e22248f2b
--- /dev/null
+++ b/source/blender/io/usd/intern/usd_reader_geom.h
@@ -0,0 +1,52 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2021 Tangent Animation.
+ * All rights reserved.
+ */
+#pragma once
+
+#include "usd.h"
+#include "usd_reader_xform.h"
+
+struct Mesh;
+
+namespace blender::io::usd {
+
+class USDGeomReader : public USDXformReader {
+
+ public:
+ USDGeomReader(const pxr::UsdPrim &prim,
+ const USDImportParams &import_params,
+ const ImportSettings &settings)
+ : USDXformReader(prim, import_params, settings)
+ {
+ }
+
+ virtual Mesh *read_mesh(struct Mesh *existing_mesh,
+ double motionSampleTime,
+ int read_flag,
+ const char **err_str) = 0;
+
+ virtual bool topology_changed(Mesh * /* existing_mesh */, double /* motionSampleTime */)
+ {
+ return true;
+ }
+
+ void add_cache_modifier();
+ void add_subdiv_modifier();
+};
+
+} // namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_reader_instance.cc b/source/blender/io/usd/intern/usd_reader_instance.cc
new file mode 100644
index 00000000000..e645b0237b9
--- /dev/null
+++ b/source/blender/io/usd/intern/usd_reader_instance.cc
@@ -0,0 +1,64 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2021 Blender Foundation.
+ * All rights reserved.
+ */
+
+#include "usd_reader_instance.h"
+
+#include "BKE_object.h"
+#include "DNA_object_types.h"
+
+#include <iostream>
+
+namespace blender::io::usd {
+
+USDInstanceReader::USDInstanceReader(const pxr::UsdPrim &prim,
+ const USDImportParams &import_params,
+ const ImportSettings &settings)
+ : USDXformReader(prim, import_params, settings)
+{
+}
+
+bool USDInstanceReader::valid() const
+{
+ return prim_.IsValid() && prim_.IsInstance();
+}
+
+void USDInstanceReader::create_object(Main *bmain, const double /* motionSampleTime */)
+{
+ this->object_ = BKE_object_add_only_object(bmain, OB_EMPTY, name_.c_str());
+ this->object_->data = nullptr;
+ this->object_->transflag |= OB_DUPLICOLLECTION;
+}
+
+void USDInstanceReader::set_instance_collection(Collection *coll)
+{
+ if (this->object_) {
+ this->object_->instance_collection = coll;
+ }
+}
+
+pxr::SdfPath USDInstanceReader::proto_path() const
+{
+ if (pxr::UsdPrim master = prim_.GetMaster()) {
+ return master.GetPath();
+ }
+
+ return pxr::SdfPath();
+}
+
+} // namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_reader_instance.h b/source/blender/io/usd/intern/usd_reader_instance.h
new file mode 100644
index 00000000000..efc1c69a7dd
--- /dev/null
+++ b/source/blender/io/usd/intern/usd_reader_instance.h
@@ -0,0 +1,47 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2021 Blender Foundation.
+ * All rights reserved.
+ */
+#pragma once
+
+#include "usd_reader_xform.h"
+
+#include <pxr/usd/usdGeom/xform.h>
+
+struct Collection;
+
+namespace blender::io::usd {
+
+/* Wraps the UsdGeomXform schema. Creates a Blender Empty object. */
+
+class USDInstanceReader : public USDXformReader {
+
+ public:
+ USDInstanceReader(const pxr::UsdPrim &prim,
+ const USDImportParams &import_params,
+ const ImportSettings &settings);
+
+ bool valid() const override;
+
+ void create_object(Main *bmain, double motionSampleTime) override;
+
+ void set_instance_collection(Collection *coll);
+
+ pxr::SdfPath proto_path() const;
+};
+
+} // namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_reader_light.cc b/source/blender/io/usd/intern/usd_reader_light.cc
new file mode 100644
index 00000000000..fda0c17968a
--- /dev/null
+++ b/source/blender/io/usd/intern/usd_reader_light.cc
@@ -0,0 +1,252 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2021 Tangent Animation.
+ * All rights reserved.
+ */
+
+#include "usd_reader_light.h"
+
+#include "BKE_light.h"
+#include "BKE_object.h"
+
+#include "DNA_light_types.h"
+#include "DNA_object_types.h"
+
+#include <pxr/usd/usdLux/light.h>
+
+#include <pxr/usd/usdLux/diskLight.h>
+#include <pxr/usd/usdLux/distantLight.h>
+#include <pxr/usd/usdLux/rectLight.h>
+#include <pxr/usd/usdLux/shapingAPI.h>
+#include <pxr/usd/usdLux/sphereLight.h>
+
+#include <iostream>
+
+namespace blender::io::usd {
+
+void USDLightReader::create_object(Main *bmain, const double /* motionSampleTime */)
+{
+ Light *blight = static_cast<Light *>(BKE_light_add(bmain, name_.c_str()));
+
+ object_ = BKE_object_add_only_object(bmain, OB_LAMP, name_.c_str());
+ object_->data = blight;
+}
+
+void USDLightReader::read_object_data(Main *bmain, const double motionSampleTime)
+{
+ Light *blight = (Light *)object_->data;
+
+ if (blight == nullptr) {
+ return;
+ }
+
+ if (!prim_) {
+ return;
+ }
+
+ pxr::UsdLuxLight light_prim(prim_);
+
+ if (!light_prim) {
+ return;
+ }
+
+ pxr::UsdLuxShapingAPI shaping_api(light_prim);
+
+ /* Set light type. */
+
+ if (prim_.IsA<pxr::UsdLuxDiskLight>()) {
+ blight->type = LA_AREA;
+ blight->area_shape = LA_AREA_DISK;
+ /* Ellipse lights are not currently supported */
+ }
+ else if (prim_.IsA<pxr::UsdLuxRectLight>()) {
+ blight->type = LA_AREA;
+ blight->area_shape = LA_AREA_RECT;
+ }
+ else if (prim_.IsA<pxr::UsdLuxSphereLight>()) {
+ blight->type = LA_LOCAL;
+
+ if (shaping_api && shaping_api.GetShapingConeAngleAttr().IsAuthored()) {
+ blight->type = LA_SPOT;
+ }
+ }
+ else if (prim_.IsA<pxr::UsdLuxDistantLight>()) {
+ blight->type = LA_SUN;
+ }
+
+ /* Set light values. */
+
+ if (pxr::UsdAttribute intensity_attr = light_prim.GetIntensityAttr()) {
+ float intensity = 0.0f;
+ if (intensity_attr.Get(&intensity, motionSampleTime)) {
+ blight->energy = intensity * this->import_params_.light_intensity_scale;
+ }
+ }
+
+ /* TODO(makowalsk): Not currently supported. */
+#if 0
+ pxr::VtValue exposure;
+ light_prim.GetExposureAttr().Get(&exposure, motionSampleTime);
+#endif
+
+ /* TODO(makowalsk): Not currently supported */
+#if 0
+ pxr::VtValue diffuse;
+ light_prim.GetDiffuseAttr().Get(&diffuse, motionSampleTime);
+#endif
+
+ if (pxr::UsdAttribute spec_attr = light_prim.GetSpecularAttr()) {
+ float spec = 0.0f;
+ if (spec_attr.Get(&spec, motionSampleTime)) {
+ blight->spec_fac = spec;
+ }
+ }
+
+ if (pxr::UsdAttribute color_attr = light_prim.GetColorAttr()) {
+ pxr::GfVec3f color;
+ if (color_attr.Get(&color, motionSampleTime)) {
+ blight->r = color[0];
+ blight->g = color[1];
+ blight->b = color[2];
+ }
+ }
+
+ /* TODO(makowalski): Not currently supported. */
+#if 0
+ pxr::VtValue use_color_temp;
+ light_prim.GetEnableColorTemperatureAttr().Get(&use_color_temp, motionSampleTime);
+#endif
+
+ /* TODO(makowalski): Not currently supported. */
+#if 0
+ pxr::VtValue color_temp;
+ light_prim.GetColorTemperatureAttr().Get(&color_temp, motionSampleTime);
+#endif
+
+ switch (blight->type) {
+ case LA_AREA:
+ if (blight->area_shape == LA_AREA_RECT && prim_.IsA<pxr::UsdLuxRectLight>()) {
+
+ pxr::UsdLuxRectLight rect_light(prim_);
+
+ if (!rect_light) {
+ break;
+ }
+
+ if (pxr::UsdAttribute width_attr = rect_light.GetWidthAttr()) {
+ float width = 0.0f;
+ if (width_attr.Get(&width, motionSampleTime)) {
+ blight->area_size = width;
+ }
+ }
+
+ if (pxr::UsdAttribute height_attr = rect_light.GetHeightAttr()) {
+ float height = 0.0f;
+ if (height_attr.Get(&height, motionSampleTime)) {
+ blight->area_sizey = height;
+ }
+ }
+ }
+ else if (blight->area_shape == LA_AREA_DISK && prim_.IsA<pxr::UsdLuxDiskLight>()) {
+
+ pxr::UsdLuxDiskLight disk_light(prim_);
+
+ if (!disk_light) {
+ break;
+ }
+
+ if (pxr::UsdAttribute radius_attr = disk_light.GetRadiusAttr()) {
+ float radius = 0.0f;
+ if (radius_attr.Get(&radius, motionSampleTime)) {
+ blight->area_size = radius * 2.0f;
+ }
+ }
+ }
+ break;
+ case LA_LOCAL:
+ if (prim_.IsA<pxr::UsdLuxSphereLight>()) {
+
+ pxr::UsdLuxSphereLight sphere_light(prim_);
+
+ if (!sphere_light) {
+ break;
+ }
+
+ if (pxr::UsdAttribute radius_attr = sphere_light.GetRadiusAttr()) {
+ float radius = 0.0f;
+ if (radius_attr.Get(&radius, motionSampleTime)) {
+ blight->area_size = radius;
+ }
+ }
+ }
+ break;
+ case LA_SPOT:
+ if (prim_.IsA<pxr::UsdLuxSphereLight>()) {
+
+ pxr::UsdLuxSphereLight sphere_light(prim_);
+
+ if (!sphere_light) {
+ break;
+ }
+
+ if (pxr::UsdAttribute radius_attr = sphere_light.GetRadiusAttr()) {
+ float radius = 0.0f;
+ if (radius_attr.Get(&radius, motionSampleTime)) {
+ blight->area_size = radius;
+ }
+ }
+
+ if (!shaping_api) {
+ break;
+ }
+
+ if (pxr::UsdAttribute cone_angle_attr = shaping_api.GetShapingConeAngleAttr()) {
+ float cone_angle = 0.0f;
+ if (cone_angle_attr.Get(&cone_angle, motionSampleTime)) {
+ blight->spotsize = cone_angle * ((float)M_PI / 180.0f) * 2.0f;
+ }
+ }
+
+ if (pxr::UsdAttribute cone_softness_attr = shaping_api.GetShapingConeSoftnessAttr()) {
+ float cone_softness = 0.0f;
+ if (cone_softness_attr.Get(&cone_softness, motionSampleTime)) {
+ blight->spotblend = cone_softness;
+ }
+ }
+ }
+ break;
+ case LA_SUN:
+ if (prim_.IsA<pxr::UsdLuxDistantLight>()) {
+ pxr::UsdLuxDistantLight distant_light(prim_);
+
+ if (!distant_light) {
+ break;
+ }
+
+ if (pxr::UsdAttribute angle_attr = distant_light.GetAngleAttr()) {
+ float angle = 0.0f;
+ if (angle_attr.Get(&angle, motionSampleTime)) {
+ blight->sun_angle = angle * (float)M_PI / 180.0f;
+ }
+ }
+ }
+ break;
+ }
+
+ USDXformReader::read_object_data(bmain, motionSampleTime);
+}
+
+} // namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_reader_light.h b/source/blender/io/usd/intern/usd_reader_light.h
new file mode 100644
index 00000000000..e7860fd2c80
--- /dev/null
+++ b/source/blender/io/usd/intern/usd_reader_light.h
@@ -0,0 +1,41 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2021 Tangent Animation.
+ * All rights reserved.
+ */
+#pragma once
+
+#include "usd.h"
+#include "usd_reader_xform.h"
+
+namespace blender::io::usd {
+
+class USDLightReader : public USDXformReader {
+
+ public:
+ USDLightReader(const pxr::UsdPrim &prim,
+ const USDImportParams &import_params,
+ const ImportSettings &settings)
+ : USDXformReader(prim, import_params, settings)
+ {
+ }
+
+ void create_object(Main *bmain, double motionSampleTime) override;
+
+ void read_object_data(Main *bmain, double motionSampleTime) override;
+};
+
+} // namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_reader_material.cc b/source/blender/io/usd/intern/usd_reader_material.cc
new file mode 100644
index 00000000000..02ed7c35e57
--- /dev/null
+++ b/source/blender/io/usd/intern/usd_reader_material.cc
@@ -0,0 +1,703 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2021 NVIDIA Corporation.
+ * All rights reserved.
+ */
+
+#include "usd_reader_material.h"
+
+#include "BKE_image.h"
+#include "BKE_main.h"
+#include "BKE_material.h"
+#include "BKE_node.h"
+
+#include "BLI_math_vector.h"
+#include "BLI_string.h"
+
+#include "DNA_material_types.h"
+
+#include <pxr/base/gf/vec3f.h>
+#include <pxr/usd/usdShade/material.h>
+#include <pxr/usd/usdShade/shader.h>
+
+#include <iostream>
+#include <vector>
+
+namespace usdtokens {
+
+/* Parameter names. */
+static const pxr::TfToken a("a", pxr::TfToken::Immortal);
+static const pxr::TfToken b("b", pxr::TfToken::Immortal);
+static const pxr::TfToken clearcoat("clearcoat", pxr::TfToken::Immortal);
+static const pxr::TfToken clearcoatRoughness("clearcoatRoughness", pxr::TfToken::Immortal);
+static const pxr::TfToken diffuseColor("diffuseColor", pxr::TfToken::Immortal);
+static const pxr::TfToken emissiveColor("emissiveColor", pxr::TfToken::Immortal);
+static const pxr::TfToken file("file", pxr::TfToken::Immortal);
+static const pxr::TfToken g("g", pxr::TfToken::Immortal);
+static const pxr::TfToken ior("ior", pxr::TfToken::Immortal);
+static const pxr::TfToken metallic("metallic", pxr::TfToken::Immortal);
+static const pxr::TfToken normal("normal", pxr::TfToken::Immortal);
+static const pxr::TfToken occlusion("occlusion", pxr::TfToken::Immortal);
+static const pxr::TfToken opacity("opacity", pxr::TfToken::Immortal);
+static const pxr::TfToken opacityThreshold("opacityThreshold", pxr::TfToken::Immortal);
+static const pxr::TfToken r("r", pxr::TfToken::Immortal);
+static const pxr::TfToken result("result", pxr::TfToken::Immortal);
+static const pxr::TfToken rgb("rgb", pxr::TfToken::Immortal);
+static const pxr::TfToken rgba("rgba", pxr::TfToken::Immortal);
+static const pxr::TfToken roughness("roughness", pxr::TfToken::Immortal);
+static const pxr::TfToken sourceColorSpace("sourceColorSpace", pxr::TfToken::Immortal);
+static const pxr::TfToken specularColor("specularColor", pxr::TfToken::Immortal);
+static const pxr::TfToken st("st", pxr::TfToken::Immortal);
+static const pxr::TfToken varname("varname", pxr::TfToken::Immortal);
+
+/* Color space names. */
+static const pxr::TfToken raw("raw", pxr::TfToken::Immortal);
+static const pxr::TfToken RAW("RAW", pxr::TfToken::Immortal);
+
+/* USD shader names. */
+static const pxr::TfToken UsdPreviewSurface("UsdPreviewSurface", pxr::TfToken::Immortal);
+static const pxr::TfToken UsdPrimvarReader_float2("UsdPrimvarReader_float2",
+ pxr::TfToken::Immortal);
+static const pxr::TfToken UsdUVTexture("UsdUVTexture", pxr::TfToken::Immortal);
+} // namespace usdtokens
+
+/* Add a node of the given type at the given location coordinates. */
+static bNode *add_node(
+ const bContext *C, bNodeTree *ntree, const int type, const float locx, const float locy)
+{
+ bNode *new_node = nodeAddStaticNode(C, ntree, type);
+
+ if (new_node) {
+ new_node->locx = locx;
+ new_node->locy = locy;
+ }
+
+ return new_node;
+}
+
+/* Connect the output socket of node 'source' to the input socket of node 'dest'. */
+static void link_nodes(
+ bNodeTree *ntree, bNode *source, const char *sock_out, bNode *dest, const char *sock_in)
+{
+ bNodeSocket *source_socket = nodeFindSocket(source, SOCK_OUT, sock_out);
+
+ if (!source_socket) {
+ std::cerr << "PROGRAMMER ERROR: Couldn't find output socket " << sock_out << std::endl;
+ return;
+ }
+
+ bNodeSocket *dest_socket = nodeFindSocket(dest, SOCK_IN, sock_in);
+
+ if (!dest_socket) {
+ std::cerr << "PROGRAMMER ERROR: Couldn't find input socket " << sock_in << std::endl;
+ return;
+ }
+
+ nodeAddLink(ntree, source, source_socket, dest, dest_socket);
+}
+
+/* Returns true if the given shader may have opacity < 1.0, based
+ * on heuristics. */
+static bool needs_blend(const pxr::UsdShadeShader &usd_shader)
+{
+ if (!usd_shader) {
+ return false;
+ }
+
+ bool needs_blend = false;
+
+ if (pxr::UsdShadeInput opacity_input = usd_shader.GetInput(usdtokens::opacity)) {
+
+ if (opacity_input.HasConnectedSource()) {
+ needs_blend = true;
+ }
+ else {
+ pxr::VtValue val;
+ if (opacity_input.GetAttr().HasAuthoredValue() && opacity_input.GetAttr().Get(&val)) {
+ float opacity = val.Get<float>();
+ needs_blend = opacity < 1.0f;
+ }
+ }
+ }
+
+ return needs_blend;
+}
+
+/* Returns the given shader's opacityThreshold input value, if this input has an
+ * authored value. Otherwise, returns the given default value. */
+static float get_opacity_threshold(const pxr::UsdShadeShader &usd_shader,
+ float default_value = 0.0f)
+{
+ if (!usd_shader) {
+ return default_value;
+ }
+
+ pxr::UsdShadeInput opacity_threshold_input = usd_shader.GetInput(usdtokens::opacityThreshold);
+
+ if (!opacity_threshold_input) {
+ return default_value;
+ }
+
+ pxr::VtValue val;
+ if (opacity_threshold_input.GetAttr().HasAuthoredValue() &&
+ opacity_threshold_input.GetAttr().Get(&val)) {
+ return val.Get<float>();
+ }
+
+ return default_value;
+}
+
+static pxr::TfToken get_source_color_space(const pxr::UsdShadeShader &usd_shader)
+{
+ if (!usd_shader) {
+ return pxr::TfToken();
+ }
+
+ pxr::UsdShadeInput color_space_input = usd_shader.GetInput(usdtokens::sourceColorSpace);
+
+ if (!color_space_input) {
+ return pxr::TfToken();
+ }
+
+ pxr::VtValue color_space_val;
+ if (color_space_input.Get(&color_space_val) && color_space_val.IsHolding<pxr::TfToken>()) {
+ return color_space_val.Get<pxr::TfToken>();
+ }
+
+ return pxr::TfToken();
+}
+
+/* Attempts to return in r_preview_surface the UsdPreviewSurface shader source
+ * of the given material. Returns true if a UsdPreviewSurface source was found
+ * and returns false otherwise. */
+static bool get_usd_preview_surface(const pxr::UsdShadeMaterial &usd_material,
+ pxr::UsdShadeShader &r_preview_surface)
+{
+ if (!usd_material) {
+ return false;
+ }
+
+ if (pxr::UsdShadeShader surf_shader = usd_material.ComputeSurfaceSource()) {
+ /* Check if we have a UsdPreviewSurface shader. */
+ pxr::TfToken shader_id;
+ if (surf_shader.GetShaderId(&shader_id) && shader_id == usdtokens::UsdPreviewSurface) {
+ r_preview_surface = surf_shader;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* Set the Blender material's viewport display color, metallic and roughness
+ * properties from the given USD preview surface shader's inputs. */
+static void set_viewport_material_props(Material *mtl, const pxr::UsdShadeShader &usd_preview)
+{
+ if (!(mtl && usd_preview)) {
+ return;
+ }
+
+ if (pxr::UsdShadeInput diffuse_color_input = usd_preview.GetInput(usdtokens::diffuseColor)) {
+ pxr::VtValue val;
+ if (diffuse_color_input.GetAttr().HasAuthoredValue() &&
+ diffuse_color_input.GetAttr().Get(&val) && val.IsHolding<pxr::GfVec3f>()) {
+ pxr::GfVec3f color = val.UncheckedGet<pxr::GfVec3f>();
+ mtl->r = color[0];
+ mtl->g = color[1];
+ mtl->b = color[2];
+ }
+ }
+
+ if (pxr::UsdShadeInput metallic_input = usd_preview.GetInput(usdtokens::metallic)) {
+ pxr::VtValue val;
+ if (metallic_input.GetAttr().HasAuthoredValue() && metallic_input.GetAttr().Get(&val) &&
+ val.IsHolding<float>()) {
+ mtl->metallic = val.Get<float>();
+ }
+ }
+
+ if (pxr::UsdShadeInput roughness_input = usd_preview.GetInput(usdtokens::roughness)) {
+ pxr::VtValue val;
+ if (roughness_input.GetAttr().HasAuthoredValue() && roughness_input.GetAttr().Get(&val) &&
+ val.IsHolding<float>()) {
+ mtl->roughness = val.Get<float>();
+ }
+ }
+}
+
+namespace blender::io::usd {
+
+namespace {
+
+/* Compute the x- and y-coordinates for placing a new node in an unoccupied region of
+ * the column with the given index. Returns the coordinates in r_locx and r_locy and
+ * updates the column-occupancy information in r_ctx. */
+void compute_node_loc(const int column, float *r_locx, float *r_locy, NodePlacementContext *r_ctx)
+{
+ if (!(r_locx && r_locy && r_ctx)) {
+ return;
+ }
+
+ (*r_locx) = r_ctx->origx - column * r_ctx->horizontal_step;
+
+ if (column >= r_ctx->column_offsets.size()) {
+ r_ctx->column_offsets.push_back(0.0f);
+ }
+
+ (*r_locy) = r_ctx->origy - r_ctx->column_offsets[column];
+
+ /* Record the y-offset of the occupied region in
+ * the column, including padding. */
+ r_ctx->column_offsets[column] += r_ctx->vertical_step + 10.0f;
+}
+
+} // End anonymous namespace.
+
+USDMaterialReader::USDMaterialReader(const USDImportParams &params, Main *bmain)
+ : params_(params), bmain_(bmain)
+{
+}
+
+Material *USDMaterialReader::add_material(const pxr::UsdShadeMaterial &usd_material) const
+{
+ if (!(bmain_ && usd_material)) {
+ return nullptr;
+ }
+
+ std::string mtl_name = usd_material.GetPrim().GetName().GetString();
+
+ /* Create the material. */
+ Material *mtl = BKE_material_add(bmain_, mtl_name.c_str());
+
+ /* Get the UsdPreviewSurface shader source for the material,
+ * if there is one. */
+ pxr::UsdShadeShader usd_preview;
+ if (get_usd_preview_surface(usd_material, usd_preview)) {
+
+ set_viewport_material_props(mtl, usd_preview);
+
+ /* Optionally, create shader nodes to represent a UsdPreviewSurface. */
+ if (params_.import_usd_preview) {
+ import_usd_preview(mtl, usd_preview);
+ }
+ }
+
+ return mtl;
+}
+
+/* Create the Principled BSDF shader node network. */
+void USDMaterialReader::import_usd_preview(Material *mtl,
+ const pxr::UsdShadeShader &usd_shader) const
+{
+ if (!(bmain_ && mtl && usd_shader)) {
+ return;
+ }
+
+ /* Create the Material's node tree containing the principled BSDF
+ * and output shaders. */
+
+ /* Add the node tree. */
+ bNodeTree *ntree = ntreeAddTree(nullptr, "Shader Nodetree", "ShaderNodeTree");
+ mtl->nodetree = ntree;
+ mtl->use_nodes = true;
+
+ /* Create the Principled BSDF shader node. */
+ bNode *principled = add_node(nullptr, ntree, SH_NODE_BSDF_PRINCIPLED, 0.0f, 300.0f);
+
+ if (!principled) {
+ std::cerr << "ERROR: Couldn't create SH_NODE_BSDF_PRINCIPLED node for USD shader "
+ << usd_shader.GetPath() << std::endl;
+ return;
+ }
+
+ /* Create the material output node. */
+ bNode *output = add_node(nullptr, ntree, SH_NODE_OUTPUT_MATERIAL, 300.0f, 300.0f);
+
+ if (!output) {
+ std::cerr << "ERROR: Couldn't create SH_NODE_OUTPUT_MATERIAL node for USD shader "
+ << usd_shader.GetPath() << std::endl;
+ return;
+ }
+
+ /* Connect the Principled BSDF node to the output node. */
+ link_nodes(ntree, principled, "BSDF", output, "Surface");
+
+ /* Recursively create the principled shader input networks. */
+ set_principled_node_inputs(principled, ntree, usd_shader);
+
+ nodeSetActive(ntree, output);
+
+ ntreeUpdateTree(bmain_, ntree);
+
+ /* Optionally, set the material blend mode. */
+
+ if (params_.set_material_blend) {
+ if (needs_blend(usd_shader)) {
+ float opacity_threshold = get_opacity_threshold(usd_shader, 0.0f);
+ if (opacity_threshold > 0.0f) {
+ mtl->blend_method = MA_BM_CLIP;
+ mtl->alpha_threshold = opacity_threshold;
+ }
+ else {
+ mtl->blend_method = MA_BM_BLEND;
+ }
+ }
+ }
+}
+
+void USDMaterialReader::set_principled_node_inputs(bNode *principled,
+ bNodeTree *ntree,
+ const pxr::UsdShadeShader &usd_shader) const
+{
+ /* The context struct keeps track of the locations for adding
+ * input nodes. */
+ NodePlacementContext context(0.0f, 300.0);
+
+ /* The column index (from right to left relative to the principled
+ * node) where we're adding the nodes. */
+ int column = 0;
+
+ /* Recursively set the principled shader inputs. */
+
+ if (pxr::UsdShadeInput diffuse_input = usd_shader.GetInput(usdtokens::diffuseColor)) {
+ set_node_input(diffuse_input, principled, "Base Color", ntree, column, &context);
+ }
+
+ if (pxr::UsdShadeInput emissive_input = usd_shader.GetInput(usdtokens::emissiveColor)) {
+ set_node_input(emissive_input, principled, "Emission", ntree, column, &context);
+ }
+
+ if (pxr::UsdShadeInput specular_input = usd_shader.GetInput(usdtokens::specularColor)) {
+ set_node_input(specular_input, principled, "Specular", ntree, column, &context);
+ }
+
+ if (pxr::UsdShadeInput metallic_input = usd_shader.GetInput(usdtokens::metallic)) {
+ ;
+ set_node_input(metallic_input, principled, "Metallic", ntree, column, &context);
+ }
+
+ if (pxr::UsdShadeInput roughness_input = usd_shader.GetInput(usdtokens::roughness)) {
+ set_node_input(roughness_input, principled, "Roughness", ntree, column, &context);
+ }
+
+ if (pxr::UsdShadeInput clearcoat_input = usd_shader.GetInput(usdtokens::clearcoat)) {
+ set_node_input(clearcoat_input, principled, "Clearcoat", ntree, column, &context);
+ }
+
+ if (pxr::UsdShadeInput clearcoat_roughness_input = usd_shader.GetInput(
+ usdtokens::clearcoatRoughness)) {
+ set_node_input(
+ clearcoat_roughness_input, principled, "Clearcoat Roughness", ntree, column, &context);
+ }
+
+ if (pxr::UsdShadeInput opacity_input = usd_shader.GetInput(usdtokens::opacity)) {
+ set_node_input(opacity_input, principled, "Alpha", ntree, column, &context);
+ }
+
+ if (pxr::UsdShadeInput ior_input = usd_shader.GetInput(usdtokens::ior)) {
+ set_node_input(ior_input, principled, "IOR", ntree, column, &context);
+ }
+
+ if (pxr::UsdShadeInput normal_input = usd_shader.GetInput(usdtokens::normal)) {
+ set_node_input(normal_input, principled, "Normal", ntree, column, &context);
+ }
+}
+
+/* Convert the given USD shader input to an input on the given Blender node. */
+void USDMaterialReader::set_node_input(const pxr::UsdShadeInput &usd_input,
+ bNode *dest_node,
+ const char *dest_socket_name,
+ bNodeTree *ntree,
+ const int column,
+ NodePlacementContext *r_ctx) const
+{
+ if (!(usd_input && dest_node && r_ctx)) {
+ return;
+ }
+
+ if (usd_input.HasConnectedSource()) {
+ /* The USD shader input has a connected source shader. Follow the connection
+ * and attempt to convert the connected USD shader to a Blender node. */
+ follow_connection(usd_input, dest_node, dest_socket_name, ntree, column, r_ctx);
+ }
+ else {
+ /* Set the destination node socket value from the USD shader input value. */
+
+ bNodeSocket *sock = nodeFindSocket(dest_node, SOCK_IN, dest_socket_name);
+ if (!sock) {
+ std::cerr << "ERROR: couldn't get destination node socket " << dest_socket_name << std::endl;
+ return;
+ }
+
+ pxr::VtValue val;
+ if (!usd_input.Get(&val)) {
+ std::cerr << "ERROR: couldn't get value for usd shader input "
+ << usd_input.GetPrim().GetPath() << std::endl;
+ return;
+ }
+
+ switch (sock->type) {
+ case SOCK_FLOAT:
+ if (val.IsHolding<float>()) {
+ ((bNodeSocketValueFloat *)sock->default_value)->value = val.UncheckedGet<float>();
+ }
+ else if (val.IsHolding<pxr::GfVec3f>()) {
+ pxr::GfVec3f v3f = val.UncheckedGet<pxr::GfVec3f>();
+ float average = (v3f[0] + v3f[1] + v3f[2]) / 3.0f;
+ ((bNodeSocketValueFloat *)sock->default_value)->value = average;
+ }
+ break;
+ case SOCK_RGBA:
+ if (val.IsHolding<pxr::GfVec3f>()) {
+ pxr::GfVec3f v3f = val.UncheckedGet<pxr::GfVec3f>();
+ copy_v3_v3(((bNodeSocketValueRGBA *)sock->default_value)->value, v3f.data());
+ }
+ break;
+ case SOCK_VECTOR:
+ if (val.IsHolding<pxr::GfVec3f>()) {
+ pxr::GfVec3f v3f = val.UncheckedGet<pxr::GfVec3f>();
+ copy_v3_v3(((bNodeSocketValueVector *)sock->default_value)->value, v3f.data());
+ }
+ else if (val.IsHolding<pxr::GfVec2f>()) {
+ pxr::GfVec2f v2f = val.UncheckedGet<pxr::GfVec2f>();
+ copy_v2_v2(((bNodeSocketValueVector *)sock->default_value)->value, v2f.data());
+ }
+ break;
+ default:
+ std::cerr << "WARNING: unexpected type " << sock->idname << " for destination node socket "
+ << dest_socket_name << std::endl;
+ break;
+ }
+ }
+}
+
+/* Follow the connected source of the USD input to create corresponding inputs
+ * for the given Blender node. */
+void USDMaterialReader::follow_connection(const pxr::UsdShadeInput &usd_input,
+ bNode *dest_node,
+ const char *dest_socket_name,
+ bNodeTree *ntree,
+ int column,
+ NodePlacementContext *r_ctx) const
+{
+ if (!(usd_input && dest_node && dest_socket_name && ntree && r_ctx)) {
+ return;
+ }
+
+ pxr::UsdShadeConnectableAPI source;
+ pxr::TfToken source_name;
+ pxr::UsdShadeAttributeType source_type;
+
+ usd_input.GetConnectedSource(&source, &source_name, &source_type);
+
+ if (!(source && source.GetPrim().IsA<pxr::UsdShadeShader>())) {
+ return;
+ }
+
+ pxr::UsdShadeShader source_shader(source.GetPrim());
+
+ if (!source_shader) {
+ return;
+ }
+
+ pxr::TfToken shader_id;
+ if (!source_shader.GetShaderId(&shader_id)) {
+ std::cerr << "ERROR: couldn't get shader id for source shader "
+ << source_shader.GetPrim().GetPath() << std::endl;
+ return;
+ }
+
+ /* For now, only convert UsdUVTexture and UsdPrimvarReader_float2 inputs. */
+ if (shader_id == usdtokens::UsdUVTexture) {
+
+ if (strcmp(dest_socket_name, "Normal") == 0) {
+
+ /* The normal texture input requires creating a normal map node. */
+ float locx = 0.0f;
+ float locy = 0.0f;
+ compute_node_loc(column + 1, &locx, &locy, r_ctx);
+
+ bNode *normal_map = add_node(nullptr, ntree, SH_NODE_NORMAL_MAP, locx, locy);
+
+ /* Currently, the Normal Map node has Tangent Space as the default,
+ * which is what we need, so we don't need to explicitly set it. */
+
+ /* Connect the Normal Map to the Normal input. */
+ link_nodes(ntree, normal_map, "Normal", dest_node, "Normal");
+
+ /* Now, create the Texture Image node input to the Normal Map "Color" input. */
+ convert_usd_uv_texture(
+ source_shader, source_name, normal_map, "Color", ntree, column + 2, r_ctx);
+ }
+ else {
+ convert_usd_uv_texture(
+ source_shader, source_name, dest_node, dest_socket_name, ntree, column + 1, r_ctx);
+ }
+ }
+ else if (shader_id == usdtokens::UsdPrimvarReader_float2) {
+ convert_usd_primvar_reader_float2(
+ source_shader, source_name, dest_node, dest_socket_name, ntree, column + 1, r_ctx);
+ }
+}
+
+void USDMaterialReader::convert_usd_uv_texture(const pxr::UsdShadeShader &usd_shader,
+ const pxr::TfToken &usd_source_name,
+ bNode *dest_node,
+ const char *dest_socket_name,
+ bNodeTree *ntree,
+ const int column,
+ NodePlacementContext *r_ctx) const
+{
+ if (!usd_shader || !dest_node || !ntree || !dest_socket_name || !bmain_ || !r_ctx) {
+ return;
+ }
+
+ float locx = 0.0f;
+ float locy = 0.0f;
+ compute_node_loc(column, &locx, &locy, r_ctx);
+
+ /* Create the Texture Image node. */
+ bNode *tex_image = add_node(nullptr, ntree, SH_NODE_TEX_IMAGE, locx, locy);
+
+ if (!tex_image) {
+ std::cerr << "ERROR: Couldn't create SH_NODE_TEX_IMAGE for node input " << dest_socket_name
+ << std::endl;
+ return;
+ }
+
+ /* Load the texture image. */
+ load_tex_image(usd_shader, tex_image);
+
+ /* Connect to destination node input. */
+
+ /* Get the source socket name. */
+ std::string source_socket_name = usd_source_name == usdtokens::a ? "Alpha" : "Color";
+
+ link_nodes(ntree, tex_image, source_socket_name.c_str(), dest_node, dest_socket_name);
+
+ /* Connect the texture image node "Vector" input. */
+ if (pxr::UsdShadeInput st_input = usd_shader.GetInput(usdtokens::st)) {
+ set_node_input(st_input, tex_image, "Vector", ntree, column, r_ctx);
+ }
+}
+
+/* Load the texture image node's texture from the path given by the USD shader's
+ * file input value. */
+void USDMaterialReader::load_tex_image(const pxr::UsdShadeShader &usd_shader,
+ bNode *tex_image) const
+{
+ if (!(usd_shader && tex_image && tex_image->type == SH_NODE_TEX_IMAGE)) {
+ return;
+ }
+
+ /* Try to load the texture image. */
+ pxr::UsdShadeInput file_input = usd_shader.GetInput(usdtokens::file);
+
+ if (!file_input) {
+ std::cerr << "WARNING: Couldn't get file input for USD shader " << usd_shader.GetPath()
+ << std::endl;
+ return;
+ }
+
+ pxr::VtValue file_val;
+ if (!file_input.Get(&file_val) || !file_val.IsHolding<pxr::SdfAssetPath>()) {
+ std::cerr << "WARNING: Couldn't get file input value for USD shader " << usd_shader.GetPath()
+ << std::endl;
+ return;
+ }
+
+ const pxr::SdfAssetPath &asset_path = file_val.Get<pxr::SdfAssetPath>();
+ std::string file_path = asset_path.GetResolvedPath();
+ if (file_path.empty()) {
+ std::cerr << "WARNING: Couldn't resolve image asset '" << asset_path
+ << "' for Texture Image node." << std::endl;
+ return;
+ }
+
+ const char *im_file = file_path.c_str();
+ Image *image = BKE_image_load_exists(bmain_, im_file);
+ if (!image) {
+ std::cerr << "WARNING: Couldn't open image file '" << im_file << "' for Texture Image node."
+ << std::endl;
+ return;
+ }
+
+ tex_image->id = &image->id;
+
+ /* Set texture color space.
+ * TODO(makowalski): For now, just checking for RAW color space,
+ * assuming sRGB otherwise, but more complex logic might be
+ * required if the color space is "auto". */
+
+ pxr::TfToken color_space = get_source_color_space(usd_shader);
+
+ if (color_space.IsEmpty()) {
+ color_space = file_input.GetAttr().GetColorSpace();
+ }
+
+ if (color_space == usdtokens::RAW || color_space == usdtokens::raw) {
+ STRNCPY(image->colorspace_settings.name, "Raw");
+ }
+}
+
+/* This function creates a Blender UV Map node, under the simplifying assumption that
+ * UsdPrimvarReader_float2 shaders output UV coordinates.
+ * TODO(makowalski): investigate supporting conversion to other Blender node types
+ * (e.g., Attribute Nodes) if needed. */
+void USDMaterialReader::convert_usd_primvar_reader_float2(
+ const pxr::UsdShadeShader &usd_shader,
+ const pxr::TfToken & /* usd_source_name */,
+ bNode *dest_node,
+ const char *dest_socket_name,
+ bNodeTree *ntree,
+ const int column,
+ NodePlacementContext *r_ctx) const
+{
+ if (!usd_shader || !dest_node || !ntree || !dest_socket_name || !bmain_ || !r_ctx) {
+ return;
+ }
+
+ float locx = 0.0f;
+ float locy = 0.0f;
+ compute_node_loc(column, &locx, &locy, r_ctx);
+
+ /* Create the UV Map node. */
+ bNode *uv_map = add_node(nullptr, ntree, SH_NODE_UVMAP, locx, locy);
+
+ if (!uv_map) {
+ std::cerr << "ERROR: Couldn't create SH_NODE_UVMAP for node input " << dest_socket_name
+ << std::endl;
+ return;
+ }
+
+ /* Set the texmap name. */
+ pxr::UsdShadeInput varname_input = usd_shader.GetInput(usdtokens::varname);
+ if (varname_input) {
+ pxr::VtValue varname_val;
+ if (varname_input.Get(&varname_val) && varname_val.IsHolding<pxr::TfToken>()) {
+ std::string varname = varname_val.Get<pxr::TfToken>().GetString();
+ if (!varname.empty()) {
+ NodeShaderUVMap *storage = (NodeShaderUVMap *)uv_map->storage;
+ BLI_strncpy(storage->uv_map, varname.c_str(), sizeof(storage->uv_map));
+ }
+ }
+ }
+
+ /* Connect to destination node input. */
+ link_nodes(ntree, uv_map, "UV", dest_node, dest_socket_name);
+}
+
+} // namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_reader_material.h b/source/blender/io/usd/intern/usd_reader_material.h
new file mode 100644
index 00000000000..a17504bd590
--- /dev/null
+++ b/source/blender/io/usd/intern/usd_reader_material.h
@@ -0,0 +1,132 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2021 NVIDIA Corporation.
+ * All rights reserved.
+ */
+#pragma once
+
+#include "usd.h"
+
+#include <pxr/usd/usdShade/material.h>
+
+struct Main;
+struct Material;
+struct bNode;
+struct bNodeTree;
+
+namespace blender::io::usd {
+
+/* Helper struct used when arranging nodes in columns, keeping track the
+ * occupancy information for a given column. I.e., for column n,
+ * column_offsets[n] is the y-offset (from top to bottom) of the occupied
+ * region in that column. */
+struct NodePlacementContext {
+ float origx;
+ float origy;
+ std::vector<float> column_offsets;
+ const float horizontal_step;
+ const float vertical_step;
+
+ NodePlacementContext(float in_origx,
+ float in_origy,
+ float in_horizontal_step = 300.0f,
+ float in_vertical_step = 300.0f)
+ : origx(in_origx),
+ origy(in_origy),
+ column_offsets(64, 0.0f),
+ horizontal_step(in_horizontal_step),
+ vertical_step(in_vertical_step)
+ {
+ }
+};
+
+/* Converts USD materials to Blender representation. */
+
+/**
+ By default, the #USDMaterialReader creates a Blender material with
+ * the same name as the USD material. If the USD material has a
+ * #UsdPreviewSurface source, the Blender material's viewport display
+ * color, roughness and metallic properties are set to the corresponding
+ * #UsdPreoviewSurface inputs.
+ *
+ * If the Import USD Preview option is enabled, the current implementation
+ * converts #UsdPreviewSurface to Blender nodes as follows:
+ *
+ * - #UsdPreviewSurface -> Principled BSDF
+ * - #UsdUVTexture -> Texture Image + Normal Map
+ * - UsdPrimvarReader_float2 -> UV Map
+ *
+ * Limitations: arbitrary primvar readers or UsdTransform2d not yet
+ * supported. For #UsdUVTexture, only the file, st and #sourceColorSpace
+ * inputs are handled.
+ *
+ * TODO(makowalski): Investigate adding support for converting additional
+ * shaders and inputs. Supporting certain types of inputs, such as texture
+ * scale and bias, will probably require creating Blender Group nodes with
+ * the corresponding inputs.
+ */
+class USDMaterialReader {
+ protected:
+ USDImportParams params_;
+
+ Main *bmain_;
+
+ public:
+ USDMaterialReader(const USDImportParams &params, Main *bmain);
+
+ Material *add_material(const pxr::UsdShadeMaterial &usd_material) const;
+
+ protected:
+ void import_usd_preview(Material *mtl, const pxr::UsdShadeShader &usd_shader) const;
+
+ void set_principled_node_inputs(bNode *principled_node,
+ bNodeTree *ntree,
+ const pxr::UsdShadeShader &usd_shader) const;
+
+ void set_node_input(const pxr::UsdShadeInput &usd_input,
+ bNode *dest_node,
+ const char *dest_socket_name,
+ bNodeTree *ntree,
+ int column,
+ NodePlacementContext *r_ctx) const;
+
+ void follow_connection(const pxr::UsdShadeInput &usd_input,
+ bNode *dest_node,
+ const char *dest_socket_name,
+ bNodeTree *ntree,
+ int column,
+ NodePlacementContext *r_ctx) const;
+
+ void convert_usd_uv_texture(const pxr::UsdShadeShader &usd_shader,
+ const pxr::TfToken &usd_source_name,
+ bNode *dest_node,
+ const char *dest_socket_name,
+ bNodeTree *ntree,
+ int column,
+ NodePlacementContext *r_ctx) const;
+
+ void load_tex_image(const pxr::UsdShadeShader &usd_shader, bNode *tex_image) const;
+
+ void convert_usd_primvar_reader_float2(const pxr::UsdShadeShader &usd_shader,
+ const pxr::TfToken &usd_source_name,
+ bNode *dest_node,
+ const char *dest_socket_name,
+ bNodeTree *ntree,
+ int column,
+ NodePlacementContext *r_ctx) const;
+};
+
+} // namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_reader_mesh.cc b/source/blender/io/usd/intern/usd_reader_mesh.cc
new file mode 100644
index 00000000000..9c75bc8afae
--- /dev/null
+++ b/source/blender/io/usd/intern/usd_reader_mesh.cc
@@ -0,0 +1,853 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Adapted from the Blender Alembic importer implementation.
+ *
+ * Modifications Copyright (C) 2021 Tangent Animation and
+ * NVIDIA Corporation. All rights reserved.
+ */
+
+#include "usd_reader_mesh.h"
+#include "usd_reader_material.h"
+
+#include "BKE_customdata.h"
+#include "BKE_main.h"
+#include "BKE_material.h"
+#include "BKE_mesh.h"
+#include "BKE_object.h"
+
+#include "BLI_math.h"
+#include "BLI_math_geom.h"
+#include "BLI_string.h"
+
+#include "DNA_customdata_types.h"
+#include "DNA_material_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include <pxr/base/vt/array.h>
+#include <pxr/base/vt/types.h>
+#include <pxr/base/vt/value.h>
+#include <pxr/usd/sdf/types.h>
+#include <pxr/usd/usdGeom/mesh.h>
+#include <pxr/usd/usdGeom/subset.h>
+#include <pxr/usd/usdShade/materialBindingAPI.h>
+
+#include <iostream>
+
+namespace usdtokens {
+/* Materials */
+static const pxr::TfToken st("st", pxr::TfToken::Immortal);
+static const pxr::TfToken UVMap("UVMap", pxr::TfToken::Immortal);
+static const pxr::TfToken Cd("Cd", pxr::TfToken::Immortal);
+static const pxr::TfToken displayColor("displayColor", pxr::TfToken::Immortal);
+static const pxr::TfToken normalsPrimvar("normals", pxr::TfToken::Immortal);
+} // namespace usdtokens
+
+namespace utils {
+/* Very similar to #blender::io::alembic::utils. */
+static void build_mat_map(const Main *bmain, std::map<std::string, Material *> *r_mat_map)
+{
+ if (r_mat_map == nullptr) {
+ return;
+ }
+
+ Material *material = static_cast<Material *>(bmain->materials.first);
+
+ for (; material; material = static_cast<Material *>(material->id.next)) {
+ /* We have to do this because the stored material name is coming directly from USD. */
+ (*r_mat_map)[pxr::TfMakeValidIdentifier(material->id.name + 2)] = material;
+ }
+}
+
+static void assign_materials(Main *bmain,
+ Object *ob,
+ const std::map<pxr::SdfPath, int> &mat_index_map,
+ const USDImportParams &params,
+ pxr::UsdStageRefPtr stage)
+{
+ if (!(stage && bmain && ob)) {
+ return;
+ }
+
+ bool can_assign = true;
+ std::map<pxr::SdfPath, int>::const_iterator it = mat_index_map.begin();
+
+ int matcount = 0;
+ for (; it != mat_index_map.end(); ++it, matcount++) {
+ if (!BKE_object_material_slot_add(bmain, ob)) {
+ can_assign = false;
+ break;
+ }
+ }
+
+ if (!can_assign) {
+ return;
+ }
+
+ /* TODO(kevin): use global map? */
+ std::map<std::string, Material *> mat_map;
+ build_mat_map(bmain, &mat_map);
+
+ blender::io::usd::USDMaterialReader mat_reader(params, bmain);
+
+ for (it = mat_index_map.begin(); it != mat_index_map.end(); ++it) {
+ std::string mat_name = it->first.GetName();
+
+ std::map<std::string, Material *>::iterator mat_iter = mat_map.find(mat_name);
+
+ Material *assigned_mat = nullptr;
+
+ if (mat_iter == mat_map.end()) {
+ /* Blender material doesn't exist, so create it now. */
+
+ /* Look up the USD material. */
+ pxr::UsdPrim prim = stage->GetPrimAtPath(it->first);
+ pxr::UsdShadeMaterial usd_mat(prim);
+
+ if (!usd_mat) {
+ std::cout << "WARNING: Couldn't construct USD material from prim " << it->first
+ << std::endl;
+ continue;
+ }
+
+ /* Add the Blender material. */
+ assigned_mat = mat_reader.add_material(usd_mat);
+
+ if (!assigned_mat) {
+ std::cout << "WARNING: Couldn't create Blender material from USD material " << it->first
+ << std::endl;
+ continue;
+ }
+
+ mat_map[mat_name] = assigned_mat;
+ }
+ else {
+ /* We found an existing Blender material. */
+ assigned_mat = mat_iter->second;
+ }
+
+ if (assigned_mat) {
+ BKE_object_material_assign(bmain, ob, assigned_mat, it->second, BKE_MAT_ASSIGN_OBDATA);
+ }
+ else {
+ /* This shouldn't happen. */
+ std::cout << "WARNING: Couldn't assign material " << mat_name << std::endl;
+ }
+ }
+}
+
+} // namespace utils
+
+static void *add_customdata_cb(Mesh *mesh, const char *name, const int data_type)
+{
+ CustomDataType cd_data_type = static_cast<CustomDataType>(data_type);
+ void *cd_ptr;
+ CustomData *loopdata;
+ int numloops;
+
+ /* unsupported custom data type -- don't do anything. */
+ if (!ELEM(cd_data_type, CD_MLOOPUV, CD_MLOOPCOL)) {
+ return nullptr;
+ }
+
+ loopdata = &mesh->ldata;
+ cd_ptr = CustomData_get_layer_named(loopdata, cd_data_type, name);
+ if (cd_ptr != nullptr) {
+ /* layer already exists, so just return it. */
+ return cd_ptr;
+ }
+
+ /* Create a new layer. */
+ numloops = mesh->totloop;
+ cd_ptr = CustomData_add_layer_named(loopdata, cd_data_type, CD_DEFAULT, nullptr, numloops, name);
+ return cd_ptr;
+}
+
+namespace blender::io::usd {
+
+USDMeshReader::USDMeshReader(const pxr::UsdPrim &prim,
+ const USDImportParams &import_params,
+ const ImportSettings &settings)
+ : USDGeomReader(prim, import_params, settings),
+ mesh_prim_(prim),
+ is_left_handed_(false),
+ has_uvs_(false),
+ is_time_varying_(false),
+ is_initial_load_(false)
+{
+}
+
+void USDMeshReader::create_object(Main *bmain, const double /* motionSampleTime */)
+{
+ Mesh *mesh = BKE_mesh_add(bmain, name_.c_str());
+
+ object_ = BKE_object_add_only_object(bmain, OB_MESH, name_.c_str());
+ object_->data = mesh;
+}
+
+void USDMeshReader::read_object_data(Main *bmain, const double motionSampleTime)
+{
+ Mesh *mesh = (Mesh *)object_->data;
+
+ is_initial_load_ = true;
+ Mesh *read_mesh = this->read_mesh(
+ mesh, motionSampleTime, import_params_.mesh_read_flag, nullptr);
+
+ is_initial_load_ = false;
+ if (read_mesh != mesh) {
+ /* FIXME: after 2.80; `mesh->flag` isn't copied by #BKE_mesh_nomain_to_mesh() */
+ /* read_mesh can be freed by BKE_mesh_nomain_to_mesh(), so get the flag before that happens. */
+ short autosmooth = (read_mesh->flag & ME_AUTOSMOOTH);
+ BKE_mesh_nomain_to_mesh(read_mesh, mesh, object_, &CD_MASK_MESH, true);
+ mesh->flag |= autosmooth;
+ }
+
+ readFaceSetsSample(bmain, mesh, motionSampleTime);
+
+ if (mesh_prim_.GetPointsAttr().ValueMightBeTimeVarying()) {
+ is_time_varying_ = true;
+ }
+
+ if (is_time_varying_) {
+ add_cache_modifier();
+ }
+
+ if (import_params_.import_subdiv) {
+ pxr::TfToken subdivScheme;
+ mesh_prim_.GetSubdivisionSchemeAttr().Get(&subdivScheme, motionSampleTime);
+
+ if (subdivScheme == pxr::UsdGeomTokens->catmullClark) {
+ add_subdiv_modifier();
+ }
+ }
+
+ USDXformReader::read_object_data(bmain, motionSampleTime);
+}
+
+bool USDMeshReader::valid() const
+{
+ return static_cast<bool>(mesh_prim_);
+}
+
+bool USDMeshReader::topology_changed(Mesh *existing_mesh, const double motionSampleTime)
+{
+ /* TODO(makowalski): Is it the best strategy to cache the mesh
+ * geometry in this function? This needs to be revisited. */
+
+ mesh_prim_.GetFaceVertexIndicesAttr().Get(&face_indices_, motionSampleTime);
+ mesh_prim_.GetFaceVertexCountsAttr().Get(&face_counts_, motionSampleTime);
+ mesh_prim_.GetPointsAttr().Get(&positions_, motionSampleTime);
+
+ /* TODO(makowalski): Reading normals probably doesn't belong in this function,
+ * as this is not required to determine if the topology has changed. */
+
+ /* If 'normals' and 'primvars:normals' are both specified, the latter has precedence. */
+ pxr::UsdGeomPrimvar primvar = mesh_prim_.GetPrimvar(usdtokens::normalsPrimvar);
+ if (primvar.HasValue()) {
+ primvar.ComputeFlattened(&normals_, motionSampleTime);
+ normal_interpolation_ = primvar.GetInterpolation();
+ }
+ else {
+ mesh_prim_.GetNormalsAttr().Get(&normals_, motionSampleTime);
+ normal_interpolation_ = mesh_prim_.GetNormalsInterpolation();
+ }
+
+ return positions_.size() != existing_mesh->totvert ||
+ face_counts_.size() != existing_mesh->totpoly ||
+ face_indices_.size() != existing_mesh->totloop;
+}
+
+void USDMeshReader::read_mpolys(Mesh *mesh)
+{
+ MPoly *mpolys = mesh->mpoly;
+ MLoop *mloops = mesh->mloop;
+
+ int loop_index = 0;
+
+ for (int i = 0; i < face_counts_.size(); i++) {
+ const int face_size = face_counts_[i];
+
+ MPoly &poly = mpolys[i];
+ poly.loopstart = loop_index;
+ poly.totloop = face_size;
+ poly.mat_nr = 0;
+
+ /* Polygons are always assumed to be smooth-shaded. If the mesh should be flat-shaded,
+ * this is encoded in custom loop normals. */
+ poly.flag |= ME_SMOOTH;
+
+ if (is_left_handed_) {
+ int loop_end_index = loop_index + (face_size - 1);
+ for (int f = 0; f < face_size; ++f, ++loop_index) {
+ mloops[loop_index].v = face_indices_[loop_end_index - f];
+ }
+ }
+ else {
+ for (int f = 0; f < face_size; ++f, ++loop_index) {
+ mloops[loop_index].v = face_indices_[loop_index];
+ }
+ }
+ }
+
+ BKE_mesh_calc_edges(mesh, false, false);
+}
+
+void USDMeshReader::read_uvs(Mesh *mesh, const double motionSampleTime, const bool load_uvs)
+{
+ unsigned int loop_index = 0;
+ unsigned int rev_loop_index = 0;
+ unsigned int uv_index = 0;
+
+ const CustomData *ldata = &mesh->ldata;
+
+ struct UVSample {
+ pxr::VtVec2fArray uvs;
+ pxr::TfToken interpolation;
+ };
+
+ std::vector<UVSample> uv_primvars(ldata->totlayer);
+
+ if (has_uvs_) {
+ for (int layer_idx = 0; layer_idx < ldata->totlayer; layer_idx++) {
+ const CustomDataLayer *layer = &ldata->layers[layer_idx];
+ std::string layer_name = std::string(layer->name);
+ if (layer->type != CD_MLOOPUV) {
+ continue;
+ }
+
+ pxr::TfToken uv_token;
+
+ /* If first time seeing uv token, store in map of `<layer->uid, TfToken>`. */
+ if (uv_token_map_.find(layer_name) == uv_token_map_.end()) {
+ uv_token = pxr::TfToken(layer_name);
+ uv_token_map_.insert(std::make_pair(layer_name, uv_token));
+ }
+ else {
+ uv_token = uv_token_map_.at(layer_name);
+ }
+
+ /* Early out if no token found, this should never happen */
+ if (uv_token.IsEmpty()) {
+ continue;
+ }
+ /* Early out if not first load and UVs aren't animated. */
+ if (!load_uvs && primvar_varying_map_.find(uv_token) != primvar_varying_map_.end() &&
+ !primvar_varying_map_.at(uv_token)) {
+ continue;
+ }
+
+ /* Early out if mesh doesn't have primvar. */
+ if (!mesh_prim_.HasPrimvar(uv_token)) {
+ continue;
+ }
+
+ if (pxr::UsdGeomPrimvar uv_primvar = mesh_prim_.GetPrimvar(uv_token)) {
+ uv_primvar.ComputeFlattened(&uv_primvars[layer_idx].uvs, motionSampleTime);
+ uv_primvars[layer_idx].interpolation = uv_primvar.GetInterpolation();
+ }
+ }
+ }
+
+ for (int i = 0; i < face_counts_.size(); i++) {
+ const int face_size = face_counts_[i];
+
+ rev_loop_index = loop_index + (face_size - 1);
+
+ for (int f = 0; f < face_size; f++, loop_index++, rev_loop_index--) {
+
+ for (int layer_idx = 0; layer_idx < ldata->totlayer; layer_idx++) {
+ const CustomDataLayer *layer = &ldata->layers[layer_idx];
+ if (layer->type != CD_MLOOPUV) {
+ continue;
+ }
+
+ /* Early out if mismatched layer sizes. */
+ if (layer_idx > uv_primvars.size()) {
+ continue;
+ }
+
+ /* Early out if no uvs loaded. */
+ if (uv_primvars[layer_idx].uvs.empty()) {
+ continue;
+ }
+
+ const UVSample &sample = uv_primvars[layer_idx];
+
+ if (!(sample.interpolation == pxr::UsdGeomTokens->faceVarying ||
+ sample.interpolation == pxr::UsdGeomTokens->vertex)) {
+ std::cerr << "WARNING: unexpected interpolation type " << sample.interpolation
+ << " for uv " << layer->name << std::endl;
+ continue;
+ }
+
+ /* For Vertex interpolation, use the vertex index. */
+ int usd_uv_index = sample.interpolation == pxr::UsdGeomTokens->vertex ?
+ mesh->mloop[loop_index].v :
+ loop_index;
+
+ if (usd_uv_index >= sample.uvs.size()) {
+ std::cerr << "WARNING: out of bounds uv index " << usd_uv_index << " for uv "
+ << layer->name << " of size " << sample.uvs.size() << std::endl;
+ continue;
+ }
+
+ MLoopUV *mloopuv = static_cast<MLoopUV *>(layer->data);
+ if (is_left_handed_) {
+ uv_index = rev_loop_index;
+ }
+ else {
+ uv_index = loop_index;
+ }
+ mloopuv[uv_index].uv[0] = sample.uvs[usd_uv_index][0];
+ mloopuv[uv_index].uv[1] = sample.uvs[usd_uv_index][1];
+ }
+ }
+ }
+}
+
+void USDMeshReader::read_colors(Mesh *mesh, const double motionSampleTime)
+{
+ if (!(mesh && mesh_prim_ && mesh->totloop > 0)) {
+ return;
+ }
+
+ /* Early out if we read the display color before and if this attribute isn't animated. */
+ if (primvar_varying_map_.find(usdtokens::displayColor) != primvar_varying_map_.end() &&
+ !primvar_varying_map_.at(usdtokens::displayColor)) {
+ return;
+ }
+
+ pxr::UsdGeomPrimvar color_primvar = mesh_prim_.GetDisplayColorPrimvar();
+
+ if (!color_primvar.HasValue()) {
+ return;
+ }
+
+ pxr::TfToken interp = color_primvar.GetInterpolation();
+
+ if (interp == pxr::UsdGeomTokens->varying) {
+ std::cerr << "WARNING: Unsupported varying interpolation for display colors\n" << std::endl;
+ return;
+ }
+
+ if (primvar_varying_map_.find(usdtokens::displayColor) == primvar_varying_map_.end()) {
+ bool might_be_time_varying = color_primvar.ValueMightBeTimeVarying();
+ primvar_varying_map_.insert(std::make_pair(usdtokens::displayColor, might_be_time_varying));
+ if (might_be_time_varying) {
+ is_time_varying_ = true;
+ }
+ }
+
+ pxr::VtArray<pxr::GfVec3f> display_colors;
+
+ if (!color_primvar.ComputeFlattened(&display_colors, motionSampleTime)) {
+ std::cerr << "WARNING: Couldn't compute display colors\n" << std::endl;
+ return;
+ }
+
+ if ((interp == pxr::UsdGeomTokens->faceVarying && display_colors.size() != mesh->totloop) ||
+ (interp == pxr::UsdGeomTokens->vertex && display_colors.size() != mesh->totvert) ||
+ (interp == pxr::UsdGeomTokens->constant && display_colors.size() != 1) ||
+ (interp == pxr::UsdGeomTokens->uniform && display_colors.size() != mesh->totpoly)) {
+ std::cerr << "WARNING: display colors count mismatch\n" << std::endl;
+ return;
+ }
+
+ void *cd_ptr = add_customdata_cb(mesh, "displayColors", CD_MLOOPCOL);
+
+ if (!cd_ptr) {
+ std::cerr << "WARNING: Couldn't add displayColors custom data.\n";
+ return;
+ }
+
+ MLoopCol *colors = static_cast<MLoopCol *>(cd_ptr);
+
+ mesh->mloopcol = colors;
+
+ MPoly *poly = mesh->mpoly;
+
+ for (int i = 0, e = mesh->totpoly; i < e; ++i, ++poly) {
+ for (int j = 0; j < poly->totloop; ++j) {
+ int loop_index = poly->loopstart + j;
+
+ /* Default for constant varying interpolation. */
+ int usd_index = 0;
+
+ if (interp == pxr::UsdGeomTokens->vertex) {
+ usd_index = mesh->mloop[loop_index].v;
+ }
+ else if (interp == pxr::UsdGeomTokens->faceVarying) {
+ usd_index = poly->loopstart;
+ if (is_left_handed_) {
+ usd_index += poly->totloop - 1 - j;
+ }
+ else {
+ usd_index += j;
+ }
+ }
+ else if (interp == pxr::UsdGeomTokens->uniform) {
+ /* Uniform varying uses the poly index. */
+ usd_index = i;
+ }
+
+ if (usd_index >= display_colors.size()) {
+ continue;
+ }
+
+ colors[loop_index].r = unit_float_to_uchar_clamp(display_colors[usd_index][0]);
+ colors[loop_index].g = unit_float_to_uchar_clamp(display_colors[usd_index][1]);
+ colors[loop_index].b = unit_float_to_uchar_clamp(display_colors[usd_index][2]);
+ colors[loop_index].a = unit_float_to_uchar_clamp(1.0);
+ }
+ }
+}
+
+void USDMeshReader::process_normals_vertex_varying(Mesh *mesh)
+{
+ if (!mesh) {
+ return;
+ }
+
+ if (normals_.empty()) {
+ BKE_mesh_calc_normals(mesh);
+ return;
+ }
+
+ if (normals_.size() != mesh->totvert) {
+ std::cerr << "WARNING: vertex varying normals count mismatch for mesh " << prim_path_
+ << std::endl;
+ BKE_mesh_calc_normals(mesh);
+ return;
+ }
+
+ for (int i = 0; i < normals_.size(); i++) {
+ MVert &mvert = mesh->mvert[i];
+ normal_float_to_short_v3(mvert.no, normals_[i].data());
+ }
+}
+
+void USDMeshReader::process_normals_face_varying(Mesh *mesh)
+{
+ if (normals_.empty()) {
+ BKE_mesh_calc_normals(mesh);
+ return;
+ }
+
+ /* Check for normals count mismatches to prevent crashes. */
+ if (normals_.size() != mesh->totloop) {
+ std::cerr << "WARNING: loop normal count mismatch for mesh " << mesh->id.name << std::endl;
+ BKE_mesh_calc_normals(mesh);
+ return;
+ }
+
+ mesh->flag |= ME_AUTOSMOOTH;
+
+ long int loop_count = normals_.size();
+
+ float(*lnors)[3] = static_cast<float(*)[3]>(
+ MEM_malloc_arrayN(loop_count, sizeof(float[3]), "USD::FaceNormals"));
+
+ MPoly *mpoly = mesh->mpoly;
+
+ for (int i = 0, e = mesh->totpoly; i < e; ++i, ++mpoly) {
+ for (int j = 0; j < mpoly->totloop; j++) {
+ int blender_index = mpoly->loopstart + j;
+
+ int usd_index = mpoly->loopstart;
+ if (is_left_handed_) {
+ usd_index += mpoly->totloop - 1 - j;
+ }
+ else {
+ usd_index += j;
+ }
+
+ lnors[blender_index][0] = normals_[usd_index][0];
+ lnors[blender_index][1] = normals_[usd_index][1];
+ lnors[blender_index][2] = normals_[usd_index][2];
+ }
+ }
+ BKE_mesh_set_custom_normals(mesh, lnors);
+
+ MEM_freeN(lnors);
+}
+
+/* Set USD uniform (per-face) normals as Blender loop normals. */
+void USDMeshReader::process_normals_uniform(Mesh *mesh)
+{
+ if (normals_.empty()) {
+ BKE_mesh_calc_normals(mesh);
+ return;
+ }
+
+ /* Check for normals count mismatches to prevent crashes. */
+ if (normals_.size() != mesh->totpoly) {
+ std::cerr << "WARNING: uniform normal count mismatch for mesh " << mesh->id.name << std::endl;
+ BKE_mesh_calc_normals(mesh);
+ return;
+ }
+
+ float(*lnors)[3] = static_cast<float(*)[3]>(
+ MEM_malloc_arrayN(mesh->totloop, sizeof(float[3]), "USD::FaceNormals"));
+
+ MPoly *mpoly = mesh->mpoly;
+
+ for (int i = 0, e = mesh->totpoly; i < e; ++i, ++mpoly) {
+
+ for (int j = 0; j < mpoly->totloop; j++) {
+ int loop_index = mpoly->loopstart + j;
+ lnors[loop_index][0] = normals_[i][0];
+ lnors[loop_index][1] = normals_[i][1];
+ lnors[loop_index][2] = normals_[i][2];
+ }
+ }
+
+ mesh->flag |= ME_AUTOSMOOTH;
+ BKE_mesh_set_custom_normals(mesh, lnors);
+
+ MEM_freeN(lnors);
+}
+
+void USDMeshReader::read_mesh_sample(ImportSettings *settings,
+ Mesh *mesh,
+ const double motionSampleTime,
+ const bool new_mesh)
+{
+ /* Note that for new meshes we always want to read verts and polys,
+ * regardless of the value of the read_flag, to avoid a crash downstream
+ * in code that expect this data to be there. */
+
+ if (new_mesh || (settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0) {
+ for (int i = 0; i < positions_.size(); i++) {
+ MVert &mvert = mesh->mvert[i];
+ mvert.co[0] = positions_[i][0];
+ mvert.co[1] = positions_[i][1];
+ mvert.co[2] = positions_[i][2];
+ }
+ }
+
+ if (new_mesh || (settings->read_flag & MOD_MESHSEQ_READ_POLY) != 0) {
+ read_mpolys(mesh);
+ if (normal_interpolation_ == pxr::UsdGeomTokens->faceVarying) {
+ process_normals_face_varying(mesh);
+ }
+ else if (normal_interpolation_ == pxr::UsdGeomTokens->uniform) {
+ process_normals_uniform(mesh);
+ }
+ else {
+ /* Default */
+ BKE_mesh_calc_normals(mesh);
+ }
+ }
+
+ /* Process point normals after reading polys. This
+ * is important in the case where the normals are empty
+ * and we invoke BKE_mesh_calc_normals(mesh), which requires
+ * edges to be defined. */
+ if ((settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0 &&
+ normal_interpolation_ == pxr::UsdGeomTokens->vertex) {
+ process_normals_vertex_varying(mesh);
+ }
+
+ if ((settings->read_flag & MOD_MESHSEQ_READ_UV) != 0) {
+ read_uvs(mesh, motionSampleTime, new_mesh);
+ }
+
+ if ((settings->read_flag & MOD_MESHSEQ_READ_COLOR) != 0) {
+ read_colors(mesh, motionSampleTime);
+ }
+}
+
+void USDMeshReader::assign_facesets_to_mpoly(double motionSampleTime,
+ MPoly *mpoly,
+ const int /* totpoly */,
+ std::map<pxr::SdfPath, int> *r_mat_map)
+{
+ if (r_mat_map == nullptr) {
+ return;
+ }
+
+ /* Find the geom subsets that have bound materials.
+ * We don't call #pxr::UsdShadeMaterialBindingAPI::GetMaterialBindSubsets()
+ * because this function returns only those subsets that are in the 'materialBind'
+ * family, but, in practice, applications (like Houdini) might export subsets
+ * in different families that are bound to materials.
+ * TODO(makowalski): Reassess if the above is the best approach. */
+ const std::vector<pxr::UsdGeomSubset> subsets = pxr::UsdGeomSubset::GetAllGeomSubsets(
+ mesh_prim_);
+
+ int current_mat = 0;
+ if (!subsets.empty()) {
+ for (const pxr::UsdGeomSubset &subset : subsets) {
+ pxr::UsdShadeMaterialBindingAPI subset_api = pxr::UsdShadeMaterialBindingAPI(
+ subset.GetPrim());
+
+ pxr::UsdShadeMaterial subset_mtl = subset_api.ComputeBoundMaterial();
+
+ if (!subset_mtl) {
+ continue;
+ }
+
+ pxr::SdfPath subset_mtl_path = subset_mtl.GetPath();
+
+ if (subset_mtl_path.IsEmpty()) {
+ continue;
+ }
+
+ if (r_mat_map->find(subset_mtl_path) == r_mat_map->end()) {
+ (*r_mat_map)[subset_mtl_path] = 1 + current_mat++;
+ }
+
+ const int mat_idx = (*r_mat_map)[subset_mtl_path] - 1;
+
+ pxr::UsdAttribute indicesAttribute = subset.GetIndicesAttr();
+ pxr::VtIntArray indices;
+ indicesAttribute.Get(&indices, motionSampleTime);
+
+ for (int i = 0; i < indices.size(); i++) {
+ MPoly &poly = mpoly[indices[i]];
+ poly.mat_nr = mat_idx;
+ }
+ }
+ }
+
+ if (r_mat_map->empty()) {
+ pxr::UsdShadeMaterialBindingAPI api = pxr::UsdShadeMaterialBindingAPI(prim_);
+
+ if (pxr::UsdShadeMaterial mtl = api.ComputeBoundMaterial()) {
+
+ pxr::SdfPath mtl_path = mtl.GetPath();
+
+ if (!mtl_path.IsEmpty()) {
+ r_mat_map->insert(std::make_pair(mtl.GetPath(), 1));
+ }
+ }
+ }
+}
+
+void USDMeshReader::readFaceSetsSample(Main *bmain, Mesh *mesh, const double motionSampleTime)
+{
+ if (!import_params_.import_materials) {
+ return;
+ }
+
+ std::map<pxr::SdfPath, int> mat_map;
+ assign_facesets_to_mpoly(motionSampleTime, mesh->mpoly, mesh->totpoly, &mat_map);
+ utils::assign_materials(bmain, object_, mat_map, this->import_params_, this->prim_.GetStage());
+}
+
+Mesh *USDMeshReader::read_mesh(Mesh *existing_mesh,
+ const double motionSampleTime,
+ const int read_flag,
+ const char ** /* err_str */)
+{
+ if (!mesh_prim_) {
+ return existing_mesh;
+ }
+
+ mesh_prim_.GetOrientationAttr().Get(&orientation_);
+ if (orientation_ == pxr::UsdGeomTokens->leftHanded) {
+ is_left_handed_ = true;
+ }
+
+ std::vector<pxr::TfToken> uv_tokens;
+
+ /* Currently we only handle UV primvars. */
+ if (read_flag & MOD_MESHSEQ_READ_UV) {
+
+ std::vector<pxr::UsdGeomPrimvar> primvars = mesh_prim_.GetPrimvars();
+
+ for (pxr::UsdGeomPrimvar p : primvars) {
+
+ pxr::TfToken name = p.GetPrimvarName();
+ pxr::SdfValueTypeName type = p.GetTypeName();
+
+ bool is_uv = false;
+
+ /* Assume all UVs are stored in one of these primvar types */
+ if (type == pxr::SdfValueTypeNames->TexCoord2hArray ||
+ type == pxr::SdfValueTypeNames->TexCoord2fArray ||
+ type == pxr::SdfValueTypeNames->TexCoord2dArray) {
+ is_uv = true;
+ }
+ /* In some cases, the st primvar is stored as float2 values. */
+ else if (name == usdtokens::st && type == pxr::SdfValueTypeNames->Float2Array) {
+ is_uv = true;
+ }
+
+ if (is_uv) {
+
+ pxr::TfToken interp = p.GetInterpolation();
+
+ if (!(interp == pxr::UsdGeomTokens->faceVarying || interp == pxr::UsdGeomTokens->vertex)) {
+ continue;
+ }
+
+ uv_tokens.push_back(p.GetBaseName());
+ has_uvs_ = true;
+
+ /* Record whether the UVs might be time varying. */
+ if (primvar_varying_map_.find(name) == primvar_varying_map_.end()) {
+ bool might_be_time_varying = p.ValueMightBeTimeVarying();
+ primvar_varying_map_.insert(std::make_pair(name, might_be_time_varying));
+ if (might_be_time_varying) {
+ is_time_varying_ = true;
+ }
+ }
+ }
+ }
+ }
+
+ Mesh *active_mesh = existing_mesh;
+ bool new_mesh = false;
+
+ /* TODO(makowalski): implement the optimization of only updating the mesh points when
+ * the topology is consistent, as in the Alembic importer. */
+
+ ImportSettings settings;
+ settings.read_flag |= read_flag;
+
+ if (topology_changed(existing_mesh, motionSampleTime)) {
+ new_mesh = true;
+ active_mesh = BKE_mesh_new_nomain_from_template(
+ existing_mesh, positions_.size(), 0, 0, face_indices_.size(), face_counts_.size());
+
+ for (pxr::TfToken token : uv_tokens) {
+ void *cd_ptr = add_customdata_cb(active_mesh, token.GetText(), CD_MLOOPUV);
+ active_mesh->mloopuv = static_cast<MLoopUV *>(cd_ptr);
+ }
+ }
+
+ read_mesh_sample(&settings, active_mesh, motionSampleTime, new_mesh || is_initial_load_);
+
+ if (new_mesh) {
+ /* Here we assume that the number of materials doesn't change, i.e. that
+ * the material slots that were created when the object was loaded from
+ * USD are still valid now. */
+ size_t num_polys = active_mesh->totpoly;
+ if (num_polys > 0 && import_params_.import_materials) {
+ std::map<pxr::SdfPath, int> mat_map;
+ assign_facesets_to_mpoly(motionSampleTime, active_mesh->mpoly, num_polys, &mat_map);
+ }
+ }
+
+ return active_mesh;
+}
+
+} // namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_reader_mesh.h b/source/blender/io/usd/intern/usd_reader_mesh.h
new file mode 100644
index 00000000000..54ad144d191
--- /dev/null
+++ b/source/blender/io/usd/intern/usd_reader_mesh.h
@@ -0,0 +1,95 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Adapted from the Blender Alembic importer implementation.
+ *
+ * Modifications Copyright (C) 2021 Tangent Animation and
+ * NVIDIA Corporation. All rights reserved.
+ */
+#pragma once
+
+#include "usd.h"
+#include "usd_reader_geom.h"
+
+#include "pxr/usd/usdGeom/mesh.h"
+
+struct MPoly;
+
+namespace blender::io::usd {
+
+class USDMeshReader : public USDGeomReader {
+ private:
+ pxr::UsdGeomMesh mesh_prim_;
+
+ std::unordered_map<std::string, pxr::TfToken> uv_token_map_;
+ std::map<const pxr::TfToken, bool> primvar_varying_map_;
+
+ /* TODO(makowalski): Is it the best strategy to cache the
+ * mesh geometry in the following members? It appears these
+ * arrays are never cleared, so this might bloat memory. */
+ pxr::VtIntArray face_indices_;
+ pxr::VtIntArray face_counts_;
+ pxr::VtVec3fArray positions_;
+ pxr::VtVec3fArray normals_;
+
+ pxr::TfToken normal_interpolation_;
+ pxr::TfToken orientation_;
+ bool is_left_handed_;
+ bool has_uvs_;
+ bool is_time_varying_;
+
+ /* This is to ensure we load all data once, because we reuse the read_mesh function
+ * in the mesh seq modifier, and in initial load. Ideally, a better fix would be
+ * implemented. Note this will break if faces or positions vary. */
+ bool is_initial_load_;
+
+ public:
+ USDMeshReader(const pxr::UsdPrim &prim,
+ const USDImportParams &import_params,
+ const ImportSettings &settings);
+
+ bool valid() const override;
+
+ void create_object(Main *bmain, double motionSampleTime) override;
+ void read_object_data(Main *bmain, double motionSampleTime) override;
+
+ struct Mesh *read_mesh(struct Mesh *existing_mesh,
+ double motionSampleTime,
+ int read_flag,
+ const char **err_str) override;
+
+ bool topology_changed(Mesh *existing_mesh, double motionSampleTime) override;
+
+ private:
+ void process_normals_vertex_varying(Mesh *mesh);
+ void process_normals_face_varying(Mesh *mesh);
+ void process_normals_uniform(Mesh *mesh);
+ void readFaceSetsSample(Main *bmain, Mesh *mesh, double motionSampleTime);
+ void assign_facesets_to_mpoly(double motionSampleTime,
+ struct MPoly *mpoly,
+ int totpoly,
+ std::map<pxr::SdfPath, int> *r_mat_map);
+
+ void read_mpolys(Mesh *mesh);
+ void read_uvs(Mesh *mesh, double motionSampleTime, bool load_uvs = false);
+ void read_colors(Mesh *mesh, double motionSampleTime);
+
+ void read_mesh_sample(ImportSettings *settings,
+ Mesh *mesh,
+ double motionSampleTime,
+ bool new_mesh);
+};
+
+} // namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_reader_nurbs.cc b/source/blender/io/usd/intern/usd_reader_nurbs.cc
new file mode 100644
index 00000000000..9b30b524729
--- /dev/null
+++ b/source/blender/io/usd/intern/usd_reader_nurbs.cc
@@ -0,0 +1,256 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Adapted from the Blender Alembic importer implementation.
+ *
+ * Modifications Copyright (C) 2021 Tangent Animation.
+ * All rights reserved.
+ */
+
+#include "usd_reader_nurbs.h"
+
+#include "BKE_curve.h"
+#include "BKE_mesh.h"
+#include "BKE_object.h"
+
+#include "BLI_listbase.h"
+
+#include "DNA_curve_types.h"
+#include "DNA_object_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include <pxr/base/vt/array.h>
+#include <pxr/base/vt/types.h>
+#include <pxr/base/vt/value.h>
+#include <pxr/usd/sdf/types.h>
+
+#include <pxr/usd/usdGeom/curves.h>
+
+static bool set_knots(const pxr::VtDoubleArray &knots, float *&nu_knots)
+{
+ if (knots.empty()) {
+ return false;
+ }
+
+ /* Skip first and last knots, as they are used for padding. */
+ const size_t num_knots = knots.size();
+ nu_knots = static_cast<float *>(MEM_callocN(num_knots * sizeof(float), __func__));
+
+ for (size_t i = 0; i < num_knots; i++) {
+ nu_knots[i] = (float)knots[i];
+ }
+
+ return true;
+}
+
+namespace blender::io::usd {
+
+void USDNurbsReader::create_object(Main *bmain, const double /* motionSampleTime */)
+{
+ curve_ = BKE_curve_add(bmain, name_.c_str(), OB_CURVE);
+
+ curve_->flag |= CU_DEFORM_FILL | CU_3D;
+ curve_->actvert = CU_ACT_NONE;
+ curve_->resolu = 2;
+
+ object_ = BKE_object_add_only_object(bmain, OB_CURVE, name_.c_str());
+ object_->data = curve_;
+}
+
+void USDNurbsReader::read_object_data(Main *bmain, const double motionSampleTime)
+{
+ Curve *cu = (Curve *)object_->data;
+ read_curve_sample(cu, motionSampleTime);
+
+ if (curve_prim_.GetPointsAttr().ValueMightBeTimeVarying()) {
+ add_cache_modifier();
+ }
+
+ USDXformReader::read_object_data(bmain, motionSampleTime);
+}
+
+void USDNurbsReader::read_curve_sample(Curve *cu, const double motionSampleTime)
+{
+ curve_prim_ = pxr::UsdGeomNurbsCurves(prim_);
+
+ pxr::UsdAttribute widthsAttr = curve_prim_.GetWidthsAttr();
+ pxr::UsdAttribute vertexAttr = curve_prim_.GetCurveVertexCountsAttr();
+ pxr::UsdAttribute pointsAttr = curve_prim_.GetPointsAttr();
+
+ pxr::VtIntArray usdCounts;
+ vertexAttr.Get(&usdCounts, motionSampleTime);
+
+ pxr::VtVec3fArray usdPoints;
+ pointsAttr.Get(&usdPoints, motionSampleTime);
+
+ pxr::VtFloatArray usdWidths;
+ widthsAttr.Get(&usdWidths, motionSampleTime);
+
+ pxr::VtIntArray orders;
+ curve_prim_.GetOrderAttr().Get(&orders, motionSampleTime);
+
+ pxr::VtDoubleArray knots;
+ curve_prim_.GetKnotsAttr().Get(&knots, motionSampleTime);
+
+ pxr::VtVec3fArray usdNormals;
+ curve_prim_.GetNormalsAttr().Get(&usdNormals, motionSampleTime);
+
+ /* If normals, extrude, else bevel.
+ * Perhaps to be replaced by Blender USD Schema. */
+ if (!usdNormals.empty()) {
+ /* Set extrusion to 1. */
+ curve_->ext1 = 1.0f;
+ }
+ else {
+ /* Set bevel depth to 1. */
+ curve_->ext2 = 1.0f;
+ }
+
+ size_t idx = 0;
+ for (size_t i = 0; i < usdCounts.size(); i++) {
+ const int num_verts = usdCounts[i];
+
+ Nurb *nu = static_cast<Nurb *>(MEM_callocN(sizeof(Nurb), __func__));
+ nu->flag = CU_SMOOTH;
+ nu->type = CU_NURBS;
+
+ nu->resolu = cu->resolu;
+ nu->resolv = cu->resolv;
+
+ nu->pntsu = num_verts;
+ nu->pntsv = 1;
+
+ if (i < orders.size()) {
+ nu->orderu = static_cast<short>(orders[i]);
+ }
+ else {
+ nu->orderu = 4;
+ nu->orderv = 4;
+ }
+
+ /* TODO(makowalski): investigate setting Cyclic U and Endpoint U options. */
+#if 0
+ if (knots.size() > 3) {
+ if ((knots[0] == knots[1]) && (knots[knots.size()] == knots[knots.size() - 1])) {
+ nu->flagu |= CU_NURB_ENDPOINT;
+ } else {
+ nu->flagu |= CU_NURB_CYCLIC;
+ }
+ }
+#endif
+
+ float weight = 1.0f;
+
+ nu->bp = static_cast<BPoint *>(MEM_callocN(sizeof(BPoint) * nu->pntsu, __func__));
+ BPoint *bp = nu->bp;
+
+ for (int j = 0; j < nu->pntsu; j++, bp++, idx++) {
+ bp->vec[0] = (float)usdPoints[idx][0];
+ bp->vec[1] = (float)usdPoints[idx][1];
+ bp->vec[2] = (float)usdPoints[idx][2];
+ bp->vec[3] = weight;
+ bp->f1 = SELECT;
+ bp->weight = weight;
+
+ float radius = 0.1f;
+ if (idx < usdWidths.size()) {
+ radius = usdWidths[idx];
+ }
+
+ bp->radius = radius;
+ }
+
+ if (!set_knots(knots, nu->knotsu)) {
+ BKE_nurb_knot_calc_u(nu);
+ }
+
+ BLI_addtail(BKE_curve_nurbs_get(cu), nu);
+ }
+}
+
+Mesh *USDNurbsReader::read_mesh(struct Mesh * /* existing_mesh */,
+ const double motionSampleTime,
+ const int /* read_flag */,
+ const char ** /* err_str */)
+{
+ pxr::UsdGeomCurves curve_prim_(prim_);
+
+ pxr::UsdAttribute widthsAttr = curve_prim_.GetWidthsAttr();
+ pxr::UsdAttribute vertexAttr = curve_prim_.GetCurveVertexCountsAttr();
+ pxr::UsdAttribute pointsAttr = curve_prim_.GetPointsAttr();
+
+ pxr::VtIntArray usdCounts;
+
+ vertexAttr.Get(&usdCounts, motionSampleTime);
+ int num_subcurves = usdCounts.size();
+
+ pxr::VtVec3fArray usdPoints;
+ pointsAttr.Get(&usdPoints, motionSampleTime);
+
+ int vertex_idx = 0;
+ int curve_idx;
+ Curve *curve = static_cast<Curve *>(object_->data);
+
+ const int curve_count = BLI_listbase_count(&curve->nurb);
+ bool same_topology = curve_count == num_subcurves;
+
+ if (same_topology) {
+ Nurb *nurbs = static_cast<Nurb *>(curve->nurb.first);
+ for (curve_idx = 0; nurbs; nurbs = nurbs->next, curve_idx++) {
+ const int num_in_usd = usdCounts[curve_idx];
+ const int num_in_blender = nurbs->pntsu;
+
+ if (num_in_usd != num_in_blender) {
+ same_topology = false;
+ break;
+ }
+ }
+ }
+
+ if (!same_topology) {
+ BKE_nurbList_free(&curve->nurb);
+ read_curve_sample(curve, motionSampleTime);
+ }
+ else {
+ Nurb *nurbs = static_cast<Nurb *>(curve->nurb.first);
+ for (curve_idx = 0; nurbs; nurbs = nurbs->next, curve_idx++) {
+ const int totpoint = usdCounts[curve_idx];
+
+ if (nurbs->bp) {
+ BPoint *point = nurbs->bp;
+
+ for (int i = 0; i < totpoint; i++, point++, vertex_idx++) {
+ point->vec[0] = usdPoints[vertex_idx][0];
+ point->vec[1] = usdPoints[vertex_idx][1];
+ point->vec[2] = usdPoints[vertex_idx][2];
+ }
+ }
+ else if (nurbs->bezt) {
+ BezTriple *bezier = nurbs->bezt;
+
+ for (int i = 0; i < totpoint; i++, bezier++, vertex_idx++) {
+ bezier->vec[1][0] = usdPoints[vertex_idx][0];
+ bezier->vec[1][1] = usdPoints[vertex_idx][1];
+ bezier->vec[1][2] = usdPoints[vertex_idx][2];
+ }
+ }
+ }
+ }
+
+ return BKE_mesh_new_nomain_from_curve(object_);
+}
+
+} // namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_reader_nurbs.h b/source/blender/io/usd/intern/usd_reader_nurbs.h
new file mode 100644
index 00000000000..33a4acf503e
--- /dev/null
+++ b/source/blender/io/usd/intern/usd_reader_nurbs.h
@@ -0,0 +1,61 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Adapted from the Blender Alembic importer implementation.
+ *
+ * Modifications Copyright (C) 2021 Tangent Animation.
+ * All rights reserved.
+ */
+#pragma once
+
+#include "usd.h"
+#include "usd_reader_geom.h"
+
+#include "pxr/usd/usdGeom/nurbsCurves.h"
+
+struct Curve;
+
+namespace blender::io::usd {
+
+class USDNurbsReader : public USDGeomReader {
+ protected:
+ pxr::UsdGeomNurbsCurves curve_prim_;
+ Curve *curve_;
+
+ public:
+ USDNurbsReader(const pxr::UsdPrim &prim,
+ const USDImportParams &import_params,
+ const ImportSettings &settings)
+ : USDGeomReader(prim, import_params, settings), curve_prim_(prim), curve_(nullptr)
+ {
+ }
+
+ bool valid() const override
+ {
+ return static_cast<bool>(curve_prim_);
+ }
+
+ void create_object(Main *bmain, double motionSampleTime) override;
+ void read_object_data(Main *bmain, double motionSampleTime) override;
+
+ void read_curve_sample(Curve *cu, double motionSampleTime);
+
+ Mesh *read_mesh(struct Mesh *existing_mesh,
+ double motionSampleTime,
+ int read_flag,
+ const char **err_str) override;
+};
+
+} // namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_reader_prim.cc b/source/blender/io/usd/intern/usd_reader_prim.cc
new file mode 100644
index 00000000000..abd70f49f23
--- /dev/null
+++ b/source/blender/io/usd/intern/usd_reader_prim.cc
@@ -0,0 +1,80 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Adapted from the Blender Alembic importer implementation.
+ *
+ * Modifications Copyright (C) 2021 Tangent Animation.
+ * All rights reserved.
+ */
+
+#include "usd_reader_prim.h"
+
+#include "BLI_utildefines.h"
+
+namespace blender::io::usd {
+
+USDPrimReader::USDPrimReader(const pxr::UsdPrim &prim,
+ const USDImportParams &import_params,
+ const ImportSettings &settings)
+ : name_(prim.GetName().GetString()),
+ prim_path_(prim.GetPrimPath().GetString()),
+ object_(nullptr),
+ prim_(prim),
+ import_params_(import_params),
+ parent_reader_(nullptr),
+ settings_(&settings),
+ refcount_(0)
+{
+}
+
+USDPrimReader::~USDPrimReader() = default;
+
+const pxr::UsdPrim &USDPrimReader::prim() const
+{
+ return prim_;
+}
+
+Object *USDPrimReader::object() const
+{
+ return object_;
+}
+
+void USDPrimReader::object(Object *ob)
+{
+ object_ = ob;
+}
+
+bool USDPrimReader::valid() const
+{
+ return prim_.IsValid();
+}
+
+int USDPrimReader::refcount() const
+{
+ return refcount_;
+}
+
+void USDPrimReader::incref()
+{
+ refcount_++;
+}
+
+void USDPrimReader::decref()
+{
+ refcount_--;
+ BLI_assert(refcount_ >= 0);
+}
+
+} // namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_reader_prim.h b/source/blender/io/usd/intern/usd_reader_prim.h
new file mode 100644
index 00000000000..5aff52f011f
--- /dev/null
+++ b/source/blender/io/usd/intern/usd_reader_prim.h
@@ -0,0 +1,131 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Adapted from the Blender Alembic importer implementation.
+ *
+ * Modifications Copyright (C) 2021 Tangent Animation.
+ * All rights reserved.
+ */
+#pragma once
+
+#include "usd.h"
+
+#include <pxr/usd/usd/prim.h>
+
+struct Main;
+struct Object;
+
+namespace blender::io::usd {
+
+struct ImportSettings {
+ bool do_convert_mat;
+ float conversion_mat[4][4];
+
+ int from_up;
+ int from_forward;
+ float scale;
+ bool is_sequence;
+ bool set_frame_range;
+
+ /* Length and frame offset of file sequences. */
+ int sequence_len;
+ int sequence_offset;
+
+ /* From MeshSeqCacheModifierData.read_flag */
+ int read_flag;
+
+ bool validate_meshes;
+
+ CacheFile *cache_file;
+
+ ImportSettings()
+ : do_convert_mat(false),
+ from_up(0),
+ from_forward(0),
+ scale(1.0f),
+ is_sequence(false),
+ set_frame_range(false),
+ sequence_len(1),
+ sequence_offset(0),
+ read_flag(0),
+ validate_meshes(false),
+ cache_file(NULL)
+ {
+ }
+};
+
+/* Most generic USD Reader. */
+
+class USDPrimReader {
+
+ protected:
+ std::string name_;
+ std::string prim_path_;
+ Object *object_;
+ pxr::UsdPrim prim_;
+ const USDImportParams &import_params_;
+ USDPrimReader *parent_reader_;
+ const ImportSettings *settings_;
+ int refcount_;
+
+ public:
+ USDPrimReader(const pxr::UsdPrim &prim,
+ const USDImportParams &import_params,
+ const ImportSettings &settings);
+ virtual ~USDPrimReader();
+
+ const pxr::UsdPrim &prim() const;
+
+ virtual bool valid() const;
+
+ virtual void create_object(Main *bmain, double motionSampleTime) = 0;
+ virtual void read_object_data(Main * /* bmain */, double /* motionSampleTime */){};
+
+ Object *object() const;
+ void object(Object *ob);
+
+ USDPrimReader *parent() const
+ {
+ return parent_reader_;
+ }
+ void parent(USDPrimReader *parent)
+ {
+ parent_reader_ = parent;
+ }
+
+ /* Since readers might be referenced through handles
+ * maintained by modifiers and constraints, we provide
+ * a reference count to facilitate managing the object
+ * lifetime.
+ * TODO(makowalski): investigate transitioning to using
+ * smart pointers for readers, or, alternatively look into
+ * making the lifetime management more robust, e.g., by
+ * making the destructors protected and implementing deletion
+ * in decref(), etc. */
+ int refcount() const;
+ void incref();
+ void decref();
+
+ const std::string &name() const
+ {
+ return name_;
+ }
+ const std::string &prim_path() const
+ {
+ return prim_path_;
+ }
+};
+
+} // namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_reader_stage.cc b/source/blender/io/usd/intern/usd_reader_stage.cc
new file mode 100644
index 00000000000..233b3d9da4d
--- /dev/null
+++ b/source/blender/io/usd/intern/usd_reader_stage.cc
@@ -0,0 +1,324 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2021 Tangent Animation and
+ * NVIDIA Corporation. All rights reserved.
+ */
+
+#include "usd_reader_stage.h"
+#include "usd_reader_camera.h"
+#include "usd_reader_curve.h"
+#include "usd_reader_instance.h"
+#include "usd_reader_light.h"
+#include "usd_reader_mesh.h"
+#include "usd_reader_nurbs.h"
+#include "usd_reader_prim.h"
+#include "usd_reader_volume.h"
+#include "usd_reader_xform.h"
+
+#include <pxr/pxr.h>
+#include <pxr/usd/usdGeom/camera.h>
+#include <pxr/usd/usdGeom/curves.h>
+#include <pxr/usd/usdGeom/mesh.h>
+#include <pxr/usd/usdGeom/nurbsCurves.h>
+#include <pxr/usd/usdGeom/scope.h>
+#include <pxr/usd/usdLux/light.h>
+
+#include <iostream>
+
+namespace blender::io::usd {
+
+USDStageReader::USDStageReader(pxr::UsdStageRefPtr stage,
+ const USDImportParams &params,
+ const ImportSettings &settings)
+ : stage_(stage), params_(params), settings_(settings)
+{
+}
+
+USDStageReader::~USDStageReader()
+{
+ clear_readers();
+}
+
+bool USDStageReader::valid() const
+{
+ return stage_;
+}
+
+USDPrimReader *USDStageReader::create_reader_if_allowed(const pxr::UsdPrim &prim)
+{
+ if (params_.import_cameras && prim.IsA<pxr::UsdGeomCamera>()) {
+ return new USDCameraReader(prim, params_, settings_);
+ }
+ if (params_.import_curves && prim.IsA<pxr::UsdGeomBasisCurves>()) {
+ return new USDCurvesReader(prim, params_, settings_);
+ }
+ if (params_.import_curves && prim.IsA<pxr::UsdGeomNurbsCurves>()) {
+ return new USDNurbsReader(prim, params_, settings_);
+ }
+ if (params_.import_meshes && prim.IsA<pxr::UsdGeomMesh>()) {
+ return new USDMeshReader(prim, params_, settings_);
+ }
+ if (params_.import_lights && prim.IsA<pxr::UsdLuxLight>()) {
+ return new USDLightReader(prim, params_, settings_);
+ }
+ if (params_.import_volumes && prim.IsA<pxr::UsdVolVolume>()) {
+ return new USDVolumeReader(prim, params_, settings_);
+ }
+ if (prim.IsA<pxr::UsdGeomImageable>()) {
+ return new USDXformReader(prim, params_, settings_);
+ }
+
+ return nullptr;
+}
+
+USDPrimReader *USDStageReader::create_reader(const pxr::UsdPrim &prim)
+{
+ if (prim.IsA<pxr::UsdGeomCamera>()) {
+ return new USDCameraReader(prim, params_, settings_);
+ }
+ if (prim.IsA<pxr::UsdGeomBasisCurves>()) {
+ return new USDCurvesReader(prim, params_, settings_);
+ }
+ if (prim.IsA<pxr::UsdGeomNurbsCurves>()) {
+ return new USDNurbsReader(prim, params_, settings_);
+ }
+ if (prim.IsA<pxr::UsdGeomMesh>()) {
+ return new USDMeshReader(prim, params_, settings_);
+ }
+ if (prim.IsA<pxr::UsdLuxLight>()) {
+ return new USDLightReader(prim, params_, settings_);
+ }
+ if (prim.IsA<pxr::UsdVolVolume>()) {
+ return new USDVolumeReader(prim, params_, settings_);
+ }
+ if (prim.IsA<pxr::UsdGeomImageable>()) {
+ return new USDXformReader(prim, params_, settings_);
+ }
+ return nullptr;
+}
+
+/* Returns true if the given prim should be included in the
+ * traversal based on the import options and the prim's visibility
+ * attribute. Note that the prim will be trivially included
+ * if it has no visibility attribute or if the visibility
+ * is inherited. */
+bool USDStageReader::include_by_visibility(const pxr::UsdGeomImageable &imageable) const
+{
+ if (!params_.import_visible_only) {
+ /* Invisible prims are allowed. */
+ return true;
+ }
+
+ pxr::UsdAttribute visibility_attr = imageable.GetVisibilityAttr();
+
+ if (!visibility_attr) {
+ /* No visibility attribute, so allow. */
+ return true;
+ }
+
+ /* Include if the prim has an animating visibility attribute or is not invisible. */
+
+ if (visibility_attr.ValueMightBeTimeVarying()) {
+ return true;
+ }
+
+ pxr::TfToken visibility;
+ visibility_attr.Get(&visibility);
+ return visibility != pxr::UsdGeomTokens->invisible;
+}
+
+/* Returns true if the given prim should be included in the
+ * traversal based on the import options and the prim's purpose
+ * attribute. E.g., return false (to exclude the prim) if the prim
+ * represents guide geometry and the 'Import Guide' option is
+ * toggled off. */
+bool USDStageReader::include_by_purpose(const pxr::UsdGeomImageable &imageable) const
+{
+ if (params_.import_guide && params_.import_proxy && params_.import_render) {
+ /* The options allow any purpose, so we trivially include the prim. */
+ return true;
+ }
+
+ pxr::UsdAttribute purpose_attr = imageable.GetPurposeAttr();
+
+ if (!purpose_attr) {
+ /* No purpose attribute, so trivially include the prim. */
+ return true;
+ }
+
+ pxr::TfToken purpose;
+ purpose_attr.Get(&purpose);
+
+ if (purpose == pxr::UsdGeomTokens->guide) {
+ return params_.import_guide;
+ }
+ if (purpose == pxr::UsdGeomTokens->proxy) {
+ return params_.import_proxy;
+ }
+ if (purpose == pxr::UsdGeomTokens->render) {
+ return params_.import_render;
+ }
+
+ return true;
+}
+
+/* Determine if the given reader can use the parent of the encapsulated USD prim
+ * to compute the Blender object's transform. If so, the reader is appropriately
+ * flagged and the function returns true. Otherwise, the function returns false. */
+static bool merge_with_parent(USDPrimReader *reader)
+{
+ USDXformReader *xform_reader = dynamic_cast<USDXformReader *>(reader);
+
+ if (!xform_reader) {
+ return false;
+ }
+
+ /* Check if the Xform reader is already merged. */
+ if (xform_reader->use_parent_xform()) {
+ return false;
+ }
+
+ /* Only merge if the parent is an Xform. */
+ if (!xform_reader->prim().GetParent().IsA<pxr::UsdGeomXform>()) {
+ return false;
+ }
+
+ /* Don't merge Xform and Scope prims. */
+ if (xform_reader->prim().IsA<pxr::UsdGeomXform>() ||
+ xform_reader->prim().IsA<pxr::UsdGeomScope>()) {
+ return false;
+ }
+
+ /* Don't merge if the prim has authored transform ops. */
+ if (xform_reader->prim_has_xform_ops()) {
+ return false;
+ }
+
+ /* Flag the Xform reader as merged. */
+ xform_reader->set_use_parent_xform(true);
+
+ return true;
+}
+
+USDPrimReader *USDStageReader::collect_readers(Main *bmain, const pxr::UsdPrim &prim)
+{
+ if (prim.IsA<pxr::UsdGeomImageable>()) {
+ pxr::UsdGeomImageable imageable(prim);
+
+ if (!include_by_purpose(imageable)) {
+ return nullptr;
+ }
+
+ if (!include_by_visibility(imageable)) {
+ return nullptr;
+ }
+ }
+
+ pxr::Usd_PrimFlagsPredicate filter_predicate = pxr::UsdPrimDefaultPredicate;
+
+ if (params_.import_instance_proxies) {
+ filter_predicate = pxr::UsdTraverseInstanceProxies(filter_predicate);
+ }
+
+ pxr::UsdPrimSiblingRange children = prim.GetFilteredChildren(filter_predicate);
+
+ std::vector<USDPrimReader *> child_readers;
+
+ for (const auto &childPrim : children) {
+ if (USDPrimReader *child_reader = collect_readers(bmain, childPrim)) {
+ child_readers.push_back(child_reader);
+ }
+ }
+
+ if (prim.IsPseudoRoot()) {
+ return nullptr;
+ }
+
+ /* Check if we can merge an Xform with its child prim. */
+ if (child_readers.size() == 1) {
+
+ USDPrimReader *child_reader = child_readers.front();
+
+ if (merge_with_parent(child_reader)) {
+ return child_reader;
+ }
+ }
+
+ USDPrimReader *reader = create_reader_if_allowed(prim);
+
+ if (!reader) {
+ return nullptr;
+ }
+
+ reader->create_object(bmain, 0.0);
+
+ readers_.push_back(reader);
+ reader->incref();
+
+ /* Set each child reader's parent. */
+ for (USDPrimReader *child_reader : child_readers) {
+ child_reader->parent(reader);
+ }
+
+ return reader;
+}
+
+void USDStageReader::collect_readers(Main *bmain)
+{
+ if (!valid()) {
+ return;
+ }
+
+ clear_readers();
+
+ /* Iterate through the stage. */
+ pxr::UsdPrim root = stage_->GetPseudoRoot();
+
+ std::string prim_path_mask(params_.prim_path_mask);
+
+ if (!prim_path_mask.empty()) {
+ pxr::UsdPrim prim = stage_->GetPrimAtPath(pxr::SdfPath(prim_path_mask));
+ if (prim.IsValid()) {
+ root = prim;
+ }
+ else {
+ std::cerr << "WARNING: Prim Path Mask " << prim_path_mask
+ << " does not specify a valid prim.\n";
+ }
+ }
+
+ stage_->SetInterpolationType(pxr::UsdInterpolationType::UsdInterpolationTypeHeld);
+ collect_readers(bmain, root);
+}
+
+void USDStageReader::clear_readers()
+{
+ for (USDPrimReader *reader : readers_) {
+ if (!reader) {
+ continue;
+ }
+
+ reader->decref();
+
+ if (reader->refcount() == 0) {
+ delete reader;
+ }
+ }
+
+ readers_.clear();
+}
+
+} // Namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_reader_stage.h b/source/blender/io/usd/intern/usd_reader_stage.h
new file mode 100644
index 00000000000..ba223962c0c
--- /dev/null
+++ b/source/blender/io/usd/intern/usd_reader_stage.h
@@ -0,0 +1,90 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2021 Tangent Animation and
+ * NVIDIA Corporation. All rights reserved.
+ */
+#pragma once
+
+struct Main;
+
+#include "usd.h"
+#include "usd_reader_prim.h"
+
+#include <pxr/usd/usd/stage.h>
+#include <pxr/usd/usdGeom/imageable.h>
+
+#include <vector>
+
+struct ImportSettings;
+
+namespace blender::io::usd {
+
+typedef std::map<pxr::SdfPath, std::vector<USDPrimReader *>> ProtoReaderMap;
+
+class USDStageReader {
+
+ protected:
+ pxr::UsdStageRefPtr stage_;
+ USDImportParams params_;
+ ImportSettings settings_;
+
+ std::vector<USDPrimReader *> readers_;
+
+ public:
+ USDStageReader(pxr::UsdStageRefPtr stage,
+ const USDImportParams &params,
+ const ImportSettings &settings);
+
+ ~USDStageReader();
+
+ USDPrimReader *create_reader_if_allowed(const pxr::UsdPrim &prim);
+
+ USDPrimReader *create_reader(const pxr::UsdPrim &prim);
+
+ void collect_readers(struct Main *bmain);
+
+ bool valid() const;
+
+ pxr::UsdStageRefPtr stage()
+ {
+ return stage_;
+ }
+ const USDImportParams &params() const
+ {
+ return params_;
+ }
+
+ const ImportSettings &settings() const
+ {
+ return settings_;
+ }
+
+ void clear_readers();
+
+ const std::vector<USDPrimReader *> &readers() const
+ {
+ return readers_;
+ };
+
+ private:
+ USDPrimReader *collect_readers(Main *bmain, const pxr::UsdPrim &prim);
+
+ bool include_by_visibility(const pxr::UsdGeomImageable &imageable) const;
+
+ bool include_by_purpose(const pxr::UsdGeomImageable &imageable) const;
+};
+
+}; // namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_reader_volume.cc b/source/blender/io/usd/intern/usd_reader_volume.cc
new file mode 100644
index 00000000000..871f791c1dd
--- /dev/null
+++ b/source/blender/io/usd/intern/usd_reader_volume.cc
@@ -0,0 +1,114 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2021 Tangent Animation.
+ * All rights reserved.
+ */
+
+#include "usd_reader_volume.h"
+
+#include "BKE_object.h"
+#include "BKE_volume.h"
+
+#include "DNA_object_types.h"
+#include "DNA_volume_types.h"
+
+#include <pxr/usd/usdVol/openVDBAsset.h>
+#include <pxr/usd/usdVol/volume.h>
+
+#include <iostream>
+
+namespace usdtokens {
+
+static const pxr::TfToken density("density", pxr::TfToken::Immortal);
+
+}
+
+namespace blender::io::usd {
+
+void USDVolumeReader::create_object(Main *bmain, const double /* motionSampleTime */)
+{
+ Volume *volume = (Volume *)BKE_volume_add(bmain, name_.c_str());
+
+ object_ = BKE_object_add_only_object(bmain, OB_VOLUME, name_.c_str());
+ object_->data = volume;
+}
+
+void USDVolumeReader::read_object_data(Main *bmain, const double motionSampleTime)
+{
+ if (!volume_) {
+ return;
+ }
+
+ Volume *volume = static_cast<Volume *>(object_->data);
+
+ if (!volume) {
+ return;
+ }
+
+ pxr::UsdVolVolume::FieldMap fields = volume_.GetFieldPaths();
+
+ for (pxr::UsdVolVolume::FieldMap::const_iterator it = fields.begin(); it != fields.end(); ++it) {
+
+ pxr::UsdPrim fieldPrim = prim_.GetStage()->GetPrimAtPath(it->second);
+
+ if (!fieldPrim.IsA<pxr::UsdVolOpenVDBAsset>()) {
+ continue;
+ }
+
+ pxr::UsdVolOpenVDBAsset fieldBase(fieldPrim);
+
+ pxr::UsdAttribute fieldNameAttr = fieldBase.GetFieldNameAttr();
+
+ if (fieldNameAttr.IsAuthored()) {
+ pxr::TfToken fieldName;
+ fieldNameAttr.Get(&fieldName, motionSampleTime);
+
+ /* A Blender volume creates density by default. */
+ if (fieldName != usdtokens::density) {
+ BKE_volume_grid_add(volume, fieldName.GetString().c_str(), VOLUME_GRID_FLOAT);
+ }
+ }
+
+ pxr::UsdAttribute filepathAttr = fieldBase.GetFilePathAttr();
+
+ if (filepathAttr.IsAuthored()) {
+ pxr::SdfAssetPath fp;
+ filepathAttr.Get(&fp, motionSampleTime);
+
+ if (filepathAttr.ValueMightBeTimeVarying()) {
+ std::vector<double> filePathTimes;
+ filepathAttr.GetTimeSamples(&filePathTimes);
+
+ if (!filePathTimes.empty()) {
+ int start = static_cast<int>(filePathTimes.front());
+ int end = static_cast<int>(filePathTimes.back());
+
+ volume->is_sequence = static_cast<char>(true);
+ volume->frame_start = start;
+ volume->frame_duration = (end - start) + 1;
+ }
+ }
+
+ std::string filepath = fp.GetResolvedPath();
+
+ strcpy(volume->filepath, filepath.c_str());
+ }
+ }
+
+ USDXformReader::read_object_data(bmain, motionSampleTime);
+}
+
+} // namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_reader_volume.h b/source/blender/io/usd/intern/usd_reader_volume.h
new file mode 100644
index 00000000000..ca2fddb5531
--- /dev/null
+++ b/source/blender/io/usd/intern/usd_reader_volume.h
@@ -0,0 +1,49 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2021 Tangent Animation.
+ * All rights reserved.
+ */
+#pragma once
+
+#include "usd.h"
+#include "usd_reader_xform.h"
+
+#include "pxr/usd/usdVol/volume.h"
+
+namespace blender::io::usd {
+
+class USDVolumeReader : public USDXformReader {
+ private:
+ pxr::UsdVolVolume volume_;
+
+ public:
+ USDVolumeReader(const pxr::UsdPrim &prim,
+ const USDImportParams &import_params,
+ const ImportSettings &settings)
+ : USDXformReader(prim, import_params, settings), volume_(prim)
+ {
+ }
+
+ bool valid() const override
+ {
+ return static_cast<bool>(volume_);
+ }
+
+ void create_object(Main *bmain, double motionSampleTime) override;
+ void read_object_data(Main *bmain, double motionSampleTime) override;
+};
+
+} // namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_reader_xform.cc b/source/blender/io/usd/intern/usd_reader_xform.cc
new file mode 100644
index 00000000000..eebcc5eb3d5
--- /dev/null
+++ b/source/blender/io/usd/intern/usd_reader_xform.cc
@@ -0,0 +1,184 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Adapted from the Blender Alembic importer implementation.
+ *
+ * Modifications Copyright (C) 2021 Tangent Animation.
+ * All rights reserved.
+ */
+
+#include "usd_reader_xform.h"
+
+#include "BKE_constraint.h"
+#include "BKE_lib_id.h"
+#include "BKE_library.h"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
+
+#include "BLI_math_geom.h"
+#include "BLI_string.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_cachefile_types.h"
+#include "DNA_constraint_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_types.h"
+#include "DNA_space_types.h" /* for FILE_MAX */
+
+#include <pxr/base/gf/math.h>
+#include <pxr/base/gf/matrix4f.h>
+
+#include <pxr/usd/usdGeom/xform.h>
+
+namespace blender::io::usd {
+
+void USDXformReader::create_object(Main *bmain, const double /* motionSampleTime */)
+{
+ object_ = BKE_object_add_only_object(bmain, OB_EMPTY, name_.c_str());
+ object_->empty_drawsize = 0.1f;
+ object_->data = nullptr;
+}
+
+void USDXformReader::read_object_data(Main * /* bmain */, const double motionSampleTime)
+{
+ bool is_constant;
+ float transform_from_usd[4][4];
+
+ read_matrix(transform_from_usd, motionSampleTime, import_params_.scale, &is_constant);
+
+ if (!is_constant) {
+ bConstraint *con = BKE_constraint_add_for_object(
+ object_, nullptr, CONSTRAINT_TYPE_TRANSFORM_CACHE);
+ bTransformCacheConstraint *data = static_cast<bTransformCacheConstraint *>(con->data);
+
+ std::string prim_path = use_parent_xform_ ? prim_.GetParent().GetPath().GetAsString() :
+ prim_path_;
+
+ BLI_strncpy(data->object_path, prim_path.c_str(), FILE_MAX);
+
+ data->cache_file = settings_->cache_file;
+ id_us_plus(&data->cache_file->id);
+ }
+
+ BKE_object_apply_mat4(object_, transform_from_usd, true, false);
+}
+
+void USDXformReader::read_matrix(float r_mat[4][4] /* local matrix */,
+ const float time,
+ const float scale,
+ bool *r_is_constant)
+{
+ if (r_is_constant) {
+ *r_is_constant = true;
+ }
+
+ unit_m4(r_mat);
+
+ pxr::UsdGeomXformable xformable;
+
+ if (use_parent_xform_) {
+ xformable = pxr::UsdGeomXformable(prim_.GetParent());
+ }
+ else {
+ xformable = pxr::UsdGeomXformable(prim_);
+ }
+
+ if (!xformable) {
+ /* This might happen if the prim is a Scope. */
+ return;
+ }
+
+ if (r_is_constant) {
+ *r_is_constant = !xformable.TransformMightBeTimeVarying();
+ }
+
+ pxr::GfMatrix4d usd_local_xf;
+ bool reset_xform_stack;
+ xformable.GetLocalTransformation(&usd_local_xf, &reset_xform_stack, time);
+
+ /* Convert the result to a float matrix. */
+ pxr::GfMatrix4f mat4f = pxr::GfMatrix4f(usd_local_xf);
+ mat4f.Get(r_mat);
+
+ /* Apply global scaling and rotation only to root objects, parenting
+ * will propagate it. */
+ if ((scale != 1.0 || settings_->do_convert_mat) && is_root_xform_) {
+
+ if (scale != 1.0f) {
+ float scale_mat[4][4];
+ scale_m4_fl(scale_mat, scale);
+ mul_m4_m4m4(r_mat, scale_mat, r_mat);
+ }
+
+ if (settings_->do_convert_mat) {
+ mul_m4_m4m4(r_mat, settings_->conversion_mat, r_mat);
+ }
+ }
+}
+
+bool USDXformReader::prim_has_xform_ops() const
+{
+ pxr::UsdGeomXformable xformable(prim_);
+
+ if (!xformable) {
+ /* This might happen if the prim is a Scope. */
+ return false;
+ }
+
+ bool reset_xform_stack = false;
+
+ return !xformable.GetOrderedXformOps(&reset_xform_stack).empty();
+}
+
+bool USDXformReader::is_root_xform_prim() const
+{
+ if (!prim_.IsValid()) {
+ return false;
+ }
+
+ if (prim_.IsInMaster()) {
+ /* We don't consider prototypes to be root prims,
+ * because we never want to apply global scaling
+ * or rotations to the prototypes themselves. */
+ return false;
+ }
+
+ if (prim_.IsA<pxr::UsdGeomXformable>()) {
+ /* If this prim doesn't have an ancestor that's a
+ * UsdGeomXformable, then it's a root prim. Note
+ * that it's not sufficient to only check the immediate
+ * parent prim, since the immediate parent could be a
+ * UsdGeomScope that has an xformable ancestor. */
+ pxr::UsdPrim cur_parent = prim_.GetParent();
+
+ if (use_parent_xform_) {
+ cur_parent = cur_parent.GetParent();
+ }
+
+ while (cur_parent && !cur_parent.IsPseudoRoot()) {
+ if (cur_parent.IsA<pxr::UsdGeomXformable>()) {
+ return false;
+ }
+ cur_parent = cur_parent.GetParent();
+ }
+
+ /* We didn't find an xformable ancestor. */
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_reader_xform.h b/source/blender/io/usd/intern/usd_reader_xform.h
new file mode 100644
index 00000000000..587ac373a4f
--- /dev/null
+++ b/source/blender/io/usd/intern/usd_reader_xform.h
@@ -0,0 +1,68 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Adapted from the Blender Alembic importer implementation.
+ *
+ * Modifications Copyright (C) 2021 Tangent Animation.
+ * All rights reserved.
+ */
+#pragma once
+
+#include "usd.h"
+#include "usd_reader_prim.h"
+
+namespace blender::io::usd {
+
+class USDXformReader : public USDPrimReader {
+ private:
+ bool use_parent_xform_;
+
+ /* Indicates if the created object is the root of a
+ * transform hierarchy. */
+ bool is_root_xform_;
+
+ public:
+ USDXformReader(const pxr::UsdPrim &prim,
+ const USDImportParams &import_params,
+ const ImportSettings &settings)
+ : USDPrimReader(prim, import_params, settings),
+ use_parent_xform_(false),
+ is_root_xform_(is_root_xform_prim())
+ {
+ }
+
+ void create_object(Main *bmain, double motionSampleTime) override;
+ void read_object_data(Main *bmain, double motionSampleTime) override;
+
+ void read_matrix(float r_mat[4][4], const float time, const float scale, bool *r_is_constant);
+
+ bool use_parent_xform() const
+ {
+ return use_parent_xform_;
+ }
+ void set_use_parent_xform(bool flag)
+ {
+ use_parent_xform_ = flag;
+ is_root_xform_ = is_root_xform_prim();
+ }
+
+ bool prim_has_xform_ops() const;
+
+ protected:
+ /* Returns true if the contained USD prim is the root of a transform hierarchy. */
+ bool is_root_xform_prim() const;
+};
+
+} // namespace blender::io::usd
diff --git a/source/blender/io/usd/usd.h b/source/blender/io/usd/usd.h
index 40e2d0d8674..6b6b2d37162 100644
--- a/source/blender/io/usd/usd.h
+++ b/source/blender/io/usd/usd.h
@@ -26,6 +26,10 @@ extern "C" {
#endif
struct bContext;
+struct Object;
+struct CacheArchiveHandle;
+struct CacheReader;
+struct CacheFile;
struct USDExportParams {
bool export_animation;
@@ -39,6 +43,34 @@ struct USDExportParams {
enum eEvaluationMode evaluation_mode;
};
+struct USDImportParams {
+ float scale;
+ bool is_sequence;
+ bool set_frame_range;
+ int sequence_len;
+ int offset;
+ bool validate_meshes;
+ char mesh_read_flag;
+ bool import_cameras;
+ bool import_curves;
+ bool import_lights;
+ bool import_materials;
+ bool import_meshes;
+ bool import_volumes;
+ char *prim_path_mask;
+ bool import_subdiv;
+ bool import_instance_proxies;
+ bool create_collection;
+ bool import_guide;
+ bool import_proxy;
+ bool import_render;
+ bool import_visible_only;
+ bool use_instancing;
+ bool import_usd_preview;
+ bool set_material_blend;
+ float light_intensity_scale;
+};
+
/* The USD_export takes a as_background_job parameter, and returns a boolean.
*
* When as_background_job=true, returns false immediately after scheduling
@@ -53,8 +85,45 @@ bool USD_export(struct bContext *C,
const struct USDExportParams *params,
bool as_background_job);
+bool USD_import(struct bContext *C,
+ const char *filepath,
+ const struct USDImportParams *params,
+ bool as_background_job);
+
int USD_get_version(void);
+/* USD Import and Mesh Cache interface. */
+
+struct CacheArchiveHandle *USD_create_handle(struct Main *bmain,
+ const char *filename,
+ struct ListBase *object_paths);
+
+void USD_free_handle(struct CacheArchiveHandle *handle);
+
+void USD_get_transform(struct CacheReader *reader, float r_mat[4][4], float time, float scale);
+
+/* Either modifies current_mesh in-place or constructs a new mesh. */
+struct Mesh *USD_read_mesh(struct CacheReader *reader,
+ struct Object *ob,
+ struct Mesh *existing_mesh,
+ const float time,
+ const char **err_str,
+ int read_flag);
+
+bool USD_mesh_topology_changed(struct CacheReader *reader,
+ struct Object *ob,
+ struct Mesh *existing_mesh,
+ const float time,
+ const char **err_str);
+
+struct CacheReader *CacheReader_open_usd_object(struct CacheArchiveHandle *handle,
+ struct CacheReader *reader,
+ struct Object *object,
+ const char *object_path);
+
+void USD_CacheReader_incref(struct CacheReader *reader);
+void USD_CacheReader_free(struct CacheReader *reader);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h
index c9d652ad03d..10a5a0f1c47 100644
--- a/source/blender/makesdna/DNA_ID.h
+++ b/source/blender/makesdna/DNA_ID.h
@@ -429,7 +429,8 @@ typedef struct PreviewImage {
* BKE_library_override typically (especially due to the check on LIB_TAG_EXTERN). */
#define ID_IS_OVERRIDABLE_LIBRARY(_id) \
(ID_IS_LINKED(_id) && !ID_MISSING(_id) && (((const ID *)(_id))->tag & LIB_TAG_EXTERN) != 0 && \
- (BKE_idtype_get_info_from_id((const ID *)(_id))->flags & IDTYPE_FLAGS_NO_LIBLINKING) == 0)
+ (BKE_idtype_get_info_from_id((const ID *)(_id))->flags & IDTYPE_FLAGS_NO_LIBLINKING) == 0 && \
+ !ELEM(GS(((ID *)(_id))->name), ID_SCE))
/* NOTE: The three checks below do not take into account whether given ID is linked or not (when
* chaining overrides over several libraries). User must ensure the ID is not linked itself
@@ -550,7 +551,7 @@ enum {
/* tag data-block as having actually increased user-count for the extra virtual user. */
LIB_TAG_EXTRAUSER_SET = 1 << 7,
- /* RESET_AFTER_USE tag newly duplicated/copied IDs.
+ /* RESET_AFTER_USE tag newly duplicated/copied IDs (see #ID_NEW_SET macro above).
* Also used internally in readfile.c to mark data-blocks needing do_versions. */
LIB_TAG_NEW = 1 << 8,
/* RESET_BEFORE_USE free test flag.
@@ -562,13 +563,32 @@ enum {
/**
* The data-block is a copy-on-write/localized version.
*
+ * RESET_NEVER
+ *
* \warning This should not be cleared on existing data.
* If support for this is needed, see T88026 as this flag controls memory ownership
* of physics *shared* pointers.
*/
LIB_TAG_COPIED_ON_WRITE = 1 << 12,
-
+ /**
+ * The data-block is not the original COW ID created by the depsgraph, but has be re-allocated
+ * during the evaluation process of another ID.
+ *
+ * RESET_NEVER
+ *
+ * Typical example is object data, when evaluating the object's modifier stack the final obdata
+ * can be different than the COW initial obdata ID.
+ */
LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT = 1 << 13,
+
+ /**
+ * The data-block is fully outside of any ID management area, and should be considered as a
+ * purely independent data.
+ *
+ * RESET_NEVER
+ *
+ * NOTE: Only used by node-groups currently.
+ */
LIB_TAG_LOCALIZED = 1 << 14,
/* RESET_NEVER tag data-block for freeing etc. behavior
diff --git a/source/blender/makesdna/DNA_asset_types.h b/source/blender/makesdna/DNA_asset_types.h
index ca16e6728dd..2975915eccd 100644
--- a/source/blender/makesdna/DNA_asset_types.h
+++ b/source/blender/makesdna/DNA_asset_types.h
@@ -81,7 +81,7 @@ typedef enum eAssetLibraryType {
// ASSET_LIBRARY_PROJECT = 2,
/** Display assets from custom asset libraries, as defined in the preferences
- * (#bUserAssetLibrary). The name will be taken from #FileSelectParams.asset_library.idname
+ * (#bUserAssetLibrary). The name will be taken from #FileSelectParams.asset_library_ref.idname
* then.
* In RNA, we add the index of the custom library to this to identify it by index. So keep
* this last! */
diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h
index 986c009ac26..634ebdff253 100644
--- a/source/blender/makesdna/DNA_brush_types.h
+++ b/source/blender/makesdna/DNA_brush_types.h
@@ -76,7 +76,9 @@ typedef struct BrushGpencilSettings {
float fill_threshold;
/** Number of pixel to consider the leak is too small (x 2). */
short fill_leak;
- char _pad2[2];
+ /* Type of caps: eGPDstroke_Caps. */
+ int8_t caps_type;
+ char _pad;
int flag2;
diff --git a/source/blender/makesdna/DNA_cachefile_defaults.h b/source/blender/makesdna/DNA_cachefile_defaults.h
index d37994bb488..74fbe5012ab 100644
--- a/source/blender/makesdna/DNA_cachefile_defaults.h
+++ b/source/blender/makesdna/DNA_cachefile_defaults.h
@@ -36,9 +36,12 @@
.scale = 1.0f, \
.object_paths ={NULL, NULL}, \
\
+ .type = 0, \
.handle = NULL, \
.handle_filepath[0] = '\0', \
.handle_readers = NULL, \
+ .use_prefetch = 1, \
+ .prefetch_cache_size = 4096, \
}
/** \} */
diff --git a/source/blender/makesdna/DNA_cachefile_types.h b/source/blender/makesdna/DNA_cachefile_types.h
index 04c99c6c4b1..0f4c53a6e7e 100644
--- a/source/blender/makesdna/DNA_cachefile_types.h
+++ b/source/blender/makesdna/DNA_cachefile_types.h
@@ -31,6 +31,13 @@ extern "C" {
struct GSet;
+/* CacheFile::type */
+typedef enum {
+ CACHEFILE_TYPE_ALEMBIC = 1,
+ CACHEFILE_TYPE_USD = 2,
+ CACHE_FILE_TYPE_INVALID = 0,
+} eCacheFileType;
+
/* CacheFile::flag */
enum {
CACHEFILE_DS_EXPAND = (1 << 0),
@@ -44,13 +51,13 @@ enum {
};
#endif
-/* Representation of an object's path inside the Alembic file.
+/* Representation of an object's path inside the archive.
* Note that this is not a file path. */
-typedef struct AlembicObjectPath {
- struct AlembicObjectPath *next, *prev;
+typedef struct CacheObjectPath {
+ struct CacheObjectPath *next, *prev;
char path[4096];
-} AlembicObjectPath;
+} CacheObjectPath;
/* CacheFile::velocity_unit
* Determines what temporal unit is used to interpret velocity vectors for motion blur effects. */
@@ -63,7 +70,7 @@ typedef struct CacheFile {
ID id;
struct AnimData *adt;
- /** Paths of the objects inside of the Alembic archive referenced by this CacheFile. */
+ /** Paths of the objects inside of the archive referenced by this CacheFile. */
ListBase object_paths;
/** 1024 = FILE_MAX. */
@@ -80,18 +87,36 @@ typedef struct CacheFile {
/** The frame offset to subtract. */
float frame_offset;
+ char _pad[4];
+
/** Animation flag. */
short flag;
- short draw_flag; /* UNUSED */
- char _pad[3];
+ /* eCacheFileType enum. */
+ char type;
+
+ /** Do not load data from the cache file and display objects in the scene as boxes, Cycles will
+ * load objects directly from the CacheFile. Other render engines which can load Alembic data
+ * directly can take care of rendering it themselves.
+ */
+ char use_render_procedural;
+
+ char _pad1[3];
+
+ /** Enable data prefetching when using the Cycles Procedural. */
+ char use_prefetch;
+
+ /** Size in megabytes for the prefetch cache used by the Cycles Procedural. */
+ int prefetch_cache_size;
+
+ char _pad2[7];
char velocity_unit;
- /* Name of the velocity property in the Alembic file. */
+ /* Name of the velocity property in the archive. */
char velocity_name[64];
/* Runtime */
- struct AbcArchiveHandle *handle;
+ struct CacheArchiveHandle *handle;
char handle_filepath[1024];
struct GSet *handle_readers;
} CacheFile;
diff --git a/source/blender/makesdna/DNA_collection_types.h b/source/blender/makesdna/DNA_collection_types.h
index 1defa8b782b..3eba02de2a3 100644
--- a/source/blender/makesdna/DNA_collection_types.h
+++ b/source/blender/makesdna/DNA_collection_types.h
@@ -102,10 +102,10 @@ typedef struct Collection {
/* Collection->flag */
enum {
- COLLECTION_RESTRICT_VIEWPORT = (1 << 0), /* Disable in viewports. */
- COLLECTION_RESTRICT_SELECT = (1 << 1), /* Not selectable in viewport. */
+ COLLECTION_HIDE_VIEWPORT = (1 << 0), /* Disable in viewports. */
+ COLLECTION_HIDE_SELECT = (1 << 1), /* Not selectable in viewport. */
/* COLLECTION_DISABLED_DEPRECATED = (1 << 2), */ /* Not used anymore */
- COLLECTION_RESTRICT_RENDER = (1 << 3), /* Disable in renders. */
+ COLLECTION_HIDE_RENDER = (1 << 3), /* Disable in renders. */
COLLECTION_HAS_OBJECT_CACHE = (1 << 4), /* Runtime: object_cache is populated. */
COLLECTION_IS_MASTER = (1 << 5), /* Is master collection embedded in the scene. */
COLLECTION_HAS_OBJECT_CACHE_INSTANCED = (1 << 6), /* for object_cache_instanced. */
diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h
index a77fbc9e45e..822b8705c9b 100644
--- a/source/blender/makesdna/DNA_constraint_types.h
+++ b/source/blender/makesdna/DNA_constraint_types.h
@@ -901,10 +901,16 @@ typedef enum eActionConstraint_Flags {
typedef enum eActionConstraint_MixMode {
/* Multiply the action transformation on the right. */
ACTCON_MIX_AFTER_FULL = 0,
+ /* Multiply the action transformation on the left. */
+ ACTCON_MIX_BEFORE_FULL = 3,
/* Multiply the action transformation on the right, with anti-shear scale handling. */
ACTCON_MIX_AFTER = 1,
/* Multiply the action transformation on the left, with anti-shear scale handling. */
ACTCON_MIX_BEFORE = 2,
+ /* Separately combine Translation, Rotation and Scale, with rotation on the right. */
+ ACTCON_MIX_AFTER_SPLIT = 4,
+ /* Separately combine Translation, Rotation and Scale, with rotation on the left. */
+ ACTCON_MIX_BEFORE_SPLIT = 5,
} eActionConstraint_MixMode;
/* Locked-Axis Values (Locked Track) */
diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h
index 380d8ad1249..68bd2961f23 100644
--- a/source/blender/makesdna/DNA_gpencil_types.h
+++ b/source/blender/makesdna/DNA_gpencil_types.h
@@ -324,6 +324,7 @@ typedef struct bGPDstroke {
struct bGPDcurve *editcurve;
bGPDstroke_Runtime runtime;
+ void *_pad5;
} bGPDstroke;
/** #bGPDstroke.flag */
diff --git a/source/blender/makesdna/DNA_layer_types.h b/source/blender/makesdna/DNA_layer_types.h
index 828c6ff2a51..63e4597150c 100644
--- a/source/blender/makesdna/DNA_layer_types.h
+++ b/source/blender/makesdna/DNA_layer_types.h
@@ -222,7 +222,7 @@ enum {
enum {
LAYER_COLLECTION_HAS_OBJECTS = (1 << 0),
/* LAYER_COLLECTION_VISIBLE_DEPSGRAPH = (1 << 1), */ /* UNUSED */
- LAYER_COLLECTION_RESTRICT_VIEWPORT = (1 << 2),
+ LAYER_COLLECTION_HIDE_VIEWPORT = (1 << 2),
LAYER_COLLECTION_VISIBLE_VIEW_LAYER = (1 << 4),
};
diff --git a/source/blender/makesdna/DNA_lineart_types.h b/source/blender/makesdna/DNA_lineart_types.h
index e93cf050e18..cdb09c3af50 100644
--- a/source/blender/makesdna/DNA_lineart_types.h
+++ b/source/blender/makesdna/DNA_lineart_types.h
@@ -1,6 +1,4 @@
/*
- * ***** BEGIN GPL LICENSE BLOCK *****
- *
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
@@ -17,12 +15,6 @@
*
* The Original Code is Copyright (C) 2010 Blender Foundation.
* All rights reserved.
- *
- * The Original Code is: all of this file.
- *
- * Contributor(s): none yet.
- *
- * ***** END GPL LICENSE BLOCK *****
*/
#pragma once
diff --git a/source/blender/makesdna/DNA_mask_types.h b/source/blender/makesdna/DNA_mask_types.h
index e6a7c004078..419118a38f4 100644
--- a/source/blender/makesdna/DNA_mask_types.h
+++ b/source/blender/makesdna/DNA_mask_types.h
@@ -174,7 +174,7 @@ typedef struct MaskLayer {
/** For animation. */
char flag;
/** Matching 'Object' flag of the same name - eventually use in the outliner. */
- char restrictflag;
+ char visibility_flag;
} MaskLayer;
/* MaskParent->flag */
@@ -206,10 +206,10 @@ enum {
MASK_SPLINE_OFFSET_SMOOTH = 1,
};
-/* ob->restrictflag */
-#define MASK_RESTRICT_VIEW (1 << 0)
-#define MASK_RESTRICT_SELECT (1 << 1)
-#define MASK_RESTRICT_RENDER (1 << 2)
+/* MaskLayer->visibility_flag */
+#define MASK_HIDE_VIEW (1 << 0)
+#define MASK_HIDE_SELECT (1 << 1)
+#define MASK_HIDE_RENDER (1 << 2)
/* SpaceClip->mask_draw_flag */
#define MASK_DRAWFLAG_SMOOTH (1 << 0)
diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h
index 932f4715298..97f14b2195d 100644
--- a/source/blender/makesdna/DNA_mesh_types.h
+++ b/source/blender/makesdna/DNA_mesh_types.h
@@ -231,6 +231,7 @@ typedef struct Mesh {
* default and Face Sets can be used without affecting the color of the mesh. */
int face_sets_color_default;
+ void *_pad2;
Mesh_Runtime runtime;
} Mesh;
diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h
index f66de378c35..1bebbc35747 100644
--- a/source/blender/makesdna/DNA_modifier_types.h
+++ b/source/blender/makesdna/DNA_modifier_types.h
@@ -136,6 +136,7 @@ typedef struct ModifierData {
/* Runtime field which contains runtime data which is specific to a modifier type. */
void *runtime;
+ void *_pad1;
} ModifierData;
typedef enum {
@@ -215,6 +216,7 @@ typedef struct LatticeModifierData {
float strength;
short flag;
char _pad[2];
+ void *_pad1;
} LatticeModifierData;
/* Lattice modifier flags. */
@@ -232,6 +234,7 @@ typedef struct CurveModifierData {
short defaxis;
short flag;
char _pad[4];
+ void *_pad1;
} CurveModifierData;
/* Curve modifier flags */
@@ -283,6 +286,7 @@ typedef struct MaskModifierData {
/** Flags for various things. */
short flag;
float threshold;
+ void *_pad1;
} MaskModifierData;
/* Mask Modifier -> mode */
@@ -373,6 +377,7 @@ typedef struct MirrorModifierData {
float uv_offset[2];
float uv_offset_copy[2];
struct Object *mirror_ob;
+ void *_pad1;
} MirrorModifierData;
/* MirrorModifierData->flag */
@@ -451,6 +456,7 @@ typedef struct BevelModifierData {
/** Curve info for the custom profile */
struct CurveProfile *custom_profile;
+ void *_pad2;
} BevelModifierData;
/* BevelModifierData->flags and BevelModifierData->lim_flags */
@@ -535,6 +541,7 @@ typedef struct FluidModifierData {
float time;
/** Domain, inflow, outflow, .... */
int type;
+ void *_pad1;
} FluidModifierData;
/* Fluid modifier flags */
@@ -680,6 +687,7 @@ typedef struct CastModifierData {
/** MAX_VGROUP_NAME. */
char defgrp_name[64];
short flag, type;
+ void *_pad1;
} CastModifierData;
/* Cast modifier flags */
@@ -725,6 +733,7 @@ typedef struct WaveModifierData {
float timeoffs, lifetime;
char _pad1[4];
+ void *_pad2;
} WaveModifierData;
/* WaveModifierData.flag */
@@ -797,6 +806,7 @@ typedef struct HookModifierData {
float force;
/** Optional vertexgroup name, MAX_VGROUP_NAME. */
char name[64];
+ void *_pad1;
} HookModifierData;
typedef struct SoftbodyModifierData {
@@ -1001,6 +1011,7 @@ typedef struct ParticleSystemModifierData {
int totdmvert, totdmedge, totdmface;
short flag;
char _pad[2];
+ void *_pad1;
} ParticleSystemModifierData;
typedef enum {
@@ -1037,6 +1048,7 @@ typedef struct ParticleInstanceModifierData {
char index_layer_name[64];
/** MAX_CUSTOMDATA_LAYER_NAME. */
char value_layer_name[64];
+ void *_pad1;
} ParticleInstanceModifierData;
typedef enum {
@@ -1057,6 +1069,7 @@ typedef struct ExplodeModifierData {
float protect;
/** MAX_CUSTOMDATA_LAYER_NAME. */
char uvname[64];
+ void *_pad1;
} ExplodeModifierData;
typedef struct MultiresModifierData {
@@ -1086,6 +1099,7 @@ typedef struct FluidsimModifierData {
/** Definition is in DNA_object_fluidsim_types.h. */
struct FluidsimSettings *fss;
+ void *_pad1;
} FluidsimModifierData;
/* DEPRECATED, only used for versioning. */
@@ -1202,6 +1216,7 @@ typedef struct SimpleDeformModifierData {
char deform_axis;
char flag;
+ void *_pad1;
} SimpleDeformModifierData;
/* SimpleDeform->flag */
@@ -1310,6 +1325,7 @@ typedef struct ScrewModifierData {
short flag;
char axis;
char _pad[5];
+ void *_pad1;
} ScrewModifierData;
enum {
@@ -1434,6 +1450,7 @@ typedef struct WarpModifierData {
char flag;
char falloff_type;
char _pad[6];
+ void *_pad1;
} WarpModifierData;
/* WarpModifierData->flag */
@@ -1497,6 +1514,7 @@ typedef struct WeightVGEditModifierData {
/* Padding... */
char _pad0[4];
+ void *_pad1;
} WeightVGEditModifierData;
/* WeightVGEdit flags. */
@@ -2064,6 +2082,7 @@ typedef struct DataTransferModifierData {
char defgrp_name[64];
int flags;
+ void *_pad2;
} DataTransferModifierData;
/* DataTransferModifierData.flags */
@@ -2094,6 +2113,7 @@ typedef struct NormalEditModifierData {
float mix_limit;
float offset[3];
char _pad0[4];
+ void *_pad1;
} NormalEditModifierData;
/* NormalEditModifierData.mode */
@@ -2154,6 +2174,7 @@ typedef struct MeshSeqCacheModifierData {
float last_lookup_time;
int _pad1;
+ void *_pad2;
} MeshSeqCacheModifierData;
/* MeshSeqCacheModifierData.read_flag */
@@ -2198,6 +2219,7 @@ typedef struct SurfaceDeformModifierData {
float mat[4][4];
float strength;
char defgrp_name[64];
+ void *_pad1;
} SurfaceDeformModifierData;
/* Surface Deform modifier flags */
@@ -2259,6 +2281,7 @@ typedef struct NodesModifierData {
/* Contains logged information from the last evaluation. This can be used to help the user to
* debug a node tree. */
void *runtime_eval_log;
+ void *_pad1;
} NodesModifierData;
typedef struct MeshToVolumeModifierData {
@@ -2286,6 +2309,7 @@ typedef struct MeshToVolumeModifierData {
float density;
char _pad2[4];
+ void *_pad3;
} MeshToVolumeModifierData;
/* MeshToVolumeModifierData->resolution_mode */
@@ -2332,6 +2356,7 @@ typedef struct VolumeToMeshModifierData {
/** MAX_NAME */
char grid_name[64];
+ void *_pad1;
} VolumeToMeshModifierData;
/** VolumeToMeshModifierData->resolution_mode */
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index 5152098f57a..fd794ed1b21 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -1328,6 +1328,13 @@ typedef struct NodeAttributeConvert {
int8_t domain;
} NodeAttributeConvert;
+typedef struct NodeGeometrySubdivisionSurface {
+ /* eSubsurfUVSmooth. */
+ uint8_t uv_smooth;
+ /* eSubsurfBoundarySmooth. */
+ uint8_t boundary_smooth;
+} NodeGeometrySubdivisionSurface;
+
typedef struct NodeGeometryMeshCircle {
/* GeometryNodeMeshCircleFillType. */
uint8_t fill_type;
@@ -1355,6 +1362,11 @@ typedef struct NodeSwitch {
uint8_t input_type;
} NodeSwitch;
+typedef struct NodeGeometryCurveSplineType {
+ /* GeometryNodeSplineType. */
+ uint8_t spline_type;
+} NodeGeometryCurveSplineType;
+
typedef struct NodeGeometryCurveSetHandles {
/* GeometryNodeCurveHandleType. */
uint8_t handle_type;
@@ -1362,6 +1374,13 @@ typedef struct NodeGeometryCurveSetHandles {
uint8_t mode;
} NodeGeometryCurveSetHandles;
+typedef struct NodeGeometryCurveSelectHandles {
+ /* GeometryNodeCurveHandleType. */
+ uint8_t handle_type;
+ /* GeometryNodeCurveHandleMode. */
+ uint8_t mode;
+} NodeGeometryCurveSelectHandles;
+
typedef struct NodeGeometryCurvePrimitiveLine {
/* GeometryNodeCurvePrimitiveLineMode. */
uint8_t mode;
@@ -1828,6 +1847,12 @@ typedef enum GeometryNodeBooleanOperation {
GEO_NODE_BOOLEAN_DIFFERENCE = 2,
} GeometryNodeBooleanOperation;
+typedef enum GeometryNodeSplineType {
+ GEO_NODE_SPLINE_TYPE_BEZIER = 0,
+ GEO_NODE_SPLINE_TYPE_NURBS = 1,
+ GEO_NODE_SPLINE_TYPE_POLY = 2,
+} GeometryNodeSplineType;
+
typedef enum GeometryNodeCurvePrimitiveCircleMode {
GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS = 0,
GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_RADIUS = 1
diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h
index 60a34fef899..0250d853898 100644
--- a/source/blender/makesdna/DNA_object_types.h
+++ b/source/blender/makesdna/DNA_object_types.h
@@ -385,14 +385,14 @@ typedef struct Object {
short softflag;
/** For restricting view, select, render etc. accessible in outliner. */
- char restrictflag;
+ short visibility_flag;
- /** Flag for pinning. */
- char shapeflag;
/** Current shape key for menu or pinned. */
short shapenr;
+ /** Flag for pinning. */
+ char shapeflag;
- char _pad3[2];
+ char _pad3[1];
/** Object constraints. */
ListBase constraints;
@@ -433,6 +433,7 @@ typedef struct Object {
ObjectLineArt lineart;
/** Runtime evaluation data (keep last). */
+ void *_pad9;
Object_Runtime runtime;
} Object;
@@ -466,8 +467,6 @@ typedef struct ObHook {
/* used many places, should be specialized. */
#define SELECT 1
-#define OBJECT_ACTIVE_MODIFIER_NONE -1
-
/* type */
enum {
OB_EMPTY = 0,
@@ -670,11 +669,19 @@ enum {
# define OB_FLAG_UNUSED_12 (1 << 12) /* cleared */
#endif
-/* ob->restrictflag */
+/* ob->visibility_flag */
enum {
- OB_RESTRICT_VIEWPORT = 1 << 0,
- OB_RESTRICT_SELECT = 1 << 1,
- OB_RESTRICT_RENDER = 1 << 2,
+ OB_HIDE_VIEWPORT = 1 << 0,
+ OB_HIDE_SELECT = 1 << 1,
+ OB_HIDE_RENDER = 1 << 2,
+ OB_HIDE_CAMERA = 1 << 3,
+ OB_HIDE_DIFFUSE = 1 << 4,
+ OB_HIDE_GLOSSY = 1 << 5,
+ OB_HIDE_TRANSMISSION = 1 << 6,
+ OB_HIDE_VOLUME_SCATTER = 1 << 7,
+ OB_HIDE_SHADOW = 1 << 8,
+ OB_HOLDOUT = 1 << 9,
+ OB_SHADOW_CATCHER = 1 << 10
};
/* ob->shapeflag */
diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h
index af524ff4866..df18501d2ea 100644
--- a/source/blender/makesdna/DNA_sequence_types.h
+++ b/source/blender/makesdna/DNA_sequence_types.h
@@ -296,6 +296,7 @@ typedef struct Editing {
int64_t disk_cache_timestamp;
EditingRuntime runtime;
+ void *_pad1;
} Editing;
/* ************* Effect Variable Structs ********* */
diff --git a/source/blender/makesdna/DNA_sound_types.h b/source/blender/makesdna/DNA_sound_types.h
index b2bb50c56a2..e6394f0a56a 100644
--- a/source/blender/makesdna/DNA_sound_types.h
+++ b/source/blender/makesdna/DNA_sound_types.h
@@ -67,6 +67,7 @@ typedef struct bSound {
/** Runtime only, always reset in readfile. */
short tags;
char _pad[4];
+ double offset_time;
/* Unused currently. */
// int type;
diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h
index 7290647dbc6..863c53615c1 100644
--- a/source/blender/makesdna/DNA_space_types.h
+++ b/source/blender/makesdna/DNA_space_types.h
@@ -754,13 +754,7 @@ typedef struct FileSelectParams {
/** Max number of levels in dirtree to show at once, 0 to disable recursion. */
short recursion_level;
- /* XXX --- still unused -- */
- /** Show font preview. */
- short f_fp;
- /** String to use for font preview. */
- char fp_str[8];
-
- /* XXX --- end unused -- */
+ char _pad4[2];
} FileSelectParams;
/**
@@ -769,7 +763,7 @@ typedef struct FileSelectParams {
typedef struct FileAssetSelectParams {
FileSelectParams base_params;
- AssetLibraryReference asset_library;
+ AssetLibraryReference asset_library_ref;
short import_type; /* eFileAssetImportType */
char _pad[6];
diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h
index 5f8a8c6230a..27376432092 100644
--- a/source/blender/makesdna/DNA_userdef_types.h
+++ b/source/blender/makesdna/DNA_userdef_types.h
@@ -71,14 +71,13 @@ typedef struct uiFontStyle {
short uifont_id;
/** Actual size depends on 'global' dpi. */
short points;
- /** Unfitted or default kerning value. */
- short kerning;
/** Style hint. */
short italic, bold;
/** Value is amount of pixels blur. */
short shadow;
/** Shadow offset in pixels. */
short shadx, shady;
+ char _pad0[2];
/** Total alpha. */
float shadowalpha;
/** 1 value, typically white or black anyway. */
@@ -645,7 +644,7 @@ typedef struct UserDef_Experimental {
char use_full_frame_compositor;
char use_sculpt_vertex_colors;
char use_sculpt_tools_tilt;
- char use_asset_browser;
+ char use_extended_asset_browser;
char use_override_templates;
char _pad[5];
/** `makesdna` does not allow empty structs. */
diff --git a/source/blender/makesdna/DNA_view3d_defaults.h b/source/blender/makesdna/DNA_view3d_defaults.h
index 9dfc37e57b1..c4d0c83b346 100644
--- a/source/blender/makesdna/DNA_view3d_defaults.h
+++ b/source/blender/makesdna/DNA_view3d_defaults.h
@@ -71,6 +71,7 @@
.gpencil_paper_opacity = 0.5f, \
.gpencil_grid_opacity = 0.9f, \
.gpencil_vertex_paint_opacity = 1.0f, \
+ .normals_constant_screen_size = 7.0f, \
}
#define _DNA_DEFAULT_View3DCursor \
diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h
index 08b29c82707..4d88f6f0c15 100644
--- a/source/blender/makesdna/DNA_view3d_types.h
+++ b/source/blender/makesdna/DNA_view3d_types.h
@@ -204,6 +204,7 @@ typedef struct View3DOverlay {
/** Edit mode settings. */
int edit_flag;
float normals_length;
+ float normals_constant_screen_size;
float backwire_opacity;
/** Paint mode settings. */
@@ -238,6 +239,8 @@ typedef struct View3DOverlay {
float gpencil_vertex_paint_opacity;
/** Handles display type for curves. */
int handle_display;
+
+ char _pad[4];
} View3DOverlay;
/* View3DOverlay->handle_display */
@@ -551,6 +554,7 @@ enum {
// V3D_OVERLAY_EDIT_CU_HANDLES = (1 << 20),
V3D_OVERLAY_EDIT_CU_NORMALS = (1 << 21),
+ V3D_OVERLAY_EDIT_CONSTANT_SCREEN_SIZE_NORMALS = (1 << 22),
};
/** #View3DOverlay.paint_flag */
diff --git a/source/blender/makesdna/DNA_workspace_types.h b/source/blender/makesdna/DNA_workspace_types.h
index 2bac040ea90..e0294d3534c 100644
--- a/source/blender/makesdna/DNA_workspace_types.h
+++ b/source/blender/makesdna/DNA_workspace_types.h
@@ -139,7 +139,7 @@ typedef struct WorkSpace {
/** Workspace-wide active asset library, for asset UIs to use (e.g. asset view UI template). The
* Asset Browser has its own and doesn't use this. */
- AssetLibraryReference asset_library;
+ AssetLibraryReference asset_library_ref;
} WorkSpace;
/**
diff --git a/source/blender/makesdna/DNA_xr_types.h b/source/blender/makesdna/DNA_xr_types.h
index fc00d5eb839..a9d427777f7 100644
--- a/source/blender/makesdna/DNA_xr_types.h
+++ b/source/blender/makesdna/DNA_xr_types.h
@@ -26,13 +26,15 @@
extern "C" {
#endif
+/* -------------------------------------------------------------------- */
+
typedef struct XrSessionSettings {
/** Shading settings, struct shared with 3D-View so settings are the same. */
struct View3DShading shading;
char _pad[7];
- char base_pose_type; /* eXRSessionBasePoseType */
+ char base_pose_type; /* #eXRSessionBasePoseType */
/** Object to take the location and rotation as base position from. */
Object *base_pose_object;
float base_pose_location[3];
@@ -68,12 +70,126 @@ typedef enum eXrActionType {
XR_VIBRATION_OUTPUT = 100,
} eXrActionType;
+/** Determines how XR action operators are executed. */
typedef enum eXrOpFlag {
XR_OP_PRESS = 0,
XR_OP_RELEASE = 1,
XR_OP_MODAL = 2,
} eXrOpFlag;
+typedef enum eXrActionFlag {
+ /** Action depends on two sub-action paths (i.e. two-handed/bi-manual action). */
+ XR_ACTION_BIMANUAL = (1 << 0),
+} eXrActionFlag;
+
+typedef enum eXrHapticFlag {
+ /** Whether to apply haptics to corresponding user paths for an action and its haptic action. */
+ XR_HAPTIC_MATCHUSERPATHS = (1 << 0),
+ /**
+ * Determines how haptics will be applied
+ * ("repeat" is mutually exclusive with "press"/"release").
+ */
+ XR_HAPTIC_PRESS = (1 << 1),
+ XR_HAPTIC_RELEASE = (1 << 2),
+ XR_HAPTIC_REPEAT = (1 << 3),
+} eXrHapticFlag;
+
+/**
+ * For axis-based inputs (thumb-stick/track-pad/etc).
+ * Determines the region for action execution (mutually exclusive per axis).
+ */
+typedef enum eXrAxisFlag {
+ XR_AXIS0_POS = (1 << 0),
+ XR_AXIS0_NEG = (1 << 1),
+ XR_AXIS1_POS = (1 << 2),
+ XR_AXIS1_NEG = (1 << 3),
+} eXrAxisFlag;
+
+typedef enum eXrPoseFlag {
+ /* Pose represents controller grip/aim. */
+ XR_POSE_GRIP = (1 << 0),
+ XR_POSE_AIM = (1 << 1),
+} eXrPoseFlag;
+
+/* -------------------------------------------------------------------- */
+
+typedef struct XrActionMapBinding {
+ struct XrActionMapBinding *next, *prev;
+
+ /** Unique name. */
+ char name[64]; /* MAX_NAME */
+
+ /** OpenXR interaction profile path. */
+ char profile[256];
+ /** OpenXR component paths. */
+ char component_path0[192];
+ char component_path1[192];
+
+ /** Input threshold/region. */
+ float float_threshold;
+ short axis_flag; /* eXrAxisFlag */
+ char _pad[2];
+
+ /** Pose action properties. */
+ float pose_location[3];
+ float pose_rotation[3];
+} XrActionMapBinding;
+
+/* -------------------------------------------------------------------- */
+
+typedef struct XrActionMapItem {
+ struct XrActionMapItem *next, *prev;
+
+ /** Unique name. */
+ char name[64]; /* MAX_NAME */
+ /** Type. */
+ char type; /** eXrActionType */
+ char _pad[7];
+
+ /** OpenXR user paths. */
+ char user_path0[64];
+ char user_path1[64];
+
+ /** Operator to be called on XR events. */
+ char op[64]; /* OP_MAX_TYPENAME */
+ /** Operator properties, assigned to ptr->data and can be written to a file. */
+ IDProperty *op_properties;
+ /** RNA pointer to access properties. */
+ struct PointerRNA *op_properties_ptr;
+
+ short op_flag; /* eXrOpFlag */
+ short action_flag; /* eXrActionFlag */
+ short haptic_flag; /* eXrHapticFlag */
+
+ /** Pose action properties. */
+ short pose_flag; /* eXrPoseFlag */
+
+ /** Haptic properties. */
+ char haptic_name[64]; /* MAX_NAME */
+ float haptic_duration;
+ float haptic_frequency;
+ float haptic_amplitude;
+
+ short selbinding;
+ char _pad3[2];
+ ListBase bindings; /* XrActionMapBinding */
+} XrActionMapItem;
+
+/* -------------------------------------------------------------------- */
+
+typedef struct XrActionMap {
+ struct XrActionMap *next, *prev;
+
+ /** Unique name. */
+ char name[64]; /* MAX_NAME */
+
+ ListBase items; /* XrActionMapItem */
+ short selitem;
+ char _pad[6];
+} XrActionMap;
+
+/* -------------------------------------------------------------------- */
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/makesdna/intern/dna_rename_defs.h b/source/blender/makesdna/intern/dna_rename_defs.h
index d363e40e4f0..2feebbfd4f4 100644
--- a/source/blender/makesdna/intern/dna_rename_defs.h
+++ b/source/blender/makesdna/intern/dna_rename_defs.h
@@ -82,11 +82,13 @@ DNA_STRUCT_RENAME_ELEM(FluidDomainSettings, guiding_vel_factor, guide_vel_factor
DNA_STRUCT_RENAME_ELEM(FluidEffectorSettings, guiding_mode, guide_mode)
DNA_STRUCT_RENAME_ELEM(Image, name, filepath)
DNA_STRUCT_RENAME_ELEM(Library, name, filepath)
+DNA_STRUCT_RENAME_ELEM(MaskLayer, restrictflag, visibility_flag)
DNA_STRUCT_RENAME_ELEM(MovieClip, name, filepath)
DNA_STRUCT_RENAME_ELEM(Object, col, color)
DNA_STRUCT_RENAME_ELEM(Object, dup_group, instance_collection)
DNA_STRUCT_RENAME_ELEM(Object, dupfacesca, instance_faces_scale)
DNA_STRUCT_RENAME_ELEM(Object, size, scale)
+DNA_STRUCT_RENAME_ELEM(Object, restrictflag, visibility_flag)
DNA_STRUCT_RENAME_ELEM(ParticleSettings, dup_group, instance_collection)
DNA_STRUCT_RENAME_ELEM(ParticleSettings, dup_ob, instance_object)
DNA_STRUCT_RENAME_ELEM(ParticleSettings, dupliweights, instance_weights)
diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c
index f2a75a60a44..061c3462a69 100644
--- a/source/blender/makesdna/intern/makesdna.c
+++ b/source/blender/makesdna/intern/makesdna.c
@@ -165,6 +165,10 @@ static char **names;
static char **types;
/** At `types_size[a]` is the size of type `a` on this systems bitness (32 or 64). */
static short *types_size_native;
+/** Contains align requirements for a struct on 32 bit systems. */
+static short *types_align_32;
+/** Contains align requirements for a struct on 64 bit systems. */
+static short *types_align_64;
/** Contains sizes as they are calculated on 32 bit systems. */
static short *types_size_32;
/** Contains sizes as they are calculated on 64 bit systems. */
@@ -406,6 +410,8 @@ static int add_type(const char *str, int size)
types_size_native[index] = size;
types_size_32[index] = size;
types_size_64[index] = size;
+ types_align_32[index] = size;
+ types_align_64[index] = size;
}
return index;
}
@@ -419,7 +425,8 @@ static int add_type(const char *str, int size)
types_size_native[types_len] = size;
types_size_32[types_len] = size;
types_size_64[types_len] = size;
-
+ types_align_32[types_len] = size;
+ types_align_64[types_len] = size;
if (types_len >= max_array_len) {
printf("too many types\n");
return types_len - 1;
@@ -966,7 +973,9 @@ static int calculate_struct_sizes(int firststruct, FILE *file_verify, const char
int size_native = 0;
int size_32 = 0;
int size_64 = 0;
- bool has_pointer = false;
+ /* Sizes of the largest field in a struct. */
+ int max_align_32 = 0;
+ int max_align_64 = 0;
/* check all elements in struct */
for (int b = 0; b < structpoin[1]; b++, sp += 2) {
@@ -995,7 +1004,6 @@ static int calculate_struct_sizes(int firststruct, FILE *file_verify, const char
/* is it a pointer or function pointer? */
if (cp[0] == '*' || cp[1] == '*') {
- has_pointer = 1;
/* has the name an extra length? (array) */
int mul = 1;
if (cp[namelen - 1] == ']') {
@@ -1042,6 +1050,8 @@ static int calculate_struct_sizes(int firststruct, FILE *file_verify, const char
size_native += sizeof(void *) * mul;
size_32 += 4 * mul;
size_64 += 8 * mul;
+ max_align_32 = MAX2(max_align_32, 4);
+ max_align_64 = MAX2(max_align_64, 8);
}
else if (cp[0] == '[') {
/* parsing can cause names "var" and "[3]"
@@ -1087,6 +1097,8 @@ static int calculate_struct_sizes(int firststruct, FILE *file_verify, const char
size_native += mul * types_size_native[type];
size_32 += mul * types_size_32[type];
size_64 += mul * types_size_64[type];
+ max_align_32 = MAX2(max_align_32, types_align_32[type]);
+ max_align_64 = MAX2(max_align_64, types_align_64[type]);
}
else {
size_native = 0;
@@ -1103,16 +1115,42 @@ static int calculate_struct_sizes(int firststruct, FILE *file_verify, const char
types_size_native[structtype] = size_native;
types_size_32[structtype] = size_32;
types_size_64[structtype] = size_64;
- /* Two ways to detect if a struct contains a pointer:
- * has_pointer is set or size_native doesn't match any of 32/64bit lengths. */
- if (has_pointer || size_64 != size_native || size_32 != size_native) {
- if (size_64 % 8) {
+ types_align_32[structtype] = max_align_32;
+ types_align_64[structtype] = max_align_64;
+
+ /* Sanity check 1: alignment should never be 0. */
+ BLI_assert(max_align_32);
+ BLI_assert(max_align_64);
+
+ /* Sanity check 2: alignment should always be equal or smaller than the maximum
+ * size of a build in type which is 8 bytes (ie int64_t or double). */
+ BLI_assert(max_align_32 <= 8);
+ BLI_assert(max_align_64 <= 8);
+
+ if (size_32 % max_align_32) {
+ /* There is an one odd case where only the 32 bit struct has alignment issues
+ * and the 64 bit does not, that can only be fixed by adding a padding pointer
+ * to the struct to resolve the problem. */
+ if ((size_64 % max_align_64 == 0) && (size_32 % max_align_32 == 4)) {
fprintf(stderr,
- "Sizeerror 8 in struct: %s (add %d bytes)\n",
+ "Sizeerror in 32 bit struct: %s (add paddding pointer)\n",
+ types[structtype]);
+ }
+ else {
+ fprintf(stderr,
+ "Sizeerror in 32 bit struct: %s (add %d bytes)\n",
types[structtype],
- size_64 % 8);
- dna_error = 1;
+ max_align_32 - (size_32 % max_align_32));
}
+ dna_error = 1;
+ }
+
+ if (size_64 % max_align_64) {
+ fprintf(stderr,
+ "Sizeerror in 64 bit struct: %s (add %d bytes)\n",
+ types[structtype],
+ max_align_64 - (size_64 % max_align_64));
+ dna_error = 1;
}
if (size_native % 4 && !ELEM(size_native, 1, 2)) {
@@ -1229,6 +1267,9 @@ static int make_structDNA(const char *base_directory,
types_size_native = MEM_callocN(sizeof(short) * max_array_len, "types_size_native");
types_size_32 = MEM_callocN(sizeof(short) * max_array_len, "types_size_32");
types_size_64 = MEM_callocN(sizeof(short) * max_array_len, "types_size_64");
+ types_align_32 = MEM_callocN(sizeof(short) * max_array_len, "types_size_32");
+ types_align_64 = MEM_callocN(sizeof(short) * max_array_len, "types_size_64");
+
structs = MEM_callocN(sizeof(short *) * max_array_len, "structs");
/* Build versioning data */
@@ -1317,7 +1358,11 @@ static int make_structDNA(const char *base_directory,
sp += 2;
/* ? num_types was elem? */
for (b = 0; b < num_types; b++, sp += 2) {
- printf(" %s %s\n", types[sp[0]], names[sp[1]]);
+ printf(" %s %s allign32:%d, allign64:%d\n",
+ types[sp[0]],
+ names[sp[1]],
+ types_align_32[sp[0]],
+ types_align_64[sp[0]]);
}
}
}
@@ -1439,6 +1484,8 @@ static int make_structDNA(const char *base_directory,
MEM_freeN(types_size_native);
MEM_freeN(types_size_32);
MEM_freeN(types_size_64);
+ MEM_freeN(types_align_32);
+ MEM_freeN(types_align_64);
MEM_freeN(structs);
BLI_memarena_free(mem_arena);
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index 97615016016..f206bde4705 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -499,6 +499,7 @@ extern StructRNA RNA_Pose;
extern StructRNA RNA_PoseBone;
extern StructRNA RNA_Preferences;
extern StructRNA RNA_PreferencesEdit;
+extern StructRNA RNA_PreferencesExperimental;
extern StructRNA RNA_PreferencesFilePaths;
extern StructRNA RNA_PreferencesInput;
extern StructRNA RNA_PreferencesKeymap;
@@ -827,6 +828,7 @@ unsigned int RNA_struct_count_properties(StructRNA *srna);
/* lower level functions for access to type properties */
const struct ListBase *RNA_struct_type_properties(StructRNA *srna);
+PropertyRNA *RNA_struct_type_find_property_no_base(StructRNA *srna, const char *identifier);
PropertyRNA *RNA_struct_type_find_property(StructRNA *srna, const char *identifier);
FunctionRNA *RNA_struct_find_function(StructRNA *srna, const char *identifier);
diff --git a/source/blender/makesrna/RNA_enum_items.h b/source/blender/makesrna/RNA_enum_items.h
new file mode 100644
index 00000000000..c8f44262020
--- /dev/null
+++ b/source/blender/makesrna/RNA_enum_items.h
@@ -0,0 +1,240 @@
+/*
+ * This program is free software you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup RNA
+ */
+
+/* NOTE: this is included multiple times with different #defines for DEF_ENUM. */
+
+/* use in cases where only dynamic types are used */
+DEF_ENUM(DummyRNA_NULL_items)
+DEF_ENUM(DummyRNA_DEFAULT_items)
+
+/* all others should follow 'rna_enum_*_items' naming */
+DEF_ENUM(rna_enum_id_type_items)
+
+DEF_ENUM(rna_enum_object_mode_items)
+DEF_ENUM(rna_enum_workspace_object_mode_items)
+DEF_ENUM(rna_enum_object_empty_drawtype_items)
+DEF_ENUM(rna_enum_object_gpencil_type_items)
+DEF_ENUM(rna_enum_metaelem_type_items)
+
+DEF_ENUM(rna_enum_proportional_falloff_items)
+DEF_ENUM(rna_enum_proportional_falloff_curve_only_items)
+DEF_ENUM(rna_enum_snap_target_items)
+DEF_ENUM(rna_enum_snap_element_items)
+DEF_ENUM(rna_enum_snap_node_element_items)
+DEF_ENUM(rna_enum_curve_fit_method_items)
+DEF_ENUM(rna_enum_mesh_select_mode_items)
+DEF_ENUM(rna_enum_mesh_select_mode_uv_items)
+DEF_ENUM(rna_enum_mesh_delimit_mode_items)
+DEF_ENUM(rna_enum_space_graph_mode_items)
+DEF_ENUM(rna_enum_space_file_browse_mode_items)
+DEF_ENUM(rna_enum_space_sequencer_view_type_items)
+DEF_ENUM(rna_enum_space_type_items)
+DEF_ENUM(rna_enum_space_image_mode_items)
+DEF_ENUM(rna_enum_space_image_mode_all_items)
+DEF_ENUM(rna_enum_space_action_mode_items)
+DEF_ENUM(rna_enum_fileselect_params_sort_items)
+DEF_ENUM(rna_enum_region_type_items)
+DEF_ENUM(rna_enum_object_modifier_type_items)
+DEF_ENUM(rna_enum_constraint_type_items)
+DEF_ENUM(rna_enum_boidrule_type_items)
+DEF_ENUM(rna_enum_sequence_modifier_type_items)
+DEF_ENUM(rna_enum_object_greasepencil_modifier_type_items)
+DEF_ENUM(rna_enum_object_shaderfx_type_items)
+
+DEF_ENUM(rna_enum_modifier_triangulate_quad_method_items)
+DEF_ENUM(rna_enum_modifier_triangulate_ngon_method_items)
+DEF_ENUM(rna_enum_modifier_shrinkwrap_mode_items)
+
+DEF_ENUM(rna_enum_image_type_items)
+DEF_ENUM(rna_enum_image_color_mode_items)
+DEF_ENUM(rna_enum_image_color_depth_items)
+DEF_ENUM(rna_enum_image_generated_type_items)
+
+DEF_ENUM(rna_enum_normal_space_items)
+DEF_ENUM(rna_enum_normal_swizzle_items)
+DEF_ENUM(rna_enum_bake_save_mode_items)
+DEF_ENUM(rna_enum_bake_target_items)
+
+DEF_ENUM(rna_enum_views_format_items)
+DEF_ENUM(rna_enum_views_format_multilayer_items)
+DEF_ENUM(rna_enum_views_format_multiview_items)
+DEF_ENUM(rna_enum_stereo3d_display_items)
+DEF_ENUM(rna_enum_stereo3d_anaglyph_type_items)
+DEF_ENUM(rna_enum_stereo3d_interlace_type_items)
+
+#ifdef WITH_OPENEXR
+DEF_ENUM(rna_enum_exr_codec_items)
+#endif
+DEF_ENUM(rna_enum_color_sets_items)
+
+DEF_ENUM(rna_enum_beztriple_keyframe_type_items)
+DEF_ENUM(rna_enum_beztriple_interpolation_mode_items)
+DEF_ENUM(rna_enum_beztriple_interpolation_easing_items)
+DEF_ENUM(rna_enum_fcurve_auto_smoothing_items)
+DEF_ENUM(rna_enum_keyframe_handle_type_items)
+DEF_ENUM(rna_enum_driver_target_rotation_mode_items)
+
+DEF_ENUM(rna_enum_keyingset_path_grouping_items)
+DEF_ENUM(rna_enum_keying_flag_items)
+DEF_ENUM(rna_enum_keying_flag_items_api)
+
+DEF_ENUM(rna_enum_fmodifier_type_items)
+
+DEF_ENUM(rna_enum_motionpath_bake_location_items)
+
+DEF_ENUM(rna_enum_event_value_all_items)
+DEF_ENUM(rna_enum_event_value_keymouse_items)
+DEF_ENUM(rna_enum_event_value_tweak_items)
+
+DEF_ENUM(rna_enum_event_type_items)
+DEF_ENUM(rna_enum_event_type_mask_items)
+
+DEF_ENUM(rna_enum_operator_type_flag_items)
+DEF_ENUM(rna_enum_operator_return_items)
+DEF_ENUM(rna_enum_operator_property_tags)
+
+DEF_ENUM(rna_enum_brush_sculpt_tool_items)
+DEF_ENUM(rna_enum_brush_uv_sculpt_tool_items)
+DEF_ENUM(rna_enum_brush_vertex_tool_items)
+DEF_ENUM(rna_enum_brush_weight_tool_items)
+DEF_ENUM(rna_enum_brush_gpencil_types_items)
+DEF_ENUM(rna_enum_brush_gpencil_vertex_types_items)
+DEF_ENUM(rna_enum_brush_gpencil_sculpt_types_items)
+DEF_ENUM(rna_enum_brush_gpencil_weight_types_items)
+DEF_ENUM(rna_enum_brush_image_tool_items)
+
+DEF_ENUM(rna_enum_axis_xy_items)
+DEF_ENUM(rna_enum_axis_xyz_items)
+
+DEF_ENUM(rna_enum_axis_flag_xyz_items)
+
+DEF_ENUM(rna_enum_symmetrize_direction_items)
+
+DEF_ENUM(rna_enum_texture_type_items)
+
+DEF_ENUM(rna_enum_light_type_items)
+
+DEF_ENUM(rna_enum_lightprobes_type_items)
+
+DEF_ENUM(rna_enum_unpack_method_items)
+
+DEF_ENUM(rna_enum_object_type_items)
+DEF_ENUM(rna_enum_object_rotation_mode_items)
+
+DEF_ENUM(rna_enum_object_type_curve_items)
+
+DEF_ENUM(rna_enum_rigidbody_object_type_items)
+DEF_ENUM(rna_enum_rigidbody_object_shape_items)
+DEF_ENUM(rna_enum_rigidbody_constraint_type_items)
+
+DEF_ENUM(rna_enum_object_axis_items)
+
+DEF_ENUM(rna_enum_render_pass_type_items)
+
+DEF_ENUM(rna_enum_bake_pass_type_items)
+DEF_ENUM(rna_enum_bake_pass_filter_type_items)
+
+DEF_ENUM(rna_enum_keymap_propvalue_items)
+
+DEF_ENUM(rna_enum_operator_context_items)
+
+DEF_ENUM(rna_enum_wm_report_items)
+
+DEF_ENUM(rna_enum_property_type_items)
+DEF_ENUM(rna_enum_property_subtype_items)
+DEF_ENUM(rna_enum_property_unit_items)
+
+DEF_ENUM(rna_enum_shading_type_items)
+
+DEF_ENUM(rna_enum_navigation_mode_items)
+
+DEF_ENUM(rna_enum_node_socket_in_out_items)
+
+DEF_ENUM(rna_enum_node_math_items)
+DEF_ENUM(rna_enum_mapping_type_items)
+DEF_ENUM(rna_enum_node_vec_math_items)
+DEF_ENUM(rna_enum_node_boolean_math_items)
+DEF_ENUM(rna_enum_node_float_compare_items)
+DEF_ENUM(rna_enum_node_filter_items)
+DEF_ENUM(rna_enum_node_float_to_int_items)
+DEF_ENUM(rna_enum_node_map_range_items)
+DEF_ENUM(rna_enum_node_clamp_items)
+
+DEF_ENUM(rna_enum_ramp_blend_items)
+
+DEF_ENUM(rna_enum_prop_dynamicpaint_type_items)
+
+DEF_ENUM(rna_enum_clip_editor_mode_items)
+
+DEF_ENUM(rna_enum_icon_items)
+DEF_ENUM(rna_enum_uilist_layout_type_items)
+
+DEF_ENUM(rna_enum_linestyle_color_modifier_type_items)
+DEF_ENUM(rna_enum_linestyle_alpha_modifier_type_items)
+DEF_ENUM(rna_enum_linestyle_thickness_modifier_type_items)
+DEF_ENUM(rna_enum_linestyle_geometry_modifier_type_items)
+
+DEF_ENUM(rna_enum_window_cursor_items)
+
+DEF_ENUM(rna_enum_dt_method_vertex_items)
+DEF_ENUM(rna_enum_dt_method_edge_items)
+DEF_ENUM(rna_enum_dt_method_loop_items)
+DEF_ENUM(rna_enum_dt_method_poly_items)
+DEF_ENUM(rna_enum_dt_mix_mode_items)
+DEF_ENUM(rna_enum_dt_layers_select_src_items)
+DEF_ENUM(rna_enum_dt_layers_select_dst_items)
+
+DEF_ENUM(rna_enum_context_mode_items)
+
+DEF_ENUM(rna_enum_preference_section_items)
+
+DEF_ENUM(rna_enum_attribute_type_items)
+DEF_ENUM(rna_enum_attribute_type_with_auto_items)
+DEF_ENUM(rna_enum_attribute_domain_items)
+DEF_ENUM(rna_enum_attribute_domain_with_auto_items)
+
+DEF_ENUM(rna_enum_collection_color_items)
+
+DEF_ENUM(rna_enum_subdivision_uv_smooth_items)
+DEF_ENUM(rna_enum_subdivision_boundary_smooth_items)
+
+DEF_ENUM(rna_enum_transform_orientation_items)
+
+/* Not available to RNA pre-processing (`makrsrna`).
+ * Defined in editors for example. */
+#ifndef RNA_MAKESRNA
+
+DEF_ENUM(rna_enum_particle_edit_hair_brush_items)
+DEF_ENUM(rna_enum_particle_edit_disconnected_hair_brush_items)
+
+DEF_ENUM(rna_enum_keyframe_paste_offset_items)
+DEF_ENUM(rna_enum_keyframe_paste_merge_items)
+
+DEF_ENUM(rna_enum_transform_pivot_items_full)
+DEF_ENUM(rna_enum_transform_mode_types)
+
+/* In the runtime part of RNA, could be removed from this section. */
+DEF_ENUM(rna_enum_nla_mode_extend_items)
+DEF_ENUM(rna_enum_nla_mode_blend_items)
+DEF_ENUM(rna_enum_keyblock_type_items)
+
+#endif
+
+#undef DEF_ENUM
diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h
index d544083a749..d7520834287 100644
--- a/source/blender/makesrna/RNA_enum_types.h
+++ b/source/blender/makesrna/RNA_enum_types.h
@@ -32,219 +32,11 @@ struct bNodeTreeType;
struct bNodeType;
/* Types */
+#define DEF_ENUM(id) extern const EnumPropertyItem id[];
+#include "RNA_enum_items.h"
-/* use in cases where only dynamic types are used */
-extern const EnumPropertyItem DummyRNA_NULL_items[];
-extern const EnumPropertyItem DummyRNA_DEFAULT_items[];
-
-/* all others should follow 'rna_enum_*_items' naming */
-extern const EnumPropertyItem rna_enum_id_type_items[];
-
-extern const EnumPropertyItem rna_enum_object_mode_items[];
-extern const EnumPropertyItem rna_enum_workspace_object_mode_items[];
-extern const EnumPropertyItem rna_enum_object_empty_drawtype_items[];
-extern const EnumPropertyItem rna_enum_object_gpencil_type_items[];
-extern const EnumPropertyItem rna_enum_metaelem_type_items[];
-
-extern const EnumPropertyItem rna_enum_proportional_falloff_items[];
-extern const EnumPropertyItem rna_enum_proportional_falloff_curve_only_items[];
-extern const EnumPropertyItem rna_enum_snap_target_items[];
-extern const EnumPropertyItem rna_enum_snap_element_items[];
-extern const EnumPropertyItem rna_enum_snap_node_element_items[];
-extern const EnumPropertyItem rna_enum_curve_fit_method_items[];
-extern const EnumPropertyItem rna_enum_mesh_select_mode_items[];
-extern const EnumPropertyItem rna_enum_mesh_select_mode_uv_items[];
-extern const EnumPropertyItem rna_enum_mesh_delimit_mode_items[];
-extern const EnumPropertyItem rna_enum_space_graph_mode_items[];
-extern const EnumPropertyItem rna_enum_space_file_browse_mode_items[];
-extern const EnumPropertyItem rna_enum_space_sequencer_view_type_items[];
-extern const EnumPropertyItem rna_enum_space_type_items[];
-extern const EnumPropertyItem rna_enum_space_image_mode_items[];
-extern const EnumPropertyItem rna_enum_space_image_mode_all_items[];
-extern const EnumPropertyItem rna_enum_space_action_mode_items[];
-extern const EnumPropertyItem rna_enum_fileselect_params_sort_items[];
-extern const EnumPropertyItem rna_enum_region_type_items[];
-extern const EnumPropertyItem rna_enum_object_modifier_type_items[];
-extern const EnumPropertyItem rna_enum_constraint_type_items[];
-extern const EnumPropertyItem rna_enum_boidrule_type_items[];
-extern const EnumPropertyItem rna_enum_sequence_modifier_type_items[];
-extern const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[];
-extern const EnumPropertyItem rna_enum_object_shaderfx_type_items[];
-
-extern const EnumPropertyItem rna_enum_modifier_triangulate_quad_method_items[];
-extern const EnumPropertyItem rna_enum_modifier_triangulate_ngon_method_items[];
-extern const EnumPropertyItem rna_enum_modifier_shrinkwrap_mode_items[];
-
-extern const EnumPropertyItem rna_enum_image_type_items[];
-extern const EnumPropertyItem rna_enum_image_color_mode_items[];
-extern const EnumPropertyItem rna_enum_image_color_depth_items[];
-extern const EnumPropertyItem rna_enum_image_generated_type_items[];
-
-extern const EnumPropertyItem rna_enum_normal_space_items[];
-extern const EnumPropertyItem rna_enum_normal_swizzle_items[];
-extern const EnumPropertyItem rna_enum_bake_save_mode_items[];
-extern const EnumPropertyItem rna_enum_bake_target_items[];
-
-extern const EnumPropertyItem rna_enum_views_format_items[];
-extern const EnumPropertyItem rna_enum_views_format_multilayer_items[];
-extern const EnumPropertyItem rna_enum_views_format_multiview_items[];
-extern const EnumPropertyItem rna_enum_stereo3d_display_items[];
-extern const EnumPropertyItem rna_enum_stereo3d_anaglyph_type_items[];
-extern const EnumPropertyItem rna_enum_stereo3d_interlace_type_items[];
-
-extern const EnumPropertyItem rna_enum_exr_codec_items[];
-extern const EnumPropertyItem rna_enum_color_sets_items[];
-
-extern const EnumPropertyItem rna_enum_beztriple_keyframe_type_items[];
-extern const EnumPropertyItem rna_enum_beztriple_interpolation_mode_items[];
-extern const EnumPropertyItem rna_enum_beztriple_interpolation_easing_items[];
-extern const EnumPropertyItem rna_enum_fcurve_auto_smoothing_items[];
-extern const EnumPropertyItem rna_enum_keyframe_handle_type_items[];
-extern const EnumPropertyItem rna_enum_driver_target_rotation_mode_items[];
-
-extern const EnumPropertyItem rna_enum_keyblock_type_items[];
-
-extern const EnumPropertyItem rna_enum_keyingset_path_grouping_items[];
-extern const EnumPropertyItem rna_enum_keying_flag_items[];
-extern const EnumPropertyItem rna_enum_keying_flag_items_api[];
-
-extern const EnumPropertyItem rna_enum_keyframe_paste_offset_items[];
-extern const EnumPropertyItem rna_enum_keyframe_paste_merge_items[];
-
-extern const EnumPropertyItem rna_enum_fmodifier_type_items[];
-
-extern const EnumPropertyItem rna_enum_nla_mode_extend_items[];
-extern const EnumPropertyItem rna_enum_nla_mode_blend_items[];
-
-extern const EnumPropertyItem rna_enum_motionpath_bake_location_items[];
-
-extern const EnumPropertyItem rna_enum_event_value_all_items[];
-extern const EnumPropertyItem rna_enum_event_value_keymouse_items[];
-extern const EnumPropertyItem rna_enum_event_value_tweak_items[];
-
-extern const EnumPropertyItem rna_enum_event_type_items[];
-extern const EnumPropertyItem rna_enum_event_type_mask_items[];
-
-extern const EnumPropertyItem rna_enum_operator_type_flag_items[];
-extern const EnumPropertyItem rna_enum_operator_return_items[];
-extern const EnumPropertyItem rna_enum_operator_property_tags[];
-
-extern const EnumPropertyItem rna_enum_brush_sculpt_tool_items[];
-extern const EnumPropertyItem rna_enum_brush_uv_sculpt_tool_items[];
-extern const EnumPropertyItem rna_enum_brush_vertex_tool_items[];
-extern const EnumPropertyItem rna_enum_brush_weight_tool_items[];
-extern const EnumPropertyItem rna_enum_brush_gpencil_types_items[];
-extern const EnumPropertyItem rna_enum_brush_gpencil_vertex_types_items[];
-extern const EnumPropertyItem rna_enum_brush_gpencil_sculpt_types_items[];
-extern const EnumPropertyItem rna_enum_brush_gpencil_weight_types_items[];
-extern const EnumPropertyItem rna_enum_brush_image_tool_items[];
-
-extern const EnumPropertyItem rna_enum_particle_edit_hair_brush_items[];
-extern const EnumPropertyItem rna_enum_particle_edit_disconnected_hair_brush_items[];
-
-extern const EnumPropertyItem rna_enum_uv_sculpt_tool_items[];
-
-extern const EnumPropertyItem rna_enum_axis_xy_items[];
-extern const EnumPropertyItem rna_enum_axis_xyz_items[];
-
-extern const EnumPropertyItem rna_enum_axis_flag_xyz_items[];
-
-extern const EnumPropertyItem rna_enum_symmetrize_direction_items[];
-
-extern const EnumPropertyItem rna_enum_texture_type_items[];
-
-extern const EnumPropertyItem rna_enum_light_type_items[];
-
-extern const EnumPropertyItem rna_enum_lightprobes_type_items[];
-
-extern const EnumPropertyItem rna_enum_unpack_method_items[];
-
-extern const EnumPropertyItem rna_enum_object_type_items[];
-extern const EnumPropertyItem rna_enum_object_rotation_mode_items[];
-
-extern const EnumPropertyItem rna_enum_object_type_curve_items[];
-
-extern const EnumPropertyItem rna_enum_rigidbody_object_type_items[];
-extern const EnumPropertyItem rna_enum_rigidbody_object_shape_items[];
-extern const EnumPropertyItem rna_enum_rigidbody_constraint_type_items[];
-
-extern const EnumPropertyItem rna_enum_object_axis_items[];
-
-extern const EnumPropertyItem rna_enum_controller_type_items[];
-
-extern const EnumPropertyItem rna_enum_render_pass_type_items[];
-extern const EnumPropertyItem rna_enum_render_pass_debug_type_items[];
-
-extern const EnumPropertyItem rna_enum_bake_pass_type_items[];
-extern const EnumPropertyItem rna_enum_bake_pass_filter_type_items[];
-
-extern const EnumPropertyItem rna_enum_keymap_propvalue_items[];
-
-extern const EnumPropertyItem rna_enum_operator_context_items[];
-
-extern const EnumPropertyItem rna_enum_wm_report_items[];
-
-extern const EnumPropertyItem rna_enum_transform_pivot_items_full[];
-extern const EnumPropertyItem rna_enum_transform_orientation_items[];
-extern const EnumPropertyItem rna_enum_transform_mode_types[];
-
-extern const EnumPropertyItem rna_enum_property_type_items[];
-extern const EnumPropertyItem rna_enum_property_subtype_items[];
-extern const EnumPropertyItem rna_enum_property_unit_items[];
-
-extern const EnumPropertyItem rna_enum_shading_type_items[];
-
-extern const EnumPropertyItem rna_enum_navigation_mode_items[];
-
-extern const EnumPropertyItem rna_enum_node_socket_in_out_items[];
-
-extern const EnumPropertyItem rna_enum_node_math_items[];
-extern const EnumPropertyItem rna_enum_mapping_type_items[];
-extern const EnumPropertyItem rna_enum_node_vec_math_items[];
-extern const EnumPropertyItem rna_enum_node_boolean_math_items[];
-extern const EnumPropertyItem rna_enum_node_float_compare_items[];
-extern const EnumPropertyItem rna_enum_node_filter_items[];
-extern const EnumPropertyItem rna_enum_node_float_to_int_items[];
-extern const EnumPropertyItem rna_enum_node_map_range_items[];
-extern const EnumPropertyItem rna_enum_node_clamp_items[];
-
-extern const EnumPropertyItem rna_enum_ramp_blend_items[];
-
-extern const EnumPropertyItem rna_enum_prop_dynamicpaint_type_items[];
-
-extern const EnumPropertyItem rna_enum_clip_editor_mode_items[];
-
-extern const EnumPropertyItem rna_enum_icon_items[];
-extern const EnumPropertyItem rna_enum_uilist_layout_type_items[];
-
-extern const EnumPropertyItem rna_enum_linestyle_color_modifier_type_items[];
-extern const EnumPropertyItem rna_enum_linestyle_alpha_modifier_type_items[];
-extern const EnumPropertyItem rna_enum_linestyle_thickness_modifier_type_items[];
-extern const EnumPropertyItem rna_enum_linestyle_geometry_modifier_type_items[];
-
-extern const EnumPropertyItem rna_enum_window_cursor_items[];
-
-extern const EnumPropertyItem rna_enum_dt_method_vertex_items[];
-extern const EnumPropertyItem rna_enum_dt_method_edge_items[];
-extern const EnumPropertyItem rna_enum_dt_method_loop_items[];
-extern const EnumPropertyItem rna_enum_dt_method_poly_items[];
-extern const EnumPropertyItem rna_enum_dt_mix_mode_items[];
-extern const EnumPropertyItem rna_enum_dt_layers_select_src_items[];
-extern const EnumPropertyItem rna_enum_dt_layers_select_dst_items[];
-
-extern const EnumPropertyItem rna_enum_context_mode_items[];
-
-extern const EnumPropertyItem rna_enum_curveprofile_preset_items[];
-extern const EnumPropertyItem rna_enum_preference_section_items[];
-
-extern const EnumPropertyItem rna_enum_attribute_type_items[];
-extern const EnumPropertyItem rna_enum_attribute_type_with_auto_items[];
-extern const EnumPropertyItem rna_enum_attribute_domain_items[];
-extern const EnumPropertyItem rna_enum_attribute_domain_with_auto_items[];
extern const EnumPropertyItem *rna_enum_attribute_domain_itemf(struct ID *id, bool *r_free);
-extern const EnumPropertyItem rna_enum_collection_color_items[];
-
/**
* For ID filters (#FILTER_ID_AC, #FILTER_ID_AR, ...) an int isn't enough. This version allows 64
* bit integers. So can't use the regular #EnumPropertyItem. Would be nice if RNA supported this
diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt
index 95b7b7e5406..7e6d0aea2ee 100644
--- a/source/blender/makesrna/intern/CMakeLists.txt
+++ b/source/blender/makesrna/intern/CMakeLists.txt
@@ -175,6 +175,7 @@ set(SRC_RNA_INC
../RNA_access.h
../RNA_define.h
../RNA_documentation.h
+ ../RNA_enum_items.h
../RNA_enum_types.h
../RNA_types.h
)
diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c
index 719b0f73a9d..36f19907080 100644
--- a/source/blender/makesrna/intern/makesrna.c
+++ b/source/blender/makesrna/intern/makesrna.c
@@ -582,6 +582,23 @@ static int rna_color_quantize(PropertyRNA *prop, PropertyDefRNA *dp)
(IS_DNATYPE_FLOAT_COMPAT(dp->dnatype) == 0));
}
+/**
+ * Return the identifier for an enum which is defined in "RNA_enum_items.h".
+ *
+ * Prevents expanding duplicate enums bloating the binary size.
+ */
+static const char *rna_enum_id_from_pointer(const EnumPropertyItem *item)
+{
+#define RNA_MAKESRNA
+#define DEF_ENUM(id) \
+ if (item == id) { \
+ return STRINGIFY(id); \
+ }
+#include "RNA_enum_items.h"
+#undef RNA_MAKESRNA
+ return NULL;
+}
+
static const char *rna_function_string(const void *func)
{
return (func) ? (const char *)func : "NULL";
@@ -3675,37 +3692,55 @@ static void rna_generate_property(FILE *f, StructRNA *srna, const char *nest, Pr
int i, defaultfound = 0, totflag = 0;
if (eprop->item) {
- fprintf(f,
- "static const EnumPropertyItem rna_%s%s_%s_items[%d] = {\n\t",
- srna->identifier,
- strnest,
- prop->identifier,
- eprop->totitem + 1);
-
- for (i = 0; i < eprop->totitem; i++) {
- fprintf(f, "{%d, ", eprop->item[i].value);
- rna_print_c_string(f, eprop->item[i].identifier);
- fprintf(f, ", ");
- fprintf(f, "%d, ", eprop->item[i].icon);
- rna_print_c_string(f, eprop->item[i].name);
- fprintf(f, ", ");
- rna_print_c_string(f, eprop->item[i].description);
- fprintf(f, "},\n\t");
-
- if (eprop->item[i].identifier[0]) {
- if (prop->flag & PROP_ENUM_FLAG) {
- totflag |= eprop->item[i].value;
+ /* Inline the enum if this is not a defined in "RNA_enum_items.h". */
+ const char *item_global_id = rna_enum_id_from_pointer(eprop->item);
+ if (item_global_id == NULL) {
+ fprintf(f,
+ "static const EnumPropertyItem rna_%s%s_%s_items[%d] = {\n\t",
+ srna->identifier,
+ strnest,
+ prop->identifier,
+ eprop->totitem + 1);
+
+ for (i = 0; i < eprop->totitem; i++) {
+ fprintf(f, "{%d, ", eprop->item[i].value);
+ rna_print_c_string(f, eprop->item[i].identifier);
+ fprintf(f, ", ");
+ fprintf(f, "%d, ", eprop->item[i].icon);
+ rna_print_c_string(f, eprop->item[i].name);
+ fprintf(f, ", ");
+ rna_print_c_string(f, eprop->item[i].description);
+ fprintf(f, "},\n\t");
+
+ if (eprop->item[i].identifier[0]) {
+ if (prop->flag & PROP_ENUM_FLAG) {
+ totflag |= eprop->item[i].value;
+ }
+ else {
+ if (eprop->defaultvalue == eprop->item[i].value) {
+ defaultfound = 1;
+ }
+ }
}
- else {
- if (eprop->defaultvalue == eprop->item[i].value) {
- defaultfound = 1;
+ }
+
+ fprintf(f, "{0, NULL, 0, NULL, NULL}\n};\n\n");
+ }
+ else {
+ for (i = 0; i < eprop->totitem; i++) {
+ if (eprop->item[i].identifier[0]) {
+ if (prop->flag & PROP_ENUM_FLAG) {
+ totflag |= eprop->item[i].value;
+ }
+ else {
+ if (eprop->defaultvalue == eprop->item[i].value) {
+ defaultfound = 1;
+ }
}
}
}
}
- fprintf(f, "{0, NULL, 0, NULL, NULL}\n};\n\n");
-
if (prop->flag & PROP_ENUM_FLAG) {
if (eprop->defaultvalue & ~totflag) {
CLOG_ERROR(&LOG,
@@ -4047,7 +4082,13 @@ static void rna_generate_property(FILE *f, StructRNA *srna, const char *nest, Pr
rna_function_string(eprop->get_ex),
rna_function_string(eprop->set_ex));
if (eprop->item) {
- fprintf(f, "rna_%s%s_%s_items, ", srna->identifier, strnest, prop->identifier);
+ const char *item_global_id = rna_enum_id_from_pointer(eprop->item);
+ if (item_global_id != NULL) {
+ fprintf(f, "%s, ", item_global_id);
+ }
+ else {
+ fprintf(f, "rna_%s%s_%s_items, ", srna->identifier, strnest, prop->identifier);
+ }
}
else {
fprintf(f, "NULL, ");
diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c
index 6df03d19538..8f8ad077935 100644
--- a/source/blender/makesrna/intern/rna_ID.c
+++ b/source/blender/makesrna/intern/rna_ID.c
@@ -741,6 +741,58 @@ static void rna_ID_override_template_create(ID *id, ReportList *reports)
BKE_lib_override_library_template_create(id);
}
+static void rna_ID_override_library_operations_update(ID *id,
+ IDOverrideLibrary *UNUSED(override_library),
+ Main *bmain,
+ ReportList *reports)
+{
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ BKE_reportf(reports, RPT_ERROR, "ID '%s' isn't an override", id->name);
+ return;
+ }
+
+ BKE_lib_override_library_operations_create(bmain, id);
+}
+
+static void rna_ID_override_library_reset(ID *id,
+ IDOverrideLibrary *UNUSED(override_library),
+ Main *bmain,
+ ReportList *reports,
+ bool do_hierarchy)
+{
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ BKE_reportf(reports, RPT_ERROR, "ID '%s' isn't an override", id->name);
+ return;
+ }
+
+ if (do_hierarchy) {
+ BKE_lib_override_library_id_hierarchy_reset(bmain, id);
+ }
+ else {
+ BKE_lib_override_library_id_reset(bmain, id);
+ }
+}
+
+static void rna_ID_override_library_destroy(ID *id,
+ IDOverrideLibrary *UNUSED(override_library),
+ Main *bmain,
+ ReportList *reports,
+ bool do_hierarchy)
+{
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ BKE_reportf(reports, RPT_ERROR, "ID '%s' isn't an override", id->name);
+ return;
+ }
+
+ if (do_hierarchy) {
+ BKE_lib_override_library_delete(bmain, id);
+ }
+ else {
+ BKE_libblock_remap(bmain, id, id->override_library->reference, ID_REMAP_SKIP_INDIRECT_USAGE);
+ BKE_id_delete(bmain, id);
+ }
+}
+
static IDOverrideLibraryProperty *rna_ID_override_library_properties_add(
IDOverrideLibrary *override_library, ReportList *reports, const char rna_path[])
{
@@ -755,6 +807,18 @@ static IDOverrideLibraryProperty *rna_ID_override_library_properties_add(
return result;
}
+static void rna_ID_override_library_properties_remove(IDOverrideLibrary *override_library,
+ ReportList *reports,
+ IDOverrideLibraryProperty *override_property)
+{
+ if (BLI_findindex(&override_library->properties, override_property) == -1) {
+ BKE_report(reports, RPT_ERROR, "Override property cannot be removed");
+ return;
+ }
+
+ BKE_lib_override_library_property_delete(override_library, override_property);
+}
+
static IDOverrideLibraryPropertyOperation *rna_ID_override_library_property_operations_add(
IDOverrideLibraryProperty *override_property,
ReportList *reports,
@@ -782,6 +846,19 @@ static IDOverrideLibraryPropertyOperation *rna_ID_override_library_property_oper
return result;
}
+static void rna_ID_override_library_property_operations_remove(
+ IDOverrideLibraryProperty *override_property,
+ ReportList *reports,
+ IDOverrideLibraryPropertyOperation *override_operation)
+{
+ if (BLI_findindex(&override_property->operations, override_operation) == -1) {
+ BKE_report(reports, RPT_ERROR, "Override operation cannot be removed");
+ return;
+ }
+
+ BKE_lib_override_library_property_operation_delete(override_property, override_operation);
+}
+
static void rna_ID_update_tag(ID *id, Main *bmain, ReportList *reports, int flag)
{
/* XXX, new function for this! */
@@ -1633,6 +1710,16 @@ static void rna_def_ID_override_library_property_operations(BlenderRNA *brna, Pr
"New Operation",
"Created operation");
RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "remove", "rna_ID_override_library_property_operations_remove");
+ RNA_def_function_ui_description(func, "Remove and delete an operation");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ parm = RNA_def_pointer(func,
+ "operation",
+ "IDOverrideLibraryPropertyOperation",
+ "Operation",
+ "Override operation to be deleted");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
}
static void rna_def_ID_override_library_property(BlenderRNA *brna)
@@ -1689,12 +1776,23 @@ static void rna_def_ID_override_library_properties(BlenderRNA *brna, PropertyRNA
parm = RNA_def_string(
func, "rna_path", NULL, 256, "RNA Path", "RNA-Path of the property to add");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+
+ func = RNA_def_function(srna, "remove", "rna_ID_override_library_properties_remove");
+ RNA_def_function_ui_description(func, "Remove and delete a property");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ parm = RNA_def_pointer(func,
+ "property",
+ "IDOverrideLibraryProperty",
+ "Property",
+ "Override property to be deleted");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
}
static void rna_def_ID_override_library(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
+ FunctionRNA *func;
srna = RNA_def_struct(brna, "IDOverrideLibrary", NULL);
RNA_def_struct_ui_text(
@@ -1710,6 +1808,35 @@ static void rna_def_ID_override_library(BlenderRNA *brna)
"List of overridden properties");
rna_def_ID_override_library_properties(brna, prop);
+ /* Update function. */
+ func = RNA_def_function(srna, "operations_update", "rna_ID_override_library_operations_update");
+ RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_SELF_ID | FUNC_USE_REPORTS);
+ RNA_def_function_ui_description(func,
+ "Update the library override operations based on the "
+ "differences between this override ID and its reference");
+
+ func = RNA_def_function(srna, "reset", "rna_ID_override_library_reset");
+ RNA_def_function_ui_description(func,
+ "Reset this override to match again its linked reference ID");
+ RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_SELF_ID | FUNC_USE_REPORTS);
+ RNA_def_boolean(
+ func,
+ "do_hierarchy",
+ true,
+ "",
+ "Also reset all the dependencies of this override to match their reference linked IDs");
+
+ func = RNA_def_function(srna, "destroy", "rna_ID_override_library_destroy");
+ RNA_def_function_ui_description(
+ func, "Delete this override ID and remap its usages to its linked reference ID instead");
+ RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_SELF_ID | FUNC_USE_REPORTS);
+ RNA_def_boolean(func,
+ "do_hierarchy",
+ true,
+ "",
+ "Also delete all the dependencies of this override and remap their usages to "
+ "their reference linked IDs");
+
rna_def_ID_override_library_property(brna);
}
diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c
index feacd47c98c..41c31dfebcb 100644
--- a/source/blender/makesrna/intern/rna_access.c
+++ b/source/blender/makesrna/intern/rna_access.c
@@ -36,6 +36,7 @@
#include "BLI_dynstr.h"
#include "BLI_ghash.h"
#include "BLI_math.h"
+#include "BLI_threads.h"
#include "BLI_utildefines.h"
#include "BLF_api.h"
@@ -882,8 +883,7 @@ bool RNA_struct_is_a(const StructRNA *type, const StructRNA *srna)
PropertyRNA *RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
{
- if (identifier[0] == '[' && identifier[1] == '"') { /* " (dummy comment to avoid confusing some
- * function lists in text editors) */
+ if (identifier[0] == '[' && identifier[1] == '"') {
/* id prop lookup, not so common */
PropertyRNA *r_prop = NULL;
PointerRNA r_ptr; /* only support single level props */
@@ -967,19 +967,33 @@ const struct ListBase *RNA_struct_type_properties(StructRNA *srna)
return &srna->cont.properties;
}
-PropertyRNA *RNA_struct_type_find_property(StructRNA *srna, const char *identifier)
+PropertyRNA *RNA_struct_type_find_property_no_base(StructRNA *srna, const char *identifier)
{
return BLI_findstring_ptr(&srna->cont.properties, identifier, offsetof(PropertyRNA, identifier));
}
+/**
+ * \note #RNA_struct_find_property is a higher level alternative to this function
+ * which takes a #PointerRNA instead of a #StructRNA.
+ */
+PropertyRNA *RNA_struct_type_find_property(StructRNA *srna, const char *identifier)
+{
+ for (; srna; srna = srna->base) {
+ PropertyRNA *prop = RNA_struct_type_find_property_no_base(srna, identifier);
+ if (prop != NULL) {
+ return prop;
+ }
+ }
+ return NULL;
+}
+
FunctionRNA *RNA_struct_find_function(StructRNA *srna, const char *identifier)
{
#if 1
FunctionRNA *func;
- StructRNA *type;
- for (type = srna; type; type = type->base) {
+ for (; srna; srna = srna->base) {
func = (FunctionRNA *)BLI_findstring_ptr(
- &type->functions, identifier, offsetof(FunctionRNA, identifier));
+ &srna->functions, identifier, offsetof(FunctionRNA, identifier));
if (func) {
return func;
}
@@ -3002,7 +3016,7 @@ void RNA_property_float_set(PointerRNA *ptr, PropertyRNA *prop, float value)
BLI_assert(RNA_property_type(prop) == PROP_FLOAT);
BLI_assert(RNA_property_array_check(prop) == false);
/* useful to check on bad values but set function should clamp */
- /* BLI_assert(RNA_property_float_clamp(ptr, prop, &value) == 0); */
+ // BLI_assert(RNA_property_float_clamp(ptr, prop, &value) == 0);
if ((idprop = rna_idproperty_check(&prop, ptr))) {
RNA_property_float_clamp(ptr, prop, &value);
@@ -3676,6 +3690,8 @@ PointerRNA RNA_property_pointer_get(PointerRNA *ptr, PropertyRNA *prop)
PointerPropertyRNA *pprop = (PointerPropertyRNA *)prop;
IDProperty *idprop;
+ static ThreadMutex lock = BLI_MUTEX_INITIALIZER;
+
BLI_assert(RNA_property_type(prop) == PROP_POINTER);
if ((idprop = rna_idproperty_check(&prop, ptr))) {
@@ -3695,9 +3711,14 @@ PointerRNA RNA_property_pointer_get(PointerRNA *ptr, PropertyRNA *prop)
return pprop->get(ptr);
}
if (prop->flag & PROP_IDPROPERTY) {
- /* XXX temporary hack to add it automatically, reading should
- * never do any write ops, to ensure thread safety etc. */
+ /* NOTE: While creating/writing data in an accessor is really bad design-wise, this is
+ * currently very difficult to avoid in that case. So a global mutex is used to keep ensuring
+ * thread safety. */
+ BLI_mutex_lock(&lock);
+ /* NOTE: We do not need to check again for existence of the pointer after locking here, since
+ * this is also done in #RNA_property_pointer_add itself. */
RNA_property_pointer_add(ptr, prop);
+ BLI_mutex_unlock(&lock);
return RNA_property_pointer_get(ptr, prop);
}
return PointerRNA_NULL;
@@ -4972,10 +4993,7 @@ void rna_iterator_array_end(CollectionPropertyIterator *iter)
{
ArrayIterator *internal = &iter->internal.array;
- if (internal->free_ptr) {
- MEM_freeN(internal->free_ptr);
- internal->free_ptr = NULL;
- }
+ MEM_SAFE_FREE(internal->free_ptr);
}
PointerRNA rna_array_lookup_int(
@@ -6723,7 +6741,7 @@ bool RNA_struct_property_is_set_ex(PointerRNA *ptr, const char *identifier, bool
return RNA_property_is_set_ex(ptr, prop, use_ghost);
}
/* python raises an error */
- /* printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name); */
+ // printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
return 0;
}
@@ -6735,7 +6753,7 @@ bool RNA_struct_property_is_set(PointerRNA *ptr, const char *identifier)
return RNA_property_is_set(ptr, prop);
}
/* python raises an error */
- /* printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name); */
+ // printf("%s: %s.%s not found.\n", __func__, ptr->type->identifier, name);
return 0;
}
diff --git a/source/blender/makesrna/intern/rna_access_compare_override.c b/source/blender/makesrna/intern/rna_access_compare_override.c
index 3912c873fd0..2c552970c82 100644
--- a/source/blender/makesrna/intern/rna_access_compare_override.c
+++ b/source/blender/makesrna/intern/rna_access_compare_override.c
@@ -1096,6 +1096,7 @@ static void rna_property_override_check_resync(Main *bmain,
PointerRNA *ptr_item_dst,
PointerRNA *ptr_item_src)
{
+ ID *id_owner = rna_property_override_property_real_id_owner(bmain, ptr_dst, NULL, NULL);
ID *id_src = rna_property_override_property_real_id_owner(bmain, ptr_item_src, NULL, NULL);
ID *id_dst = rna_property_override_property_real_id_owner(bmain, ptr_item_dst, NULL, NULL);
@@ -1109,9 +1110,18 @@ static void rna_property_override_check_resync(Main *bmain,
* remapped to its new local override. In that case overrides and linked data
* are always properly matching. */
id_src != id_dst &&
- /* If one of the pointers is NULL and not the other, or if linked reference ID
- * of `id_src` is not `id_dst`, we are in a non-matching case. */
- (ELEM(NULL, id_src, id_dst) || id_src->override_library->reference != id_dst)) {
+ /* If one of the pointers is NULL and not the other, we are in a non-matching case. */
+ (ELEM(NULL, id_src, id_dst) ||
+ /* If `id_dst` is not from same lib as id_src, and linked reference ID of `id_src` is not
+ * `id_dst`, we are in a non-matching case. */
+ (id_dst->lib != id_src->lib && id_src->override_library->reference != id_dst) ||
+ /* If `id_dst` is from same lib as id_src, and is not same as `id_owner`, we are in a
+ * non-matching case.
+ *
+ * NOTE: Here we are testing if `id_owner` is referencing itself, in that case the new
+ * override copy generated by `BKE_lib_override_library_update` will already have its
+ * self-references updated to itself, instead of still pointing to its linked source. */
+ (id_dst->lib == id_src->lib && id_dst != id_owner))) {
ptr_dst->owner_id->tag |= LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
CLOG_INFO(&LOG, 3, "Local override %s detected as needing resync", ptr_dst->owner_id->name);
}
diff --git a/source/blender/makesrna/intern/rna_armature.c b/source/blender/makesrna/intern/rna_armature.c
index 49d02524e43..690506fa517 100644
--- a/source/blender/makesrna/intern/rna_armature.c
+++ b/source/blender/makesrna/intern/rna_armature.c
@@ -56,7 +56,7 @@ static void rna_Armature_update_data(Main *UNUSED(bmain), Scene *UNUSED(scene),
DEG_id_tag_update(id, 0);
WM_main_add_notifier(NC_GEOM | ND_DATA, id);
- /*WM_main_add_notifier(NC_OBJECT|ND_POSE, NULL); */
+ // WM_main_add_notifier(NC_OBJECT|ND_POSE, NULL);
}
static void rna_Armature_dependency_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
@@ -994,7 +994,7 @@ static void rna_def_bone_common(StructRNA *srna, int editbone)
}
RNA_def_property_float_sdna(prop, NULL, "rad_head");
/* XXX range is 0 to lim, where lim = 10000.0f * MAX2(1.0, view3d->grid); */
- /*RNA_def_property_range(prop, 0, 1000); */
+ // RNA_def_property_range(prop, 0, 1000);
RNA_def_property_ui_range(prop, 0.01, 100, 0.1, 3);
RNA_def_property_ui_text(
prop, "Envelope Head Radius", "Radius of head of bone (for Envelope deform only)");
@@ -1008,7 +1008,7 @@ static void rna_def_bone_common(StructRNA *srna, int editbone)
}
RNA_def_property_float_sdna(prop, NULL, "rad_tail");
/* XXX range is 0 to lim, where lim = 10000.0f * MAX2(1.0, view3d->grid); */
- /*RNA_def_property_range(prop, 0, 1000); */
+ // RNA_def_property_range(prop, 0, 1000);
RNA_def_property_ui_range(prop, 0.01, 100, 0.1, 3);
RNA_def_property_ui_text(
prop, "Envelope Tail Radius", "Radius of tail of bone (for Envelope deform only)");
@@ -1346,7 +1346,7 @@ static void rna_def_edit_bone(BlenderRNA *brna)
/* calculated and read only, not actual data access */
prop = RNA_def_property(srna, "matrix", PROP_FLOAT, PROP_MATRIX);
- /* RNA_def_property_float_sdna(prop, NULL, ""); */ /* Doesn't access any real data. */
+ // RNA_def_property_float_sdna(prop, NULL, ""); /* Doesn't access any real data. */
RNA_def_property_multi_array(prop, 2, rna_matrix_dimsize_4x4);
// RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_flag(prop, PROP_THICK_WRAP); /* no reference to original data */
diff --git a/source/blender/makesrna/intern/rna_asset.c b/source/blender/makesrna/intern/rna_asset.c
index 80b2594d0c9..1e583f4ca52 100644
--- a/source/blender/makesrna/intern/rna_asset.c
+++ b/source/blender/makesrna/intern/rna_asset.c
@@ -273,7 +273,7 @@ static void rna_def_asset_handle_api(StructRNA *srna)
parm = RNA_def_pointer(func, "asset_file_handle", "FileSelectEntry", "", "");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
parm = RNA_def_pointer(func,
- "asset_library",
+ "asset_library_ref",
"AssetLibraryReference",
"",
"The asset library containing the given asset, only valid if the asset "
@@ -319,7 +319,7 @@ static void rna_def_asset_library_reference(BlenderRNA *brna)
{
StructRNA *srna = RNA_def_struct(brna, "AssetLibraryReference", NULL);
RNA_def_struct_ui_text(
- srna, "Asset Library Reference", "Identifier to refere to the asset library");
+ srna, "Asset Library Reference", "Identifier to refer to the asset library");
}
/**
@@ -329,7 +329,7 @@ PropertyRNA *rna_def_asset_library_reference_common(struct StructRNA *srna,
const char *get,
const char *set)
{
- PropertyRNA *prop = RNA_def_property(srna, "asset_library", PROP_ENUM, PROP_NONE);
+ PropertyRNA *prop = RNA_def_property(srna, "asset_library_ref", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, DummyRNA_NULL_items);
RNA_def_property_enum_funcs(prop, get, set, "rna_asset_library_reference_itemf");
diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c
index 2b09ea51a84..cdca58df4b0 100644
--- a/source/blender/makesrna/intern/rna_brush.c
+++ b/source/blender/makesrna/intern/rna_brush.c
@@ -1281,6 +1281,12 @@ static void rna_def_gpencil_options(BlenderRNA *brna)
{0, NULL, 0, NULL, NULL},
};
+ static EnumPropertyItem rna_enum_gpencil_brush_caps_types_items[] = {
+ {GP_STROKE_CAP_ROUND, "ROUND", ICON_GP_CAPS_ROUND, "Round", ""},
+ {GP_STROKE_CAP_FLAT, "FLAT", ICON_GP_CAPS_FLAT, "Flat", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
srna = RNA_def_struct(brna, "BrushGpencilSettings", NULL);
RNA_def_struct_sdna(srna, "BrushGpencilSettings");
RNA_def_struct_path_func(srna, "rna_BrushGpencilSettings_path");
@@ -1750,6 +1756,12 @@ static void rna_def_gpencil_options(BlenderRNA *brna)
RNA_def_property_update(
prop, NC_GPENCIL | ND_DATA, "rna_BrushGpencilSettings_eraser_mode_update");
+ prop = RNA_def_property(srna, "caps_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "caps_type");
+ RNA_def_property_enum_items(prop, rna_enum_gpencil_brush_caps_types_items);
+ RNA_def_property_ui_text(prop, "Caps Type", "The shape of the start and end of the stroke");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+
prop = RNA_def_property(srna, "fill_draw_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "fill_draw_mode");
RNA_def_property_enum_items(prop, rna_enum_gpencil_fill_draw_modes_items);
diff --git a/source/blender/makesrna/intern/rna_cachefile.c b/source/blender/makesrna/intern/rna_cachefile.c
index c25cea1b4b3..74d924b8937 100644
--- a/source/blender/makesrna/intern/rna_cachefile.c
+++ b/source/blender/makesrna/intern/rna_cachefile.c
@@ -37,6 +37,7 @@
# include "BKE_cachefile.h"
# include "DEG_depsgraph.h"
+# include "DEG_depsgraph_build.h"
# include "WM_api.h"
# include "WM_types.h"
@@ -53,6 +54,12 @@ static void rna_CacheFile_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Poin
WM_main_add_notifier(NC_OBJECT | ND_DRAW, NULL);
}
+static void rna_CacheFile_dependency_update(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ rna_CacheFile_update(bmain, scene, ptr);
+ DEG_relations_tag_update(bmain);
+}
+
static void rna_CacheFile_object_paths_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
{
CacheFile *cache_file = (CacheFile *)ptr->data;
@@ -64,8 +71,8 @@ static void rna_CacheFile_object_paths_begin(CollectionPropertyIterator *iter, P
/* cachefile.object_paths */
static void rna_def_alembic_object_path(BlenderRNA *brna)
{
- StructRNA *srna = RNA_def_struct(brna, "AlembicObjectPath", NULL);
- RNA_def_struct_sdna(srna, "AlembicObjectPath");
+ StructRNA *srna = RNA_def_struct(brna, "CacheObjectPath", NULL);
+ RNA_def_struct_sdna(srna, "CacheObjectPath");
RNA_def_struct_ui_text(srna, "Object Path", "Path of an object inside of an Alembic archive");
RNA_def_struct_ui_icon(srna, ICON_NONE);
@@ -81,8 +88,8 @@ static void rna_def_alembic_object_path(BlenderRNA *brna)
/* cachefile.object_paths */
static void rna_def_cachefile_object_paths(BlenderRNA *brna, PropertyRNA *cprop)
{
- RNA_def_property_srna(cprop, "AlembicObjectPaths");
- StructRNA *srna = RNA_def_struct(brna, "AlembicObjectPaths", NULL);
+ RNA_def_property_srna(cprop, "CacheObjectPaths");
+ StructRNA *srna = RNA_def_struct(brna, "CacheObjectPaths", NULL);
RNA_def_struct_sdna(srna, "CacheFile");
RNA_def_struct_ui_text(srna, "Object Paths", "Collection of object paths");
}
@@ -105,6 +112,16 @@ static void rna_def_cachefile(BlenderRNA *brna)
prop, "Sequence", "Whether the cache is separated in a series of files");
RNA_def_property_update(prop, 0, "rna_CacheFile_update");
+ prop = RNA_def_property(srna, "use_render_procedural", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_ui_text(
+ prop,
+ "Use Render Engine Procedural",
+ "Display boxes in the viewport as placeholders for the objects, Cycles will use a "
+ "procedural to load the objects during viewport rendering in experimental mode, "
+ "other render engines will also receive a placeholder and should take care of loading the "
+ "Alembic data themselves if possible");
+ RNA_def_property_update(prop, 0, "rna_CacheFile_dependency_update");
+
/* ----------------- For Scene time ------------------- */
prop = RNA_def_property(srna, "override_frame", PROP_BOOLEAN, PROP_NONE);
@@ -133,6 +150,23 @@ static void rna_def_cachefile(BlenderRNA *brna)
"determine which file to use in a file sequence");
RNA_def_property_update(prop, 0, "rna_CacheFile_update");
+ /* ----------------- Cache controls ----------------- */
+
+ prop = RNA_def_property(srna, "use_prefetch", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_ui_text(
+ prop,
+ "Use Prefetch",
+ "When enabled, the Cycles Procedural will preload animation data for faster updates");
+ RNA_def_property_update(prop, 0, "rna_CacheFile_update");
+
+ prop = RNA_def_property(srna, "prefetch_cache_size", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_ui_text(
+ prop,
+ "Prefetch Cache Size",
+ "Memory usage limit in megabytes for the Cycles Procedural cache, if the data does not "
+ "fit within the limit, rendering is aborted");
+ RNA_def_property_update(prop, 0, "rna_CacheFile_update");
+
/* ----------------- Axis Conversion ----------------- */
prop = RNA_def_property(srna, "forward_axis", PROP_ENUM, PROP_NONE);
@@ -169,8 +203,8 @@ static void rna_def_cachefile(BlenderRNA *brna)
NULL,
NULL,
NULL);
- RNA_def_property_struct_type(prop, "AlembicObjectPath");
- RNA_def_property_srna(prop, "AlembicObjectPaths");
+ RNA_def_property_struct_type(prop, "CacheObjectPath");
+ RNA_def_property_srna(prop, "CacheObjectPaths");
RNA_def_property_ui_text(
prop, "Object Paths", "Paths of the objects inside the Alembic archive");
diff --git a/source/blender/makesrna/intern/rna_collection.c b/source/blender/makesrna/intern/rna_collection.c
index 608a8e51b09..77276f47689 100644
--- a/source/blender/makesrna/intern/rna_collection.c
+++ b/source/blender/makesrna/intern/rna_collection.c
@@ -322,17 +322,17 @@ static void rna_Collection_flag_set(PointerRNA *ptr, const bool value, const int
static void rna_Collection_hide_select_set(PointerRNA *ptr, bool value)
{
- rna_Collection_flag_set(ptr, value, COLLECTION_RESTRICT_SELECT);
+ rna_Collection_flag_set(ptr, value, COLLECTION_HIDE_SELECT);
}
static void rna_Collection_hide_viewport_set(PointerRNA *ptr, bool value)
{
- rna_Collection_flag_set(ptr, value, COLLECTION_RESTRICT_VIEWPORT);
+ rna_Collection_flag_set(ptr, value, COLLECTION_HIDE_VIEWPORT);
}
static void rna_Collection_hide_render_set(PointerRNA *ptr, bool value)
{
- rna_Collection_flag_set(ptr, value, COLLECTION_RESTRICT_RENDER);
+ rna_Collection_flag_set(ptr, value, COLLECTION_HIDE_RENDER);
}
static void rna_Collection_flag_update(Main *bmain, Scene *scene, PointerRNA *ptr)
@@ -496,7 +496,7 @@ void RNA_def_collections(BlenderRNA *brna)
/* Flags */
prop = RNA_def_property(srna, "hide_select", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flag", COLLECTION_RESTRICT_SELECT);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", COLLECTION_HIDE_SELECT);
RNA_def_property_boolean_funcs(prop, NULL, "rna_Collection_hide_select_set");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_ui_icon(prop, ICON_RESTRICT_SELECT_OFF, -1);
@@ -504,7 +504,7 @@ void RNA_def_collections(BlenderRNA *brna)
RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_Collection_flag_update");
prop = RNA_def_property(srna, "hide_viewport", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flag", COLLECTION_RESTRICT_VIEWPORT);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", COLLECTION_HIDE_VIEWPORT);
RNA_def_property_boolean_funcs(prop, NULL, "rna_Collection_hide_viewport_set");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_ui_icon(prop, ICON_RESTRICT_VIEW_OFF, -1);
@@ -512,7 +512,7 @@ void RNA_def_collections(BlenderRNA *brna)
RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_Collection_flag_update");
prop = RNA_def_property(srna, "hide_render", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flag", COLLECTION_RESTRICT_RENDER);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", COLLECTION_HIDE_RENDER);
RNA_def_property_boolean_funcs(prop, NULL, "rna_Collection_hide_render_set");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_ui_icon(prop, ICON_RESTRICT_RENDER_OFF, -1);
diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c
index 3064703b02e..5968c8bac8f 100644
--- a/source/blender/makesrna/intern/rna_constraint.c
+++ b/source/blender/makesrna/intern/rna_constraint.c
@@ -679,7 +679,7 @@ static void rna_ActionConstraint_mix_mode_set(PointerRNA *ptr, int value)
acon->mix_mode = value;
/* The After mode can be computed in world space for efficiency
- * and backward compatibility, while Before requires Local. */
+ * and backward compatibility, while Before or Split requires Local. */
if (ELEM(value, ACTCON_MIX_AFTER, ACTCON_MIX_AFTER_FULL)) {
con->ownspace = CONSTRAINT_SPACE_WORLD;
}
@@ -715,7 +715,7 @@ static int rna_SplineIKConstraint_joint_bindings_get_length(PointerRNA *ptr,
length[0] = ikData->numpoints;
}
else {
- length[0] = 256; /* for raw_access, untested */
+ length[0] = 0;
}
return length[0];
@@ -1773,25 +1773,47 @@ static void rna_def_constraint_action(BlenderRNA *brna)
};
static const EnumPropertyItem mix_mode_items[] = {
+ {ACTCON_MIX_BEFORE_FULL,
+ "BEFORE_FULL",
+ 0,
+ "Before Original (Full)",
+ "Apply the action channels before the original transformation, as if applied to an "
+ "imaginary parent in Full Inherit Scale mode. Will create shear when combining rotation "
+ "and non-uniform scale"},
{ACTCON_MIX_BEFORE,
"BEFORE",
0,
- "Before Original",
- "Apply the action channels before the original transformation, "
- "as if applied to an imaginary parent with Aligned Inherit Scale"},
- {ACTCON_MIX_AFTER,
- "AFTER",
+ "Before Original (Aligned)",
+ "Apply the action channels before the original transformation, as if applied to an "
+ "imaginary parent in Aligned Inherit Scale mode. This effectively uses Full for location "
+ "and Split Channels for rotation and scale"},
+ {ACTCON_MIX_BEFORE_SPLIT,
+ "BEFORE_SPLIT",
0,
- "After Original",
- "Apply the action channels after the original transformation, "
- "as if applied to an imaginary child with Aligned Inherit Scale"},
+ "Before Original (Split Channels)",
+ "Apply the action channels before the original transformation, handling location, rotation "
+ "and scale separately"},
+ {0, "", 0, NULL, NULL},
{ACTCON_MIX_AFTER_FULL,
"AFTER_FULL",
0,
- "After Original (Full Scale)",
- "Apply the action channels after the original transformation, as if "
- "applied to an imaginary child with Full Inherit Scale. This mode "
- "can create shear and is provided only for backward compatibility"},
+ "After Original (Full)",
+ "Apply the action channels after the original transformation, as if applied to an "
+ "imaginary child in Full Inherit Scale mode. Will create shear when combining rotation "
+ "and non-uniform scale"},
+ {ACTCON_MIX_AFTER,
+ "AFTER",
+ 0,
+ "After Original (Aligned)",
+ "Apply the action channels after the original transformation, as if applied to an "
+ "imaginary child in Aligned Inherit Scale mode. This effectively uses Full for location "
+ "and Split Channels for rotation and scale"},
+ {ACTCON_MIX_AFTER_SPLIT,
+ "AFTER_SPLIT",
+ 0,
+ "After Original (Split Channels)",
+ "Apply the action channels after the original transformation, handling location, rotation "
+ "and scale separately"},
{0, NULL, 0, NULL, NULL},
};
@@ -3487,6 +3509,12 @@ void RNA_def_constraint(BlenderRNA *brna)
RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
RNA_def_property_ui_icon(prop, ICON_HIDE_OFF, -1);
+ prop = RNA_def_property(srna, "enabled", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", CONSTRAINT_OFF);
+ RNA_def_property_ui_text(prop, "Enabled", "Use the results of this constraint");
+ RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
+ RNA_def_property_ui_icon(prop, ICON_HIDE_ON, 1);
+
prop = RNA_def_property(srna, "show_expanded", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE);
RNA_def_property_boolean_sdna(prop, NULL, "ui_expand_flag", 0);
diff --git a/source/blender/makesrna/intern/rna_curveprofile.c b/source/blender/makesrna/intern/rna_curveprofile.c
index b3ab8cc15a2..66c0fc72c7a 100644
--- a/source/blender/makesrna/intern/rna_curveprofile.c
+++ b/source/blender/makesrna/intern/rna_curveprofile.c
@@ -23,9 +23,6 @@
#include "DNA_curve_types.h"
#include "DNA_curveprofile_types.h"
-#include "DNA_texture_types.h"
-
-#include "BLI_utildefines.h"
#include "RNA_define.h"
#include "rna_internal.h"
@@ -37,31 +34,7 @@
# include "RNA_access.h"
-# include "DNA_image_types.h"
-# include "DNA_material_types.h"
-# include "DNA_movieclip_types.h"
-# include "DNA_node_types.h"
-# include "DNA_object_types.h"
-# include "DNA_particle_types.h"
-# include "DNA_sequence_types.h"
-
-# include "MEM_guardedalloc.h"
-
-# include "BKE_colorband.h"
# include "BKE_curveprofile.h"
-# include "BKE_image.h"
-# include "BKE_linestyle.h"
-# include "BKE_movieclip.h"
-# include "BKE_node.h"
-
-# include "DEG_depsgraph.h"
-
-# include "ED_node.h"
-
-# include "IMB_colormanagement.h"
-# include "IMB_imbuf.h"
-
-# include "SEQ_sequencer.h"
/**
* Set both handle types for all selected points in the profile-- faster than changing types
diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c
index 1f187382980..a38bbd3d6d2 100644
--- a/source/blender/makesrna/intern/rna_fcurve.c
+++ b/source/blender/makesrna/intern/rna_fcurve.c
@@ -809,7 +809,7 @@ static int rna_FModifierGenerator_coefficients_get_length(PointerRNA *ptr,
length[0] = gen->arraysize;
}
else {
- length[0] = 100; /* for raw_access, untested */
+ length[0] = 0;
}
return length[0];
diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c
index aad6f1231dd..3e5dce64c7b 100644
--- a/source/blender/makesrna/intern/rna_gpencil.c
+++ b/source/blender/makesrna/intern/rna_gpencil.c
@@ -2117,7 +2117,7 @@ static void rna_def_gpencil_layer(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_viewlayer_masks", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", GP_LAYER_DISABLE_MASKS_IN_VIEWLAYER);
RNA_def_property_ui_text(
- prop, "Use Masks in Render", "Include the mask layers when rendering the viewlayer");
+ prop, "Use Masks in Render", "Include the mask layers when rendering the view-layer");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
/* blend mode */
diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c
index c058ab6cfcc..e44ddb07d53 100644
--- a/source/blender/makesrna/intern/rna_image.c
+++ b/source/blender/makesrna/intern/rna_image.c
@@ -290,15 +290,10 @@ static void rna_UDIMTile_tile_number_set(PointerRNA *ptr, int value)
ImageTile *tile = (ImageTile *)ptr->data;
Image *image = (Image *)ptr->owner_id;
- /* The index of the first tile can't be changed. */
- if (tile->tile_number == 1001) {
- return;
- }
-
/* Check that no other tile already has that number. */
ImageTile *cur_tile = BKE_image_get_tile(image, value);
- if (cur_tile == NULL || cur_tile == tile) {
- tile->tile_number = value;
+ if (cur_tile == NULL) {
+ BKE_image_reassign_tile(image, tile, value);
}
}
diff --git a/source/blender/makesrna/intern/rna_layer.c b/source/blender/makesrna/intern/rna_layer.c
index 0414afe1514..ab4cbc429ce 100644
--- a/source/blender/makesrna/intern/rna_layer.c
+++ b/source/blender/makesrna/intern/rna_layer.c
@@ -134,7 +134,7 @@ static bool rna_LayerCollection_visible_get(LayerCollection *layer_collection, b
}
if (v3d->local_collections_uuid & layer_collection->local_collections_bits) {
- return (layer_collection->runtime_flag & LAYER_COLLECTION_RESTRICT_VIEWPORT) == 0;
+ return (layer_collection->runtime_flag & LAYER_COLLECTION_HIDE_VIEWPORT) == 0;
}
return false;
diff --git a/source/blender/makesrna/intern/rna_light.c b/source/blender/makesrna/intern/rna_light.c
index 0593db0dd56..8bec337885e 100644
--- a/source/blender/makesrna/intern/rna_light.c
+++ b/source/blender/makesrna/intern/rna_light.c
@@ -188,6 +188,7 @@ static void rna_def_light(BlenderRNA *brna)
prop = RNA_def_property(srna, "node_tree", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "nodetree");
RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Node Tree", "Node tree for node based lights");
prop = RNA_def_property(srna, "use_nodes", PROP_BOOLEAN, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_linestyle.c b/source/blender/makesrna/intern/rna_linestyle.c
index ca97f2c9a55..8cd1ac0d963 100644
--- a/source/blender/makesrna/intern/rna_linestyle.c
+++ b/source/blender/makesrna/intern/rna_linestyle.c
@@ -2189,6 +2189,7 @@ static void rna_def_linestyle(BlenderRNA *brna)
prop = RNA_def_property(srna, "node_tree", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "nodetree");
RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Node Tree", "Node tree for node-based shaders");
prop = RNA_def_property(srna, "use_nodes", PROP_BOOLEAN, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_mask.c b/source/blender/makesrna/intern/rna_mask.c
index 8c7d9698a67..9c90c209389 100644
--- a/source/blender/makesrna/intern/rna_mask.c
+++ b/source/blender/makesrna/intern/rna_mask.c
@@ -991,19 +991,19 @@ static void rna_def_mask_layer(BlenderRNA *brna)
/* restrict */
prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "restrictflag", MASK_RESTRICT_VIEW);
+ RNA_def_property_boolean_sdna(prop, NULL, "visibility_flag", MASK_HIDE_VIEW);
RNA_def_property_ui_text(prop, "Restrict View", "Restrict visibility in the viewport");
RNA_def_property_ui_icon(prop, ICON_RESTRICT_VIEW_OFF, -1);
RNA_def_property_update(prop, NC_MASK | ND_DRAW, NULL);
prop = RNA_def_property(srna, "hide_select", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "restrictflag", MASK_RESTRICT_SELECT);
+ RNA_def_property_boolean_sdna(prop, NULL, "visibility_flag", MASK_HIDE_SELECT);
RNA_def_property_ui_text(prop, "Restrict Select", "Restrict selection in the viewport");
RNA_def_property_ui_icon(prop, ICON_RESTRICT_SELECT_OFF, -1);
RNA_def_property_update(prop, NC_MASK | ND_DRAW, NULL);
prop = RNA_def_property(srna, "hide_render", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "restrictflag", MASK_RESTRICT_RENDER);
+ RNA_def_property_boolean_sdna(prop, NULL, "visibility_flag", MASK_HIDE_RENDER);
RNA_def_property_ui_text(prop, "Restrict Render", "Restrict renderability");
RNA_def_property_ui_icon(prop, ICON_RESTRICT_RENDER_OFF, -1);
RNA_def_property_update(prop, NC_MASK | NA_EDITED, NULL);
diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c
index d91c0bfaf29..144950235c8 100644
--- a/source/blender/makesrna/intern/rna_material.c
+++ b/source/blender/makesrna/intern/rna_material.c
@@ -849,8 +849,7 @@ void RNA_def_material(BlenderRNA *brna)
prop = RNA_def_property(srna, "node_tree", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "nodetree");
RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP);
- /* XXX: remove once overrides in material node trees are supported. */
- RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Node Tree", "Node tree for node based materials");
prop = RNA_def_property(srna, "use_nodes", PROP_BOOLEAN, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c
index 9caff88a3a5..fbc578acb8e 100644
--- a/source/blender/makesrna/intern/rna_mesh.c
+++ b/source/blender/makesrna/intern/rna_mesh.c
@@ -1687,7 +1687,7 @@ static void rna_def_mvert(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all");
prop = RNA_def_property(srna, "normal", PROP_FLOAT, PROP_DIRECTION);
- /* RNA_def_property_float_sdna(prop, NULL, "no"); */
+ // RNA_def_property_float_sdna(prop, NULL, "no");
RNA_def_property_array(prop, 3);
RNA_def_property_range(prop, -1.0f, 1.0f);
RNA_def_property_float_funcs(
diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c
index b424a575094..486d8d13564 100644
--- a/source/blender/makesrna/intern/rna_modifier.c
+++ b/source/blender/makesrna/intern/rna_modifier.c
@@ -595,6 +595,44 @@ const EnumPropertyItem rna_enum_axis_flag_xyz_items[] = {
{0, NULL, 0, NULL, NULL},
};
+const EnumPropertyItem rna_enum_subdivision_uv_smooth_items[] = {
+ {SUBSURF_UV_SMOOTH_NONE, "NONE", 0, "None", "UVs are not smoothed, boundaries are kept sharp"},
+ {SUBSURF_UV_SMOOTH_PRESERVE_CORNERS,
+ "PRESERVE_CORNERS",
+ 0,
+ "Keep Corners",
+ "UVs are smoothed, corners on discontinuous boundary are kept sharp"},
+ {SUBSURF_UV_SMOOTH_PRESERVE_CORNERS_AND_JUNCTIONS,
+ "PRESERVE_CORNERS_AND_JUNCTIONS",
+ 0,
+ "Keep Corners, Junctions",
+ "UVs are smoothed, corners on discontinuous boundary and "
+ "junctions of 3 or more regions are kept sharp"},
+ {SUBSURF_UV_SMOOTH_PRESERVE_CORNERS_JUNCTIONS_AND_CONCAVE,
+ "PRESERVE_CORNERS_JUNCTIONS_AND_CONCAVE",
+ 0,
+ "Keep Corners, Junctions, Concave",
+ "UVs are smoothed, corners on discontinuous boundary, "
+ "junctions of 3 or more regions and darts and concave corners are kept sharp"},
+ {SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES,
+ "PRESERVE_BOUNDARIES",
+ 0,
+ "Keep Boundaries",
+ "UVs are smoothed, boundaries are kept sharp"},
+ {SUBSURF_UV_SMOOTH_ALL, "SMOOTH_ALL", 0, "All", "UVs and boundaries are smoothed"},
+ {0, NULL, 0, NULL, NULL},
+};
+
+const EnumPropertyItem rna_enum_subdivision_boundary_smooth_items[] = {
+ {SUBSURF_BOUNDARY_SMOOTH_PRESERVE_CORNERS,
+ "PRESERVE_CORNERS",
+ 0,
+ "Keep Corners",
+ "Smooth boundaries, but corners are kept sharp"},
+ {SUBSURF_BOUNDARY_SMOOTH_ALL, "ALL", 0, "All", "Smooth boundaries, including corners"},
+ {0, NULL, 0, NULL, NULL},
+};
+
#ifdef RNA_RUNTIME
# include "DNA_curve_types.h"
# include "DNA_fluid_types.h"
@@ -1631,55 +1669,12 @@ static IDProperty **rna_NodesModifier_properties(PointerRNA *ptr)
static void rna_def_property_subdivision_common(StructRNA *srna)
{
- static const EnumPropertyItem prop_uv_smooth_items[] = {
- {SUBSURF_UV_SMOOTH_NONE,
- "NONE",
- 0,
- "None",
- "UVs are not smoothed, boundaries are kept sharp"},
- {SUBSURF_UV_SMOOTH_PRESERVE_CORNERS,
- "PRESERVE_CORNERS",
- 0,
- "Keep Corners",
- "UVs are smoothed, corners on discontinuous boundary are kept sharp"},
- {SUBSURF_UV_SMOOTH_PRESERVE_CORNERS_AND_JUNCTIONS,
- "PRESERVE_CORNERS_AND_JUNCTIONS",
- 0,
- "Keep Corners, Junctions",
- "UVs are smoothed, corners on discontinuous boundary and "
- "junctions of 3 or more regions are kept sharp"},
- {SUBSURF_UV_SMOOTH_PRESERVE_CORNERS_JUNCTIONS_AND_CONCAVE,
- "PRESERVE_CORNERS_JUNCTIONS_AND_CONCAVE",
- 0,
- "Keep Corners, Junctions, Concave",
- "UVs are smoothed, corners on discontinuous boundary, "
- "junctions of 3 or more regions and darts and concave corners are kept sharp"},
- {SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES,
- "PRESERVE_BOUNDARIES",
- 0,
- "Keep Boundaries",
- "UVs are smoothed, boundaries are kept sharp"},
- {SUBSURF_UV_SMOOTH_ALL, "SMOOTH_ALL", 0, "All", "UVs and boundaries are smoothed"},
- {0, NULL, 0, NULL, NULL},
- };
-
- static const EnumPropertyItem prop_boundary_smooth_items[] = {
- {SUBSURF_BOUNDARY_SMOOTH_PRESERVE_CORNERS,
- "PRESERVE_CORNERS",
- 0,
- "Keep Corners",
- "Smooth boundaries, but corners are kept sharp"},
- {SUBSURF_BOUNDARY_SMOOTH_ALL, "ALL", 0, "All", "Smooth boundaries, including corners"},
- {0, NULL, 0, NULL, NULL},
- };
-
PropertyRNA *prop;
-
RNA_define_lib_overridable(true);
prop = RNA_def_property(srna, "uv_smooth", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "uv_smooth");
- RNA_def_property_enum_items(prop, prop_uv_smooth_items);
+ RNA_def_property_enum_items(prop, rna_enum_subdivision_uv_smooth_items);
RNA_def_property_ui_text(prop, "UV Smooth", "Controls how smoothing is applied to UVs");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
@@ -1693,7 +1688,7 @@ static void rna_def_property_subdivision_common(StructRNA *srna)
prop = RNA_def_property(srna, "boundary_smooth", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "boundary_smooth");
- RNA_def_property_enum_items(prop, prop_boundary_smooth_items);
+ RNA_def_property_enum_items(prop, rna_enum_subdivision_boundary_smooth_items);
RNA_def_property_ui_text(prop, "Boundary Smooth", "Controls how open boundaries are smoothed");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index 3d4256db335..d8ab7c7a61b 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -439,6 +439,34 @@ static const EnumPropertyItem rna_node_geometry_attribute_randomize_operation_it
{0, NULL, 0, NULL, NULL},
};
+static const EnumPropertyItem rna_node_geometry_curve_handle_type_items[] = {
+ {GEO_NODE_CURVE_HANDLE_FREE,
+ "FREE",
+ ICON_HANDLE_FREE,
+ "Free",
+ "The handle can be moved anywhere, and doesn't influence the point's other handle"},
+ {GEO_NODE_CURVE_HANDLE_AUTO,
+ "AUTO",
+ ICON_HANDLE_AUTO,
+ "Auto",
+ "The location is automatically calculated to be smooth"},
+ {GEO_NODE_CURVE_HANDLE_VECTOR,
+ "VECTOR",
+ ICON_HANDLE_VECTOR,
+ "Vector",
+ "The location is calculated to point to the next/previous control point"},
+ {GEO_NODE_CURVE_HANDLE_ALIGN,
+ "ALIGN",
+ ICON_HANDLE_ALIGNED,
+ "Align",
+ "The location is constrained to point in the opposite direction as the other handle"},
+ {0, NULL, 0, NULL, NULL}};
+
+static const EnumPropertyItem rna_node_geometry_curve_handle_side_items[] = {
+ {GEO_NODE_CURVE_HANDLE_LEFT, "LEFT", ICON_NONE, "Left", "Use the left handles"},
+ {GEO_NODE_CURVE_HANDLE_RIGHT, "RIGHT", ICON_NONE, "Right", "Use the right handles"},
+ {0, NULL, 0, NULL, NULL}};
+
#ifndef RNA_RUNTIME
static const EnumPropertyItem node_sampler_type_items[] = {
{0, "NEAREST", 0, "Nearest", ""},
@@ -1008,7 +1036,7 @@ static void rna_NodeTree_get_from_context(
void *ret1, *ret2, *ret3;
RNA_pointer_create(NULL, ntreetype->rna_ext.srna, NULL, &ptr); /* dummy */
- /* RNA_struct_find_function(&ptr, "get_from_context"); */
+ // RNA_struct_find_function(&ptr, "get_from_context");
func = &rna_NodeTree_get_from_context_func;
RNA_parameter_list_create(&list, &ptr, func);
@@ -2959,7 +2987,7 @@ static void rna_NodeSocketInterface_register_properties(bNodeTree *ntree,
}
RNA_pointer_create((ID *)ntree, &RNA_NodeSocketInterface, stemp, &ptr);
- /* RNA_struct_find_function(&ptr, "register_properties"); */
+ // RNA_struct_find_function(&ptr, "register_properties");
func = &rna_NodeSocketInterface_register_properties_func;
RNA_parameter_list_create(&list, &ptr, func);
@@ -2985,7 +3013,7 @@ static void rna_NodeSocketInterface_init_socket(
RNA_pointer_create((ID *)ntree, &RNA_NodeSocketInterface, stemp, &ptr);
RNA_pointer_create((ID *)ntree, &RNA_Node, node, &node_ptr);
RNA_pointer_create((ID *)ntree, &RNA_NodeSocket, sock, &sock_ptr);
- /* RNA_struct_find_function(&ptr, "init_socket"); */
+ // RNA_struct_find_function(&ptr, "init_socket");
func = &rna_NodeSocketInterface_init_socket_func;
RNA_parameter_list_create(&list, &ptr, func);
@@ -3015,7 +3043,7 @@ static void rna_NodeSocketInterface_from_socket(bNodeTree *ntree,
RNA_pointer_create((ID *)ntree, &RNA_NodeSocketInterface, stemp, &ptr);
RNA_pointer_create((ID *)ntree, &RNA_Node, node, &node_ptr);
RNA_pointer_create((ID *)ntree, &RNA_NodeSocket, sock, &sock_ptr);
- /* RNA_struct_find_function(&ptr, "from_socket"); */
+ // RNA_struct_find_function(&ptr, "from_socket");
func = &rna_NodeSocketInterface_from_socket_func;
RNA_parameter_list_create(&list, &ptr, func);
@@ -4717,6 +4745,7 @@ static void def_frame(StructRNA *srna)
RNA_def_property_pointer_sdna(prop, NULL, "id");
RNA_def_property_struct_type(prop, "Text");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Text", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
@@ -5724,6 +5753,7 @@ static void def_sh_tex_pointdensity(StructRNA *srna)
NULL,
NULL);
RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "resolution", PROP_INT, PROP_NONE);
@@ -6143,6 +6173,7 @@ static void def_sh_tex_ies(StructRNA *srna)
RNA_def_property_pointer_sdna(prop, NULL, "id");
RNA_def_property_struct_type(prop, "Text");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "IES Text", "Internal IES file");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
@@ -6183,6 +6214,7 @@ static void def_sh_script(StructRNA *srna)
RNA_def_property_pointer_sdna(prop, NULL, "id");
RNA_def_property_struct_type(prop, "Text");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Script", "Internal shader script to define the shader");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_ShaderNodeScript_update");
@@ -7908,6 +7940,7 @@ static void def_cmp_movieclip(StructRNA *srna)
RNA_def_property_pointer_sdna(prop, NULL, "id");
RNA_def_property_struct_type(prop, "MovieClip");
RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Movie Clip", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
@@ -7922,6 +7955,7 @@ static void def_cmp_stabilize2d(StructRNA *srna)
RNA_def_property_pointer_sdna(prop, NULL, "id");
RNA_def_property_struct_type(prop, "MovieClip");
RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Movie Clip", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
@@ -7952,6 +7986,7 @@ static void def_cmp_moviedistortion(StructRNA *srna)
RNA_def_property_pointer_sdna(prop, NULL, "id");
RNA_def_property_struct_type(prop, "MovieClip");
RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Movie Clip", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
@@ -7981,6 +8016,7 @@ static void def_cmp_mask(StructRNA *srna)
RNA_def_property_pointer_sdna(prop, NULL, "id");
RNA_def_property_struct_type(prop, "Mask");
RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Mask", "");
prop = RNA_def_property(srna, "use_feather", PROP_BOOLEAN, PROP_NONE);
@@ -8473,6 +8509,7 @@ static void def_cmp_keyingscreen(StructRNA *srna)
RNA_def_property_pointer_sdna(prop, NULL, "id");
RNA_def_property_struct_type(prop, "MovieClip");
RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Movie Clip", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
@@ -8610,6 +8647,7 @@ static void def_cmp_trackpos(StructRNA *srna)
RNA_def_property_pointer_sdna(prop, NULL, "id");
RNA_def_property_struct_type(prop, "MovieClip");
RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Movie Clip", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
@@ -8674,6 +8712,7 @@ static void def_cmp_planetrackdeform(StructRNA *srna)
RNA_def_property_pointer_sdna(prop, NULL, "id");
RNA_def_property_struct_type(prop, "MovieClip");
RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Movie Clip", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
@@ -8804,6 +8843,7 @@ static void def_cmp_cryptomatte(StructRNA *srna)
prop, "rna_NodeCryptomatte_scene_get", "rna_NodeCryptomatte_scene_set", NULL, NULL);
RNA_def_property_struct_type(prop, "Scene");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Scene", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
@@ -8815,6 +8855,7 @@ static void def_cmp_cryptomatte(StructRNA *srna)
"rna_NodeCryptomatte_image_poll");
RNA_def_property_struct_type(prop, "Image");
RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Image", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
@@ -8914,6 +8955,7 @@ static void def_tex_image(StructRNA *srna)
RNA_def_property_pointer_sdna(prop, NULL, "id");
RNA_def_property_struct_type(prop, "Image");
RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Image", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
@@ -9067,6 +9109,26 @@ static void def_geo_triangulate(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
+static void def_geo_subdivision_surface(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeGeometrySubdivisionSurface", "storage");
+ prop = RNA_def_property(srna, "uv_smooth", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "uv_smooth");
+ RNA_def_property_enum_items(prop, rna_enum_subdivision_uv_smooth_items);
+ RNA_def_property_enum_default(prop, SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES);
+ RNA_def_property_ui_text(prop, "UV Smooth", "Controls how smoothing is applied to UVs");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "boundary_smooth", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "boundary_smooth");
+ RNA_def_property_enum_items(prop, rna_enum_subdivision_boundary_smooth_items);
+ RNA_def_property_enum_default(prop, SUBSURF_BOUNDARY_SMOOTH_ALL);
+ RNA_def_property_ui_text(prop, "Boundary Smooth", "Controls how open boundaries are smoothed");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
static void def_geo_attribute_randomize(StructRNA *srna)
{
PropertyRNA *prop;
@@ -9441,52 +9503,59 @@ static void def_geo_attribute_vector_rotate(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
-static void def_geo_curve_set_handles(StructRNA *srna)
+static void def_geo_curve_spline_type(StructRNA *srna)
{
static const EnumPropertyItem type_items[] = {
- {GEO_NODE_CURVE_HANDLE_FREE,
- "FREE",
- ICON_HANDLE_FREE,
- "Free",
- "The handle can be moved anywhere, and doesn't influence the point's other handle"},
- {GEO_NODE_CURVE_HANDLE_AUTO,
- "AUTO",
- ICON_HANDLE_AUTO,
- "Auto",
- "The location is automatically calculated to be smooth"},
- {GEO_NODE_CURVE_HANDLE_VECTOR,
- "VECTOR",
- ICON_HANDLE_VECTOR,
- "Vector",
- "The location is calculated to point to the next/previous control point"},
- {GEO_NODE_CURVE_HANDLE_ALIGN,
- "ALIGN",
- ICON_HANDLE_ALIGNED,
- "Align",
- "The location is constrained to point in the opposite direction as the other handle"},
+ {GEO_NODE_SPLINE_TYPE_BEZIER, "BEZIER", ICON_NONE, "Bezier", "Set the splines to Bezier"},
+ {GEO_NODE_SPLINE_TYPE_NURBS, "NURBS", ICON_NONE, "NURBS", "Set the splines to NURBS"},
+ {GEO_NODE_SPLINE_TYPE_POLY, "POLY", ICON_NONE, "Poly", "Set the splines to Poly"},
{0, NULL, 0, NULL, NULL}};
- static const EnumPropertyItem mode_items[] = {
- {GEO_NODE_CURVE_HANDLE_LEFT, "LEFT", ICON_NONE, "Left", "Update the left handles"},
- {GEO_NODE_CURVE_HANDLE_RIGHT, "RIGHT", ICON_NONE, "Right", "Update the right handles"},
- {0, NULL, 0, NULL, NULL}};
+ PropertyRNA *prop;
+ RNA_def_struct_sdna_from(srna, "NodeGeometryCurveSplineType", "storage");
+
+ prop = RNA_def_property(srna, "spline_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "spline_type");
+ RNA_def_property_enum_items(prop, type_items);
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+}
+static void def_geo_curve_set_handles(StructRNA *srna)
+{
PropertyRNA *prop;
RNA_def_struct_sdna_from(srna, "NodeGeometryCurveSetHandles", "storage");
prop = RNA_def_property(srna, "handle_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "handle_type");
- RNA_def_property_enum_items(prop, type_items);
+ RNA_def_property_enum_items(prop, rna_node_geometry_curve_handle_type_items);
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
- RNA_def_property_enum_items(prop, mode_items);
+ RNA_def_property_enum_items(prop, rna_node_geometry_curve_handle_side_items);
RNA_def_property_ui_text(prop, "Mode", "Whether to update left and right handles");
RNA_def_property_flag(prop, PROP_ENUM_FLAG);
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
+static void def_geo_curve_select_handles(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeGeometryCurveSelectHandles", "storage");
+
+ prop = RNA_def_property(srna, "handle_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "handle_type");
+ RNA_def_property_enum_items(prop, rna_node_geometry_curve_handle_type_items);
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+
+ prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_node_geometry_curve_handle_side_items);
+ RNA_def_property_ui_text(prop, "Mode", "Whether to check the type of left and right handles");
+ RNA_def_property_flag(prop, PROP_ENUM_FLAG);
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+}
+
static void def_geo_curve_primitive_circle(StructRNA *srna)
{
static const EnumPropertyItem mode_items[] = {
@@ -10874,6 +10943,7 @@ static void rna_def_node_socket_object(BlenderRNA *brna,
RNA_def_property_update(
prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
/* socket interface */
srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard");
@@ -10909,6 +10979,7 @@ static void rna_def_node_socket_image(BlenderRNA *brna,
RNA_def_property_update(
prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
/* socket interface */
srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard");
@@ -10959,6 +11030,7 @@ static void rna_def_node_socket_collection(BlenderRNA *brna,
RNA_def_property_update(
prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
/* socket interface */
srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard");
@@ -10994,6 +11066,7 @@ static void rna_def_node_socket_texture(BlenderRNA *brna,
RNA_def_property_update(
prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
/* socket interface */
srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard");
@@ -11031,6 +11104,7 @@ static void rna_def_node_socket_material(BlenderRNA *brna,
RNA_def_property_update(
prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
/* socket interface */
srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard");
@@ -11416,12 +11490,14 @@ static void rna_def_node(BlenderRNA *brna)
prop = RNA_def_property(srna, "inputs", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "inputs", NULL);
RNA_def_property_struct_type(prop, "NodeSocket");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Inputs", "");
rna_def_node_sockets_api(brna, prop, SOCK_IN);
prop = RNA_def_property(srna, "outputs", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "outputs", NULL);
RNA_def_property_struct_type(prop, "NodeSocket");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Outputs", "");
rna_def_node_sockets_api(brna, prop, SOCK_OUT);
@@ -11868,6 +11944,7 @@ static void rna_def_nodetree(BlenderRNA *brna)
prop = RNA_def_property(srna, "nodes", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "nodes", NULL);
RNA_def_property_struct_type(prop, "Node");
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Nodes", "");
rna_def_nodetree_nodes_api(brna, prop);
@@ -11885,6 +11962,7 @@ static void rna_def_nodetree(BlenderRNA *brna)
RNA_def_property_pointer_funcs(
prop, NULL, NULL, NULL, "rna_GPencil_datablocks_annotations_poll");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Grease Pencil Data", "Grease Pencil data-block");
RNA_def_property_update(prop, NC_NODE, NULL);
diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c
index ed681291e29..0f6b89722a4 100644
--- a/source/blender/makesrna/intern/rna_object.c
+++ b/source/blender/makesrna/intern/rna_object.c
@@ -218,6 +218,12 @@ static EnumPropertyItem instance_items_pointcloud[] = {
{OB_DUPLIVERTS, "POINTS", 0, "Points", "Instantiate child objects on all points"},
{0, NULL, 0, NULL, NULL},
};
+
+static EnumPropertyItem instance_items_empty[] = {
+ {0, "NONE", 0, "None", ""},
+ INSTANCE_ITEM_COLLECTION,
+ {0, NULL, 0, NULL, NULL},
+};
#endif
#undef INSTANCE_ITEMS_SHARED
#undef INSTANCE_ITEM_COLLECTION
@@ -751,7 +757,7 @@ static const EnumPropertyItem *rna_Object_instance_type_itemf(bContext *UNUSED(C
const EnumPropertyItem *item;
if (ob->type == OB_EMPTY) {
- item = instance_items;
+ item = instance_items_empty;
}
else if (ob->type == OB_POINTCLOUD) {
item = instance_items_pointcloud;
@@ -2927,6 +2933,97 @@ static void rna_def_object_lineart(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_object_lineart_update");
}
+static void rna_def_object_visibility(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ /* Hide options. */
+ prop = RNA_def_property(srna, "hide_viewport", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "visibility_flag", OB_HIDE_VIEWPORT);
+ RNA_def_property_ui_text(prop, "Disable in Viewports", "Globally disable in viewports");
+ RNA_def_property_ui_icon(prop, ICON_RESTRICT_VIEW_OFF, -1);
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_hide_update");
+
+ prop = RNA_def_property(srna, "hide_select", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "visibility_flag", OB_HIDE_SELECT);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_ui_text(prop, "Disable Selection", "Disable selection in viewport");
+ RNA_def_property_ui_icon(prop, ICON_RESTRICT_SELECT_OFF, -1);
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_hide_update");
+
+ prop = RNA_def_property(srna, "hide_render", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "visibility_flag", OB_HIDE_RENDER);
+ RNA_def_property_ui_text(prop, "Disable in Renders", "Globally disable in renders");
+ RNA_def_property_ui_icon(prop, ICON_RESTRICT_RENDER_OFF, -1);
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_hide_update");
+
+ /* Instancer options. */
+ prop = RNA_def_property(srna, "show_instancer_for_render", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "duplicator_visibility_flag", OB_DUPLI_FLAG_RENDER);
+ RNA_def_property_ui_text(prop, "Render Instancer", "Make instancer visible when rendering");
+ RNA_def_property_update(
+ prop, NC_OBJECT | ND_DRAW, "rna_Object_duplicator_visibility_flag_update");
+
+ prop = RNA_def_property(srna, "show_instancer_for_viewport", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "duplicator_visibility_flag", OB_DUPLI_FLAG_VIEWPORT);
+ RNA_def_property_ui_text(prop, "Display Instancer", "Make instancer visible in the viewport");
+ RNA_def_property_update(
+ prop, NC_OBJECT | ND_DRAW, "rna_Object_duplicator_visibility_flag_update");
+
+ /* Ray visibility. */
+ prop = RNA_def_property(srna, "visible_camera", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "visibility_flag", OB_HIDE_CAMERA);
+ RNA_def_property_ui_text(prop, "Camera Visibility", "Object visibility to camera rays");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_internal_update_draw");
+
+ prop = RNA_def_property(srna, "visible_diffuse", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "visibility_flag", OB_HIDE_DIFFUSE);
+ RNA_def_property_ui_text(prop, "Diffuse Visibility", "Object visibility to diffuse rays");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_internal_update_draw");
+
+ prop = RNA_def_property(srna, "visible_glossy", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "visibility_flag", OB_HIDE_GLOSSY);
+ RNA_def_property_ui_text(prop, "Glossy Visibility", "Object visibility to glossy rays");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_internal_update_draw");
+
+ prop = RNA_def_property(srna, "visible_transmission", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "visibility_flag", OB_HIDE_TRANSMISSION);
+ RNA_def_property_ui_text(
+ prop, "Transmission Visibility", "Object visibility to transmission rays");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_internal_update_draw");
+
+ prop = RNA_def_property(srna, "visible_volume_scatter", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "visibility_flag", OB_HIDE_VOLUME_SCATTER);
+ RNA_def_property_ui_text(
+ prop, "Volume Scatter Visibility", "Object visibility to volume scattering rays");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_internal_update_draw");
+
+ prop = RNA_def_property(srna, "visible_shadow", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "visibility_flag", OB_HIDE_SHADOW);
+ RNA_def_property_ui_text(prop, "Shadow Visibility", "Object visibility to shadow rays");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_internal_update_draw");
+
+ /* Holdout and shadow catcher. */
+ prop = RNA_def_property(srna, "is_holdout", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "visibility_flag", OB_HOLDOUT);
+ RNA_def_property_ui_text(
+ prop,
+ "Holdout",
+ "Render objects as a holdout or matte, creating a hole in the image with zero alpha, to "
+ "fill out in compositing with real footage or another render");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_hide_update");
+
+ prop = RNA_def_property(srna, "is_shadow_catcher", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "visibility_flag", OB_SHADOW_CATCHER);
+ RNA_def_property_ui_text(
+ prop,
+ "Shadow Catcher",
+ "Only render shadows and reflections on this object, for compositing renders into real "
+ "footage. Objects with this setting are considered to already exist in the footage, "
+ "objects without it are synthetic objects being composited into it");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_internal_update_draw");
+}
+
static void rna_def_object(BlenderRNA *brna)
{
StructRNA *srna;
@@ -3506,37 +3603,7 @@ static void rna_def_object(BlenderRNA *brna)
RNA_def_property_struct_type(prop, "RigidBodyConstraint");
RNA_def_property_ui_text(prop, "Rigid Body Constraint", "Constraint constraining rigid bodies");
- /* restrict */
- prop = RNA_def_property(srna, "hide_viewport", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "restrictflag", OB_RESTRICT_VIEWPORT);
- RNA_def_property_ui_text(prop, "Disable in Viewports", "Globally disable in viewports");
- RNA_def_property_ui_icon(prop, ICON_RESTRICT_VIEW_OFF, -1);
- RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_hide_update");
-
- prop = RNA_def_property(srna, "hide_select", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "restrictflag", OB_RESTRICT_SELECT);
- RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
- RNA_def_property_ui_text(prop, "Disable Selection", "Disable selection in viewport");
- RNA_def_property_ui_icon(prop, ICON_RESTRICT_SELECT_OFF, -1);
- RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_hide_update");
-
- prop = RNA_def_property(srna, "hide_render", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "restrictflag", OB_RESTRICT_RENDER);
- RNA_def_property_ui_text(prop, "Disable in Renders", "Globally disable in renders");
- RNA_def_property_ui_icon(prop, ICON_RESTRICT_RENDER_OFF, -1);
- RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_hide_update");
-
- prop = RNA_def_property(srna, "show_instancer_for_render", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "duplicator_visibility_flag", OB_DUPLI_FLAG_RENDER);
- RNA_def_property_ui_text(prop, "Render Instancer", "Make instancer visible when rendering");
- RNA_def_property_update(
- prop, NC_OBJECT | ND_DRAW, "rna_Object_duplicator_visibility_flag_update");
-
- prop = RNA_def_property(srna, "show_instancer_for_viewport", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "duplicator_visibility_flag", OB_DUPLI_FLAG_VIEWPORT);
- RNA_def_property_ui_text(prop, "Display Instancer", "Make instancer visible in the viewport");
- RNA_def_property_update(
- prop, NC_OBJECT | ND_DRAW, "rna_Object_duplicator_visibility_flag_update");
+ rna_def_object_visibility(srna);
/* instancing */
prop = RNA_def_property(srna, "instance_type", PROP_ENUM, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c
index de4cfb2b61a..f732e14d905 100644
--- a/source/blender/makesrna/intern/rna_particle.c
+++ b/source/blender/makesrna/intern/rna_particle.c
@@ -1851,20 +1851,20 @@ static void rna_def_particle(BlenderRNA *brna)
prop = RNA_def_property(srna, "birth_time", PROP_FLOAT, PROP_TIME);
RNA_def_property_float_sdna(prop, NULL, "time");
- /* RNA_def_property_range(prop, lowerLimitf, upperLimitf); */
+ // RNA_def_property_range(prop, lowerLimitf, upperLimitf);
RNA_def_property_ui_text(prop, "Birth Time", "");
prop = RNA_def_property(srna, "lifetime", PROP_FLOAT, PROP_TIME);
- /* RNA_def_property_range(prop, lowerLimitf, upperLimitf); */
+ // RNA_def_property_range(prop, lowerLimitf, upperLimitf);
RNA_def_property_ui_text(prop, "Lifetime", "");
prop = RNA_def_property(srna, "die_time", PROP_FLOAT, PROP_TIME);
RNA_def_property_float_sdna(prop, NULL, "dietime");
- /* RNA_def_property_range(prop, lowerLimitf, upperLimitf); */
+ // RNA_def_property_range(prop, lowerLimitf, upperLimitf);
RNA_def_property_ui_text(prop, "Die Time", "");
prop = RNA_def_property(srna, "size", PROP_FLOAT, PROP_NONE);
- /* RNA_def_property_range(prop, lowerLimitf, upperLimitf); */
+ // RNA_def_property_range(prop, lowerLimitf, upperLimitf);
RNA_def_property_ui_text(prop, "Size", "");
/* */
@@ -3658,7 +3658,7 @@ static void rna_def_particle_system(BlenderRNA *brna)
/* access to particle settings is redirected through functions */
/* to allow proper id-buttons functionality */
prop = RNA_def_property(srna, "settings", PROP_POINTER, PROP_NONE);
- /*RNA_def_property_pointer_sdna(prop, NULL, "part"); */
+ // RNA_def_property_pointer_sdna(prop, NULL, "part");
RNA_def_property_struct_type(prop, "ParticleSettings");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NEVER_NULL);
RNA_def_property_pointer_funcs(
diff --git a/source/blender/makesrna/intern/rna_render.c b/source/blender/makesrna/intern/rna_render.c
index 73924c45d52..4400d198b4a 100644
--- a/source/blender/makesrna/intern/rna_render.c
+++ b/source/blender/makesrna/intern/rna_render.c
@@ -896,6 +896,12 @@ static void rna_def_render_engine(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
RNA_def_property_ui_text(prop, "Use Stereo Viewport", "Support rendering stereo 3D viewport");
+ prop = RNA_def_property(srna, "bl_use_alembic_procedural", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "type->flag", RE_USE_ALEMBIC_PROCEDURAL);
+ RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
+ RNA_def_property_ui_text(
+ prop, "Use Alembic Procedural", "Support loading Alembic data at render time");
+
RNA_define_verify_sdna(1);
}
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index 7d7eec6f256..9d158761a21 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -616,6 +616,7 @@ const EnumPropertyItem rna_enum_transform_orientation_items[] = {
# include "BLI_string_utils.h"
# include "DNA_anim_types.h"
+# include "DNA_cachefile_types.h"
# include "DNA_color_types.h"
# include "DNA_mesh_types.h"
# include "DNA_node_types.h"
@@ -1619,6 +1620,11 @@ static void rna_RenderSettings_engine_update(Main *bmain,
ED_render_engine_changed(bmain, true);
}
+static void rna_Scene_update_render_engine(Main *bmain)
+{
+ ED_render_engine_changed(bmain, true);
+}
+
static bool rna_RenderSettings_multiple_engines_get(PointerRNA *UNUSED(ptr))
{
return (BLI_listbase_count(&R_engines) > 1);
@@ -7664,6 +7670,7 @@ void RNA_def_scene(BlenderRNA *brna)
prop = RNA_def_property(srna, "node_tree", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "nodetree");
RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Node Tree", "Compositing node tree");
prop = RNA_def_property(srna, "use_nodes", PROP_BOOLEAN, PROP_NONE);
@@ -7835,6 +7842,10 @@ void RNA_def_scene(BlenderRNA *brna)
RNA_def_property_update(prop, NC_SCENE, NULL);
RNA_def_property_update(prop, NC_SCENE, "rna_Scene_volume_update");
+ func = RNA_def_function(srna, "update_render_engine", "rna_Scene_update_render_engine");
+ RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_USE_MAIN);
+ RNA_def_function_ui_description(func, "Trigger a render engine update");
+
/* Statistics */
func = RNA_def_function(srna, "statistics", "rna_Scene_statistics_string_get");
RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS);
@@ -7895,6 +7906,7 @@ void RNA_def_scene(BlenderRNA *brna)
RNA_def_property_pointer_sdna(prop, NULL, "master_collection");
RNA_def_property_struct_type(prop, "Collection");
RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop,
"Collection",
"Scene root collection that owns all the objects and other collections "
diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c
index 74fe2a26505..dad77b4aad5 100644
--- a/source/blender/makesrna/intern/rna_sequencer.c
+++ b/source/blender/makesrna/intern/rna_sequencer.c
@@ -148,9 +148,9 @@ static void rna_Sequence_invalidate_preprocessed_update(Main *UNUSED(bmain),
}
}
-static void rna_Sequence_invalidate_composite_update(Main *UNUSED(bmain),
- Scene *UNUSED(scene),
- PointerRNA *ptr)
+static void UNUSED_FUNCTION(rna_Sequence_invalidate_composite_update)(Main *UNUSED(bmain),
+ Scene *UNUSED(scene),
+ PointerRNA *ptr)
{
Scene *scene = (Scene *)ptr->owner_id;
Editing *ed = SEQ_editing_get(scene, false);
@@ -1900,7 +1900,7 @@ static void rna_def_sequence(BlenderRNA *brna)
/* stupid 0-100 -> 0-1 */
RNA_def_property_float_funcs(prop, "rna_Sequence_opacity_get", "rna_Sequence_opacity_set", NULL);
RNA_def_property_update(
- prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_composite_update");
+ prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update");
prop = RNA_def_property(srna, "effect_fader", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_range(prop, 0.0f, 1.0f);
diff --git a/source/blender/makesrna/intern/rna_sequencer_api.c b/source/blender/makesrna/intern/rna_sequencer_api.c
index 4895ab11618..264ccccd350 100644
--- a/source/blender/makesrna/intern/rna_sequencer_api.c
+++ b/source/blender/makesrna/intern/rna_sequencer_api.c
@@ -310,7 +310,8 @@ static Sequence *rna_Sequences_new_movie(ID *id,
SEQ_add_load_data_init(&load_data, name, file, frame_start, channel);
load_data.fit_method = fit_method;
load_data.allow_invalid_file = true;
- Sequence *seq = SEQ_add_movie_strip(bmain, scene, seqbase, &load_data);
+ double video_start_offset;
+ Sequence *seq = SEQ_add_movie_strip(bmain, scene, seqbase, &load_data, &video_start_offset);
DEG_relations_tag_update(bmain);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
@@ -359,7 +360,7 @@ static Sequence *rna_Sequences_new_sound(ID *id,
SeqLoadData load_data;
SEQ_add_load_data_init(&load_data, name, file, frame_start, channel);
load_data.allow_invalid_file = true;
- Sequence *seq = SEQ_add_sound_strip(bmain, scene, seqbase, &load_data);
+ Sequence *seq = SEQ_add_sound_strip(bmain, scene, seqbase, &load_data, 0.0f);
if (seq == NULL) {
BKE_report(reports, RPT_ERROR, "Sequences.new_sound: unable to open sound file");
diff --git a/source/blender/makesrna/intern/rna_simulation.c b/source/blender/makesrna/intern/rna_simulation.c
index cc9a4bec2e7..6f5041c9ed1 100644
--- a/source/blender/makesrna/intern/rna_simulation.c
+++ b/source/blender/makesrna/intern/rna_simulation.c
@@ -43,6 +43,7 @@ static void rna_def_simulation(BlenderRNA *brna)
prop = RNA_def_property(srna, "node_tree", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "nodetree");
RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Node Tree", "Node tree defining the simulation");
/* common */
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index 1d4318602c2..5ab13e7b44e 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -778,6 +778,19 @@ static void rna_Space_show_region_toolbar_update(bContext *C, PointerRNA *ptr)
rna_Space_bool_from_region_flag_update_by_type(C, ptr, RGN_TYPE_TOOLS, RGN_FLAG_HIDDEN);
}
+static bool rna_Space_show_region_tool_props_get(PointerRNA *ptr)
+{
+ return !rna_Space_bool_from_region_flag_get_by_type(ptr, RGN_TYPE_TOOL_PROPS, RGN_FLAG_HIDDEN);
+}
+static void rna_Space_show_region_tool_props_set(PointerRNA *ptr, bool value)
+{
+ rna_Space_bool_from_region_flag_set_by_type(ptr, RGN_TYPE_TOOL_PROPS, RGN_FLAG_HIDDEN, !value);
+}
+static void rna_Space_show_region_tool_props_update(bContext *C, PointerRNA *ptr)
+{
+ rna_Space_bool_from_region_flag_update_by_type(C, ptr, RGN_TYPE_TOOL_PROPS, RGN_FLAG_HIDDEN);
+}
+
/* Channels Region. */
static bool rna_Space_show_region_channels_get(PointerRNA *ptr)
{
@@ -2569,13 +2582,13 @@ static int rna_FileAssetSelectParams_asset_library_get(PointerRNA *ptr)
/* Just an extra sanity check to ensure this isn't somehow called for RNA_FileSelectParams. */
BLI_assert(ptr->type == &RNA_FileAssetSelectParams);
- return ED_asset_library_reference_to_enum_value(&params->asset_library);
+ return ED_asset_library_reference_to_enum_value(&params->asset_library_ref);
}
static void rna_FileAssetSelectParams_asset_library_set(PointerRNA *ptr, int value)
{
FileAssetSelectParams *params = ptr->data;
- params->asset_library = ED_asset_library_reference_from_enum_value(value);
+ params->asset_library_ref = ED_asset_library_reference_from_enum_value(value);
}
static void rna_FileAssetSelectParams_asset_category_set(PointerRNA *ptr, uint64_t value)
@@ -3196,6 +3209,10 @@ static void rna_def_space_generic_show_region_toggles(StructRNA *srna, int regio
region_type_mask &= ~(1 << RGN_TYPE_TOOLS);
DEF_SHOW_REGION_PROPERTY(show_region_toolbar, "Toolbar", "");
}
+ if (region_type_mask & (1 << RGN_TYPE_TOOL_PROPS)) {
+ region_type_mask &= ~(1 << RGN_TYPE_TOOL_PROPS);
+ DEF_SHOW_REGION_PROPERTY(show_region_tool_props, "Toolbar", "");
+ }
if (region_type_mask & (1 << RGN_TYPE_CHANNELS)) {
region_type_mask &= ~(1 << RGN_TYPE_CHANNELS);
DEF_SHOW_REGION_PROPERTY(show_region_channels, "Channels", "");
@@ -4346,6 +4363,21 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna)
RNA_def_property_float_default(prop, 0.02);
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+ prop = RNA_def_property(srna, "normals_constant_screen_size", PROP_FLOAT, PROP_PIXEL);
+ RNA_def_property_float_sdna(prop, NULL, "overlay.normals_constant_screen_size");
+ RNA_def_property_ui_text(prop, "Normal Screen Size", "Screen size for normals in the 3D view");
+ RNA_def_property_range(prop, 0.0, 100000.0);
+ RNA_def_property_ui_range(prop, 1.0, 100.0, 50, 0);
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+
+ prop = RNA_def_property(srna, "use_normals_constant_screen_size", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(
+ prop, NULL, "overlay.edit_flag", V3D_OVERLAY_EDIT_CONSTANT_SCREEN_SIZE_NORMALS);
+ RNA_def_property_ui_text(prop,
+ "Constant Screen Size Normals",
+ "Keep size of normals constant in relation to 3D view");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+
prop = RNA_def_property(srna, "backwire_opacity", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "overlay.backwire_opacity");
RNA_def_property_ui_text(prop, "Backwire Opacity", "Opacity when rendering transparent wires");
@@ -6546,7 +6578,8 @@ static void rna_def_space_filebrowser(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "SpaceFile");
RNA_def_struct_ui_text(srna, "Space File Browser", "File browser space data");
- rna_def_space_generic_show_region_toggles(srna, (1 << RGN_TYPE_TOOLS) | (1 << RGN_TYPE_UI));
+ rna_def_space_generic_show_region_toggles(
+ srna, (1 << RGN_TYPE_TOOLS) | (1 << RGN_TYPE_UI) | (1 << RGN_TYPE_TOOL_PROPS));
prop = RNA_def_property(srna, "browse_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_space_file_browse_mode_items);
@@ -7297,6 +7330,13 @@ static void rna_def_space_clip(BlenderRNA *brna)
RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_MOVIECLIP);
RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, NULL);
+ /* transform */
+ prop = RNA_def_property(srna, "cursor_location", PROP_FLOAT, PROP_XYZ);
+ RNA_def_property_float_sdna(prop, NULL, "cursor");
+ RNA_def_property_array(prop, 2);
+ RNA_def_property_ui_text(prop, "2D Cursor Location", "2D cursor location for this view");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_CLIP, NULL);
+
/* pivot point */
prop = RNA_def_property(srna, "pivot_point", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "around");
diff --git a/source/blender/makesrna/intern/rna_speaker.c b/source/blender/makesrna/intern/rna_speaker.c
index 43f0d27f514..2dce9d32006 100644
--- a/source/blender/makesrna/intern/rna_speaker.c
+++ b/source/blender/makesrna/intern/rna_speaker.c
@@ -55,7 +55,9 @@ static void rna_def_speaker(BlenderRNA *brna)
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_ui_text(prop, "Mute", "Mute the speaker");
RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_SOUND);
- /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */
+# if 0
+ RNA_def_property_update(prop, 0, "rna_Speaker_update");
+# endif
prop = RNA_def_property(srna, "sound", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "Sound");
@@ -63,24 +65,30 @@ static void rna_def_speaker(BlenderRNA *brna)
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Sound", "Sound data-block used by this speaker");
- /* RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_sound_set", NULL); */
- /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */
+# if 0
+ RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_sound_set", NULL);
+ RNA_def_property_update(prop, 0, "rna_Speaker_update");
+# endif
prop = RNA_def_property(srna, "volume_max", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_text(
prop, "Maximum Volume", "Maximum volume, no matter how near the object is");
- /* RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_volume_max_set", NULL); */
- /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */
+# if 0
+ RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_volume_max_set", NULL);
+ RNA_def_property_update(prop, 0, "rna_Speaker_update");
+# endif
prop = RNA_def_property(srna, "volume_min", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_text(
prop, "Minimum Volume", "Minimum volume, no matter how far away the object is");
- /* RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_volume_min_set", NULL); */
- /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */
+# if 0
+ RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_volume_min_set", NULL);
+ RNA_def_property_update(prop, 0, "rna_Speaker_update");
+# endif
prop = RNA_def_property(srna, "distance_max", PROP_FLOAT, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
@@ -89,24 +97,30 @@ static void rna_def_speaker(BlenderRNA *brna)
prop,
"Maximum Distance",
"Maximum distance for volume calculation, no matter how far away the object is");
- /* RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_distance_max_set", NULL); */
- /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */
+# if 0
+ RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_distance_max_set", NULL);
+ RNA_def_property_update(prop, 0, "rna_Speaker_update");
+# endif
prop = RNA_def_property(srna, "distance_reference", PROP_FLOAT, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_range(prop, 0.0f, FLT_MAX);
RNA_def_property_ui_text(
prop, "Reference Distance", "Reference distance at which volume is 100%");
- /* RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_distance_reference_set", NULL); */
- /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */
+# if 0
+ RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_distance_reference_set", NULL);
+ RNA_def_property_update(prop, 0, "rna_Speaker_update");
+# endif
prop = RNA_def_property(srna, "attenuation", PROP_FLOAT, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_range(prop, 0.0f, FLT_MAX);
RNA_def_property_ui_text(
prop, "Attenuation", "How strong the distance affects volume, depending on distance model");
- /* RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_attenuation_set", NULL); */
- /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */
+# if 0
+ RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_attenuation_set", NULL);
+ RNA_def_property_update(prop, 0, "rna_Speaker_update");
+# endif
prop = RNA_def_property(srna, "cone_angle_outer", PROP_FLOAT, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
@@ -116,8 +130,10 @@ static void rna_def_speaker(BlenderRNA *brna)
"Outer Cone Angle",
"Angle of the outer cone, in degrees, outside this cone the volume is "
"the outer cone volume, between inner and outer cone the volume is interpolated");
- /* RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_cone_angle_outer_set", NULL); */
- /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */
+# if 0
+ RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_cone_angle_outer_set", NULL);
+ RNA_def_property_update(prop, 0, "rna_Speaker_update");
+# endif
prop = RNA_def_property(srna, "cone_angle_inner", PROP_FLOAT, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
@@ -126,29 +142,37 @@ static void rna_def_speaker(BlenderRNA *brna)
prop,
"Inner Cone Angle",
"Angle of the inner cone, in degrees, inside the cone the volume is 100%");
- /* RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_cone_angle_inner_set", NULL); */
- /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */
+# if 0
+ RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_cone_angle_inner_set", NULL);
+ RNA_def_property_update(prop, 0, "rna_Speaker_update");
+# endif
prop = RNA_def_property(srna, "cone_volume_outer", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_text(prop, "Outer Cone Volume", "Volume outside the outer cone");
- /* RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_cone_volume_outer_set", NULL); */
- /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */
+# if 0
+ RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_cone_volume_outer_set", NULL);
+ RNA_def_property_update(prop, 0, "rna_Speaker_update");
+# endif
prop = RNA_def_property(srna, "volume", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_text(prop, "Volume", "How loud the sound is");
RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_SOUND);
- /* RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_volume_set", NULL); */
- /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */
+# if 0
+ RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_volume_set", NULL);
+ RNA_def_property_update(prop, 0, "rna_Speaker_update");
+# endif
prop = RNA_def_property(srna, "pitch", PROP_FLOAT, PROP_NONE);
RNA_def_property_range(prop, 0.1f, 10.0f);
RNA_def_property_ui_text(prop, "Pitch", "Playback pitch of the sound");
RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_SOUND);
- /* RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_pitch_set", NULL); */
- /* RNA_def_property_update(prop, 0, "rna_Speaker_update"); */
+# if 0
+ RNA_def_property_float_funcs(prop, NULL, "rna_Speaker_pitch_set", NULL);
+ RNA_def_property_update(prop, 0, "rna_Speaker_update");
+# endif
/* common */
rna_def_animdata_common(srna);
diff --git a/source/blender/makesrna/intern/rna_text.c b/source/blender/makesrna/intern/rna_text.c
index 1351c027004..52f762e5494 100644
--- a/source/blender/makesrna/intern/rna_text.c
+++ b/source/blender/makesrna/intern/rna_text.c
@@ -240,8 +240,7 @@ static void rna_def_text(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_module", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flags", TXT_ISSCRIPT);
- RNA_def_property_ui_text(
- prop, "Register", "Run this text as a script on loading, Text name must end with \".py\"");
+ RNA_def_property_ui_text(prop, "Register", "Run this text as a Python script on loading");
prop = RNA_def_property(srna, "indentation", PROP_ENUM, PROP_NONE); /* as an enum */
RNA_def_property_enum_bitflag_sdna(prop, NULL, "flags");
diff --git a/source/blender/makesrna/intern/rna_texture.c b/source/blender/makesrna/intern/rna_texture.c
index 128f1cb1e21..5a74cfa9964 100644
--- a/source/blender/makesrna/intern/rna_texture.c
+++ b/source/blender/makesrna/intern/rna_texture.c
@@ -1642,6 +1642,7 @@ static void rna_def_texture(BlenderRNA *brna)
prop = RNA_def_property(srna, "node_tree", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "nodetree");
RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Node Tree", "Node tree for node-based textures");
RNA_def_property_update(prop, 0, "rna_Texture_nodes_update");
diff --git a/source/blender/makesrna/intern/rna_texture_api.c b/source/blender/makesrna/intern/rna_texture_api.c
index 42b3e4420c1..83c1efd55bc 100644
--- a/source/blender/makesrna/intern/rna_texture_api.c
+++ b/source/blender/makesrna/intern/rna_texture_api.c
@@ -59,14 +59,36 @@ void RNA_api_texture(StructRNA *srna)
PropertyRNA *parm;
func = RNA_def_function(srna, "evaluate", "texture_evaluate");
- RNA_def_function_ui_description(func, "Evaluate the texture at the coordinates given");
+ RNA_def_function_ui_description(
+ func, "Evaluate the texture at the a given coordinate and returns the result");
- parm = RNA_def_float_vector(func, "value", 3, NULL, -FLT_MAX, FLT_MAX, "", "", -1e4, 1e4);
+ parm = RNA_def_float_vector(
+ func,
+ "value",
+ 3,
+ NULL,
+ -FLT_MAX,
+ FLT_MAX,
+ "The coordinates (x,y,z) of the texture, in case of a 3D texture, the z value is the slice "
+ "of the texture that is evaluated. For 2D textures such as images, the z value is ignored",
+ "",
+ -1e4,
+ 1e4);
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
/* return location and normal */
parm = RNA_def_float_vector(
- func, "result", 4, NULL, -FLT_MAX, FLT_MAX, "Result", NULL, -1e4, 1e4);
+ func,
+ "result",
+ 4,
+ NULL,
+ -FLT_MAX,
+ FLT_MAX,
+ "The result of the texture where (x,y,z,w) are (red, green, blue, intensity). For greyscale "
+ "textures, often intensity only will be used",
+ NULL,
+ -1e4,
+ 1e4);
RNA_def_parameter_flags(parm, PROP_THICK_WRAP, 0);
RNA_def_function_output(func, parm);
}
diff --git a/source/blender/makesrna/intern/rna_ui.c b/source/blender/makesrna/intern/rna_ui.c
index a88b100435a..c506a533032 100644
--- a/source/blender/makesrna/intern/rna_ui.c
+++ b/source/blender/makesrna/intern/rna_ui.c
@@ -1458,7 +1458,7 @@ static void rna_def_panel(BlenderRNA *brna)
RNA_def_property_string_sdna(prop, NULL, "type->description");
RNA_def_property_string_maxlength(prop, RNA_DYN_DESCR_MAX); /* else it uses the pointer size! */
RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Panel_bl_description_set");
- /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */
+ // RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
RNA_def_property_clear_flag(prop, PROP_NEVER_NULL); /* check for NULL */
@@ -1820,7 +1820,7 @@ static void rna_def_menu(BlenderRNA *brna)
RNA_def_property_string_sdna(prop, NULL, "type->description");
RNA_def_property_string_maxlength(prop, RNA_DYN_DESCR_MAX); /* else it uses the pointer size! */
RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Menu_bl_description_set");
- /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */
+ // RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
RNA_def_property_clear_flag(prop, PROP_NEVER_NULL); /* check for NULL */
diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c
index d29a90a1886..73811924c23 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -1118,12 +1118,6 @@ static void rna_def_userdef_theme_ui_font_style(BlenderRNA *brna)
StructRNA *srna;
PropertyRNA *prop;
- static const EnumPropertyItem font_kerning_style[] = {
- {0, "UNFITTED", 0, "Unfitted", "Use scaled but un-grid-fitted kerning distances"},
- {1, "FITTED", 0, "Fitted", "Use scaled and grid-fitted kerning distances"},
- {0, NULL, 0, NULL, NULL},
- };
-
srna = RNA_def_struct(brna, "ThemeFontStyle", NULL);
RNA_def_struct_sdna(srna, "uiFontStyle");
RNA_def_struct_clear_flag(srna, STRUCT_UNDO);
@@ -1134,12 +1128,6 @@ static void rna_def_userdef_theme_ui_font_style(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Points", "Font size in points");
RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
- prop = RNA_def_property(srna, "font_kerning_style", PROP_ENUM, PROP_NONE);
- RNA_def_property_enum_sdna(prop, NULL, "kerning");
- RNA_def_property_enum_items(prop, font_kerning_style);
- RNA_def_property_ui_text(prop, "Kerning Style", "Which style to use for font kerning");
- RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
-
prop = RNA_def_property(srna, "shadow", PROP_INT, PROP_PIXEL);
RNA_def_property_range(prop, 0, 5);
RNA_def_property_ui_text(prop, "Shadow Size", "Shadow size (0, 3 and 5 supported)");
@@ -6155,7 +6143,7 @@ static void rna_def_userdef_filepaths(BlenderRNA *brna)
prop,
"Python Scripts Directory",
"Alternate script path, matching the default layout with subdirectories: "
- "startup, addons, modules, and presets (requires restart)");
+ "startup, add-ons, modules, and presets (requires restart)");
/* TODO: editing should reset sys.path! */
prop = RNA_def_property(srna, "i18n_branches_directory", PROP_STRING, PROP_DIRPATH);
@@ -6297,12 +6285,12 @@ static void rna_def_userdef_experimental(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "Sculpt Mode Tilt Support", "Support for pen tablet tilt events in Sculpt Mode");
- prop = RNA_def_property(srna, "use_asset_browser", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "use_asset_browser", 1);
- RNA_def_property_ui_text(
- prop,
- "Asset Browser",
- "Enable Asset Browser editor and operators to manage data-blocks as asset");
+ prop = RNA_def_property(srna, "use_extended_asset_browser", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_ui_text(prop,
+ "Extended Asset Browser",
+ "Enable Asset Browser editor and operators to manage regular "
+ "data-blocks as assets, not just poses");
+ RNA_def_property_update(prop, 0, "rna_userdef_ui_update");
prop = RNA_def_property(srna, "use_override_templates", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "use_override_templates", 1);
diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c
index 667f3822935..b910648495b 100644
--- a/source/blender/makesrna/intern/rna_wm.c
+++ b/source/blender/makesrna/intern/rna_wm.c
@@ -1915,7 +1915,7 @@ static void rna_def_operator(BlenderRNA *brna)
/* Without setting the length the pointer size would be used. -3 because `.` -> `_OT_`. */
RNA_def_property_string_maxlength(prop, OP_MAX_TYPENAME - 3);
RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_idname_set");
- /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */
+ // RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_flag(prop, PROP_REGISTER);
RNA_def_struct_name_property(srna, prop);
@@ -1923,7 +1923,7 @@ static void rna_def_operator(BlenderRNA *brna)
RNA_def_property_string_sdna(prop, NULL, "type->name");
RNA_def_property_string_maxlength(prop, RNA_DYN_DESCR_MAX); /* else it uses the pointer size! */
RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_label_set");
- /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */
+ // RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_flag(prop, PROP_REGISTER);
prop = RNA_def_property(srna, "bl_translation_context", PROP_STRING, PROP_NONE);
@@ -1943,7 +1943,7 @@ static void rna_def_operator(BlenderRNA *brna)
"rna_Operator_bl_description_get",
"rna_Operator_bl_description_length",
"rna_Operator_bl_description_set");
- /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */
+ // RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
prop = RNA_def_property(srna, "bl_undo_group", PROP_STRING, PROP_NONE);
@@ -1953,7 +1953,7 @@ static void rna_def_operator(BlenderRNA *brna)
"rna_Operator_bl_undo_group_get",
"rna_Operator_bl_undo_group_length",
"rna_Operator_bl_undo_group_set");
- /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */
+ // RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
prop = RNA_def_property(srna, "bl_options", PROP_ENUM, PROP_NONE);
@@ -2013,7 +2013,7 @@ static void rna_def_macro_operator(BlenderRNA *brna)
RNA_def_property_string_sdna(prop, NULL, "type->idname");
RNA_def_property_string_maxlength(prop, OP_MAX_TYPENAME); /* else it uses the pointer size! */
RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_idname_set");
- /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */
+ // RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_flag(prop, PROP_REGISTER);
RNA_def_struct_name_property(srna, prop);
@@ -2021,7 +2021,7 @@ static void rna_def_macro_operator(BlenderRNA *brna)
RNA_def_property_string_sdna(prop, NULL, "type->name");
RNA_def_property_string_maxlength(prop, RNA_DYN_DESCR_MAX); /* else it uses the pointer size! */
RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_label_set");
- /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */
+ // RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_flag(prop, PROP_REGISTER);
prop = RNA_def_property(srna, "bl_translation_context", PROP_STRING, PROP_NONE);
@@ -2041,7 +2041,7 @@ static void rna_def_macro_operator(BlenderRNA *brna)
"rna_Operator_bl_description_get",
"rna_Operator_bl_description_length",
"rna_Operator_bl_description_set");
- /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */
+ // RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
prop = RNA_def_property(srna, "bl_undo_group", PROP_STRING, PROP_NONE);
@@ -2051,7 +2051,7 @@ static void rna_def_macro_operator(BlenderRNA *brna)
"rna_Operator_bl_undo_group_get",
"rna_Operator_bl_undo_group_length",
"rna_Operator_bl_undo_group_set");
- /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */
+ // RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
prop = RNA_def_property(srna, "bl_options", PROP_ENUM, PROP_NONE);
@@ -2073,11 +2073,13 @@ static void rna_def_operator_type_macro(BlenderRNA *brna)
srna, "Operator Macro", "Storage of a sub operator in a macro after it has been added");
RNA_def_struct_sdna(srna, "wmOperatorTypeMacro");
- /* prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); */
- /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */
- /* RNA_def_property_string_sdna(prop, NULL, "idname"); */
- /* RNA_def_property_ui_text(prop, "Name", "Name of the sub operator"); */
- /* RNA_def_struct_name_property(srna, prop); */
+# if 0
+ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_string_sdna(prop, NULL, "idname");
+ RNA_def_property_ui_text(prop, "Name", "Name of the sub operator");
+ RNA_def_struct_name_property(srna, prop);
+# endif
prop = RNA_def_property(srna, "properties", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_NEVER_NULL);
diff --git a/source/blender/makesrna/intern/rna_wm_api.c b/source/blender/makesrna/intern/rna_wm_api.c
index bde15daa682..e123604cbe9 100644
--- a/source/blender/makesrna/intern/rna_wm_api.c
+++ b/source/blender/makesrna/intern/rna_wm_api.c
@@ -230,7 +230,7 @@ static wmKeyMapItem *rna_KeyMap_item_new(wmKeyMap *km,
bool repeat,
bool head)
{
- /* wmWindowManager *wm = CTX_wm_manager(C); */
+ // wmWindowManager *wm = CTX_wm_manager(C);
wmKeyMapItem *kmi = NULL;
char idname_bl[OP_MAX_TYPENAME];
int modifier = 0;
diff --git a/source/blender/makesrna/intern/rna_wm_gizmo.c b/source/blender/makesrna/intern/rna_wm_gizmo.c
index 6a1574f3dbe..febb0e14e07 100644
--- a/source/blender/makesrna/intern/rna_wm_gizmo.c
+++ b/source/blender/makesrna/intern/rna_wm_gizmo.c
@@ -80,7 +80,7 @@ static void rna_gizmo_draw_cb(const struct bContext *C, struct wmGizmo *gz)
ParameterList list;
FunctionRNA *func;
RNA_pointer_create(NULL, gz->type->rna_ext.srna, gz, &gz_ptr);
- /* RNA_struct_find_function(&gz_ptr, "draw"); */
+ /* Reference `RNA_struct_find_function(&gz_ptr, "draw")` directly. */
func = &rna_Gizmo_draw_func;
RNA_parameter_list_create(&list, &gz_ptr, func);
RNA_parameter_set_lookup(&list, "context", &C);
@@ -98,7 +98,7 @@ static void rna_gizmo_draw_select_cb(const struct bContext *C, struct wmGizmo *g
ParameterList list;
FunctionRNA *func;
RNA_pointer_create(NULL, gz->type->rna_ext.srna, gz, &gz_ptr);
- /* RNA_struct_find_function(&gz_ptr, "draw_select"); */
+ /* Reference `RNA_struct_find_function(&gz_ptr, "draw_select")` directly. */
func = &rna_Gizmo_draw_select_func;
RNA_parameter_list_create(&list, &gz_ptr, func);
RNA_parameter_set_lookup(&list, "context", &C);
@@ -117,7 +117,7 @@ static int rna_gizmo_test_select_cb(struct bContext *C, struct wmGizmo *gz, cons
ParameterList list;
FunctionRNA *func;
RNA_pointer_create(NULL, gz->type->rna_ext.srna, gz, &gz_ptr);
- /* RNA_struct_find_function(&gz_ptr, "test_select"); */
+ /* Reference `RNA_struct_find_function(&gz_ptr, "test_select")` directly. */
func = &rna_Gizmo_test_select_func;
RNA_parameter_list_create(&list, &gz_ptr, func);
RNA_parameter_set_lookup(&list, "context", &C);
@@ -144,7 +144,7 @@ static int rna_gizmo_modal_cb(struct bContext *C,
FunctionRNA *func;
const int tweak_flag_int = tweak_flag;
RNA_pointer_create(NULL, gz->type->rna_ext.srna, gz, &gz_ptr);
- /* RNA_struct_find_function(&gz_ptr, "modal"); */
+ /* Reference `RNA_struct_find_function(&gz_ptr, "modal")` directly. */
func = &rna_Gizmo_modal_func;
RNA_parameter_list_create(&list, &gz_ptr, func);
RNA_parameter_set_lookup(&list, "context", &C);
@@ -168,7 +168,7 @@ static void rna_gizmo_setup_cb(struct wmGizmo *gz)
ParameterList list;
FunctionRNA *func;
RNA_pointer_create(NULL, gz->type->rna_ext.srna, gz, &gz_ptr);
- /* RNA_struct_find_function(&gz_ptr, "setup"); */
+ /* Reference `RNA_struct_find_function(&gz_ptr, "setup")` directly. */
func = &rna_Gizmo_setup_func;
RNA_parameter_list_create(&list, &gz_ptr, func);
gzgroup->type->rna_ext.call((bContext *)NULL, &gz_ptr, func, &list);
@@ -183,7 +183,7 @@ static int rna_gizmo_invoke_cb(struct bContext *C, struct wmGizmo *gz, const str
ParameterList list;
FunctionRNA *func;
RNA_pointer_create(NULL, gz->type->rna_ext.srna, gz, &gz_ptr);
- /* RNA_struct_find_function(&gz_ptr, "invoke"); */
+ /* Reference `RNA_struct_find_function(&gz_ptr, "invoke")` directly. */
func = &rna_Gizmo_invoke_func;
RNA_parameter_list_create(&list, &gz_ptr, func);
RNA_parameter_set_lookup(&list, "context", &C);
@@ -206,7 +206,7 @@ static void rna_gizmo_exit_cb(struct bContext *C, struct wmGizmo *gz, bool cance
ParameterList list;
FunctionRNA *func;
RNA_pointer_create(NULL, gz->type->rna_ext.srna, gz, &gz_ptr);
- /* RNA_struct_find_function(&gz_ptr, "exit"); */
+ /* Reference `RNA_struct_find_function(&gz_ptr, "exit")` directly. */
func = &rna_Gizmo_exit_func;
RNA_parameter_list_create(&list, &gz_ptr, func);
RNA_parameter_set_lookup(&list, "context", &C);
@@ -226,7 +226,7 @@ static void rna_gizmo_select_refresh_cb(struct wmGizmo *gz)
ParameterList list;
FunctionRNA *func;
RNA_pointer_create(NULL, gz->type->rna_ext.srna, gz, &gz_ptr);
- /* RNA_struct_find_function(&gz_ptr, "select_refresh"); */
+ /* Reference `RNA_struct_find_function(&gz_ptr, "select_refresh")` directly. */
func = &rna_Gizmo_select_refresh_func;
RNA_parameter_list_create(&list, &gz_ptr, func);
gzgroup->type->rna_ext.call((bContext *)NULL, &gz_ptr, func, &list);
@@ -785,7 +785,7 @@ static void rna_gizmogroup_invoke_prepare_cb(const bContext *C,
FunctionRNA *func;
RNA_pointer_create(NULL, gzgroup->type->rna_ext.srna, gzgroup, &gzgroup_ptr);
- /* RNA_struct_find_function(&wgroupr, "invoke_prepare"); */
+ /* Reference `RNA_struct_find_function(&wgroupr, "invoke_prepare")` directly. */
func = &rna_GizmoGroup_invoke_prepare_func;
RNA_parameter_list_create(&list, &gzgroup_ptr, func);
@@ -1033,7 +1033,7 @@ static void rna_def_gizmo(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_property_string_sdna(prop, NULL, "type->idname");
RNA_def_property_string_maxlength(prop, MAX_NAME);
RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Gizmo_bl_idname_set");
- /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */
+ // RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_flag(prop, PROP_REGISTER);
RNA_define_verify_sdna(1); /* not in sdna */
@@ -1362,7 +1362,7 @@ static void rna_def_gizmogroup(BlenderRNA *brna)
RNA_def_property_string_sdna(prop, NULL, "type->name");
RNA_def_property_string_maxlength(prop, MAX_NAME); /* else it uses the pointer size! */
RNA_def_property_string_funcs(prop, NULL, NULL, "rna_GizmoGroup_bl_label_set");
- /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */
+ // RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_flag(prop, PROP_REGISTER);
prop = RNA_def_property(srna, "bl_space_type", PROP_ENUM, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_workspace.c b/source/blender/makesrna/intern/rna_workspace.c
index 95f62d7de16..d76ad254140 100644
--- a/source/blender/makesrna/intern/rna_workspace.c
+++ b/source/blender/makesrna/intern/rna_workspace.c
@@ -112,13 +112,13 @@ static void rna_WorkSpace_owner_ids_clear(WorkSpace *workspace)
static int rna_WorkSpace_asset_library_get(PointerRNA *ptr)
{
const WorkSpace *workspace = ptr->data;
- return ED_asset_library_reference_to_enum_value(&workspace->asset_library);
+ return ED_asset_library_reference_to_enum_value(&workspace->asset_library_ref);
}
static void rna_WorkSpace_asset_library_set(PointerRNA *ptr, int value)
{
WorkSpace *workspace = ptr->data;
- workspace->asset_library = ED_asset_library_reference_from_enum_value(value);
+ workspace->asset_library_ref = ED_asset_library_reference_from_enum_value(value);
}
static bToolRef *rna_WorkSpace_tools_from_tkey(WorkSpace *workspace,
diff --git a/source/blender/makesrna/intern/rna_world.c b/source/blender/makesrna/intern/rna_world.c
index 1ca0eb74cf5..826e6d21c36 100644
--- a/source/blender/makesrna/intern/rna_world.c
+++ b/source/blender/makesrna/intern/rna_world.c
@@ -216,7 +216,7 @@ void RNA_def_world(BlenderRNA *brna)
RNA_def_property_array(prop, 3);
RNA_def_property_float_array_default(prop, default_world_color);
RNA_def_property_ui_text(prop, "Color", "Color of the background");
- /* RNA_def_property_update(prop, 0, "rna_World_update"); */
+ // RNA_def_property_update(prop, 0, "rna_World_update");
/* render-only uses this */
RNA_def_property_update(prop, 0, "rna_World_draw_update");
@@ -237,6 +237,7 @@ void RNA_def_world(BlenderRNA *brna)
prop = RNA_def_property(srna, "node_tree", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "nodetree");
RNA_def_property_clear_flag(prop, PROP_PTR_NO_OWNERSHIP);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Node Tree", "Node tree for node based worlds");
prop = RNA_def_property(srna, "use_nodes", PROP_BOOLEAN, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_xr.c b/source/blender/makesrna/intern/rna_xr.c
index 56e8418972c..4cab92ad878 100644
--- a/source/blender/makesrna/intern/rna_xr.c
+++ b/source/blender/makesrna/intern/rna_xr.c
@@ -18,9 +18,14 @@
* \ingroup RNA
*/
+#include "BLI_math.h"
+
+#include "DNA_space_types.h"
#include "DNA_view3d_types.h"
+#include "DNA_windowmanager_types.h"
#include "DNA_xr_types.h"
+#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
@@ -30,10 +35,452 @@
#ifdef RNA_RUNTIME
-# include "BLI_math.h"
+# include "BLI_listbase.h"
# include "WM_api.h"
+/* -------------------------------------------------------------------- */
+/** \name XR Action Map
+ * \{ */
+
+static XrActionMapBinding *rna_XrActionMapBinding_new(XrActionMapItem *ami,
+ const char *name,
+ bool replace_existing)
+{
+# ifdef WITH_XR_OPENXR
+ return WM_xr_actionmap_binding_new(ami, name, replace_existing);
+# else
+ UNUSED_VARS(ami, name, replace_existing);
+ return NULL;
+# endif
+}
+
+static XrActionMapBinding *rna_XrActionMapBinding_new_from_binding(XrActionMapItem *ami,
+ XrActionMapBinding *amb_src)
+{
+# ifdef WITH_XR_OPENXR
+ return WM_xr_actionmap_binding_add_copy(ami, amb_src);
+# else
+ UNUSED_VARS(ami, amb_src);
+ return NULL;
+# endif
+}
+
+static void rna_XrActionMapBinding_remove(XrActionMapItem *ami,
+ ReportList *reports,
+ PointerRNA *amb_ptr)
+{
+# ifdef WITH_XR_OPENXR
+ XrActionMapBinding *amb = amb_ptr->data;
+ if (WM_xr_actionmap_binding_remove(ami, amb) == false) {
+ BKE_reportf(reports,
+ RPT_ERROR,
+ "ActionMapBinding '%s' cannot be removed from '%s'",
+ amb->name,
+ ami->name);
+ return;
+ }
+ RNA_POINTER_INVALIDATE(amb_ptr);
+# else
+ UNUSED_VARS(ami, reports, amb_ptr);
+# endif
+}
+
+static XrActionMapBinding *rna_XrActionMapBinding_find(XrActionMapItem *ami, const char *name)
+{
+# ifdef WITH_XR_OPENXR
+ return WM_xr_actionmap_binding_find(ami, name);
+# else
+ UNUSED_VARS(ami, name);
+ return NULL;
+# endif
+}
+
+static int rna_XrActionMapBinding_axis0_region_get(PointerRNA *ptr)
+{
+# ifdef WITH_XR_OPENXR
+ XrActionMapBinding *amb = ptr->data;
+ if ((amb->axis_flag & XR_AXIS0_POS) != 0) {
+ return XR_AXIS0_POS;
+ }
+ if ((amb->axis_flag & XR_AXIS0_NEG) != 0) {
+ return XR_AXIS0_NEG;
+ }
+# else
+ UNUSED_VARS(ptr);
+# endif
+ return 0;
+}
+
+static void rna_XrActionMapBinding_axis0_region_set(PointerRNA *ptr, int value)
+{
+# ifdef WITH_XR_OPENXR
+ XrActionMapBinding *amb = ptr->data;
+ amb->axis_flag &= ~(XR_AXIS0_POS | XR_AXIS0_NEG);
+ amb->axis_flag |= value;
+# else
+ UNUSED_VARS(ptr, value);
+# endif
+}
+
+static int rna_XrActionMapBinding_axis1_region_get(PointerRNA *ptr)
+{
+# ifdef WITH_XR_OPENXR
+ XrActionMapBinding *amb = ptr->data;
+ if ((amb->axis_flag & XR_AXIS1_POS) != 0) {
+ return XR_AXIS1_POS;
+ }
+ if ((amb->axis_flag & XR_AXIS1_NEG) != 0) {
+ return XR_AXIS1_NEG;
+ }
+# else
+ UNUSED_VARS(ptr);
+# endif
+ return 0;
+}
+
+static void rna_XrActionMapBinding_axis1_region_set(PointerRNA *ptr, int value)
+{
+# ifdef WITH_XR_OPENXR
+ XrActionMapBinding *amb = ptr->data;
+ amb->axis_flag &= ~(XR_AXIS1_POS | XR_AXIS1_NEG);
+ amb->axis_flag |= value;
+# else
+ UNUSED_VARS(ptr, value);
+# endif
+}
+
+static void rna_XrActionMapBinding_name_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
+{
+# ifdef WITH_XR_OPENXR
+ wmWindowManager *wm = bmain->wm.first;
+ if (wm && wm->xr.runtime) {
+ ListBase *actionmaps = WM_xr_actionmaps_get(wm->xr.runtime);
+ short idx = WM_xr_actionmap_selected_index_get(wm->xr.runtime);
+ XrActionMap *actionmap = BLI_findlink(actionmaps, idx);
+ if (actionmap) {
+ XrActionMapItem *ami = BLI_findlink(&actionmap->items, actionmap->selitem);
+ if (ami) {
+ XrActionMapBinding *amb = ptr->data;
+ WM_xr_actionmap_binding_ensure_unique(ami, amb);
+ }
+ }
+ }
+# else
+ UNUSED_VARS(bmain, ptr);
+# endif
+}
+
+static XrActionMapItem *rna_XrActionMapItem_new(XrActionMap *am,
+ const char *name,
+ bool replace_existing)
+{
+# ifdef WITH_XR_OPENXR
+ return WM_xr_actionmap_item_new(am, name, replace_existing);
+# else
+ UNUSED_VARS(am, name, replace_existing);
+ return NULL;
+# endif
+}
+
+static XrActionMapItem *rna_XrActionMapItem_new_from_item(XrActionMap *am,
+ XrActionMapItem *ami_src)
+{
+# ifdef WITH_XR_OPENXR
+ return WM_xr_actionmap_item_add_copy(am, ami_src);
+# else
+ UNUSED_VARS(am, ami_src);
+ return NULL;
+# endif
+}
+
+static void rna_XrActionMapItem_remove(XrActionMap *am, ReportList *reports, PointerRNA *ami_ptr)
+{
+# ifdef WITH_XR_OPENXR
+ XrActionMapItem *ami = ami_ptr->data;
+ if (WM_xr_actionmap_item_remove(am, ami) == false) {
+ BKE_reportf(
+ reports, RPT_ERROR, "ActionMapItem '%s' cannot be removed from '%s'", ami->name, am->name);
+ return;
+ }
+ RNA_POINTER_INVALIDATE(ami_ptr);
+# else
+ UNUSED_VARS(am, reports, ami_ptr);
+# endif
+}
+
+static XrActionMapItem *rna_XrActionMapItem_find(XrActionMap *am, const char *name)
+{
+# ifdef WITH_XR_OPENXR
+ return WM_xr_actionmap_item_find(am, name);
+# else
+ UNUSED_VARS(am, name);
+ return NULL;
+# endif
+}
+
+static void rna_XrActionMapItem_op_name_get(PointerRNA *ptr, char *value)
+{
+# ifdef WITH_XR_OPENXR
+ XrActionMapItem *ami = ptr->data;
+ if (ami->op[0]) {
+ if (ami->op_properties_ptr) {
+ wmOperatorType *ot = WM_operatortype_find(ami->op, 1);
+ if (ot) {
+ strcpy(value, WM_operatortype_name(ot, ami->op_properties_ptr));
+ return;
+ }
+ }
+ strcpy(value, ami->op);
+ return;
+ }
+# else
+ UNUSED_VARS(ptr);
+# endif
+ value[0] = '\0';
+}
+
+static int rna_XrActionMapItem_op_name_length(PointerRNA *ptr)
+{
+# ifdef WITH_XR_OPENXR
+ XrActionMapItem *ami = ptr->data;
+ if (ami->op[0]) {
+ if (ami->op_properties_ptr) {
+ wmOperatorType *ot = WM_operatortype_find(ami->op, 1);
+ if (ot) {
+ return strlen(WM_operatortype_name(ot, ami->op_properties_ptr));
+ }
+ }
+ return strlen(ami->op);
+ }
+# else
+ UNUSED_VARS(ptr);
+# endif
+ return 0;
+}
+
+static PointerRNA rna_XrActionMapItem_op_properties_get(PointerRNA *ptr)
+{
+# ifdef WITH_XR_OPENXR
+ XrActionMapItem *ami = ptr->data;
+ if (ami->op_properties_ptr) {
+ return *(ami->op_properties_ptr);
+ }
+# else
+ UNUSED_VARS(ptr);
+# endif
+ return PointerRNA_NULL;
+}
+
+static bool rna_XrActionMapItem_bimanual_get(PointerRNA *ptr)
+{
+# ifdef WITH_XR_OPENXR
+ XrActionMapItem *ami = ptr->data;
+ if ((ami->action_flag & XR_ACTION_BIMANUAL) != 0) {
+ return true;
+ }
+# else
+ UNUSED_VARS(ptr);
+# endif
+ return false;
+}
+
+static void rna_XrActionMapItem_bimanual_set(PointerRNA *ptr, bool value)
+{
+# ifdef WITH_XR_OPENXR
+ XrActionMapItem *ami = ptr->data;
+ SET_FLAG_FROM_TEST(ami->action_flag, value, XR_ACTION_BIMANUAL);
+# else
+ UNUSED_VARS(ptr, value);
+# endif
+}
+
+static bool rna_XrActionMapItem_haptic_match_user_paths_get(PointerRNA *ptr)
+{
+# ifdef WITH_XR_OPENXR
+ XrActionMapItem *ami = ptr->data;
+ if ((ami->haptic_flag & XR_HAPTIC_MATCHUSERPATHS) != 0) {
+ return true;
+ }
+# else
+ UNUSED_VARS(ptr);
+# endif
+ return false;
+}
+
+static void rna_XrActionMapItem_haptic_match_user_paths_set(PointerRNA *ptr, bool value)
+{
+# ifdef WITH_XR_OPENXR
+ XrActionMapItem *ami = ptr->data;
+ SET_FLAG_FROM_TEST(ami->haptic_flag, value, XR_HAPTIC_MATCHUSERPATHS);
+# else
+ UNUSED_VARS(ptr, value);
+# endif
+}
+
+static int rna_XrActionMapItem_haptic_mode_get(PointerRNA *ptr)
+{
+# ifdef WITH_XR_OPENXR
+ XrActionMapItem *ami = ptr->data;
+ if ((ami->haptic_flag & XR_HAPTIC_RELEASE) != 0) {
+ return ((ami->haptic_flag & XR_HAPTIC_PRESS) != 0) ? (XR_HAPTIC_PRESS | XR_HAPTIC_RELEASE) :
+ XR_HAPTIC_RELEASE;
+ }
+ if ((ami->haptic_flag & XR_HAPTIC_REPEAT) != 0) {
+ return XR_HAPTIC_REPEAT;
+ }
+# else
+ UNUSED_VARS(ptr);
+# endif
+ return XR_HAPTIC_PRESS;
+}
+
+static void rna_XrActionMapItem_haptic_mode_set(PointerRNA *ptr, int value)
+{
+# ifdef WITH_XR_OPENXR
+ XrActionMapItem *ami = ptr->data;
+ ami->haptic_flag &= ~(XR_HAPTIC_PRESS | XR_HAPTIC_RELEASE | XR_HAPTIC_REPEAT);
+ ami->haptic_flag |= value;
+# else
+ UNUSED_VARS(ptr, value);
+# endif
+}
+
+static bool rna_XrActionMapItem_pose_is_controller_grip_get(PointerRNA *ptr)
+{
+# ifdef WITH_XR_OPENXR
+ XrActionMapItem *ami = ptr->data;
+ if ((ami->pose_flag & XR_POSE_GRIP) != 0) {
+ return true;
+ }
+# else
+ UNUSED_VARS(ptr);
+# endif
+ return false;
+}
+
+static void rna_XrActionMapItem_pose_is_controller_grip_set(PointerRNA *ptr, bool value)
+{
+# ifdef WITH_XR_OPENXR
+ XrActionMapItem *ami = ptr->data;
+ SET_FLAG_FROM_TEST(ami->pose_flag, value, XR_POSE_GRIP);
+# else
+ UNUSED_VARS(ptr, value);
+# endif
+}
+
+static bool rna_XrActionMapItem_pose_is_controller_aim_get(PointerRNA *ptr)
+{
+# ifdef WITH_XR_OPENXR
+ XrActionMapItem *ami = ptr->data;
+ if ((ami->pose_flag & XR_POSE_AIM) != 0) {
+ return true;
+ }
+# else
+ UNUSED_VARS(ptr);
+# endif
+ return false;
+}
+
+static void rna_XrActionMapItem_pose_is_controller_aim_set(PointerRNA *ptr, bool value)
+{
+# ifdef WITH_XR_OPENXR
+ XrActionMapItem *ami = ptr->data;
+ SET_FLAG_FROM_TEST(ami->pose_flag, value, XR_POSE_AIM);
+# else
+ UNUSED_VARS(ptr, value);
+# endif
+}
+
+static void rna_XrActionMapItem_name_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
+{
+# ifdef WITH_XR_OPENXR
+ wmWindowManager *wm = bmain->wm.first;
+ if (wm && wm->xr.runtime) {
+ ListBase *actionmaps = WM_xr_actionmaps_get(wm->xr.runtime);
+ short idx = WM_xr_actionmap_selected_index_get(wm->xr.runtime);
+ XrActionMap *actionmap = BLI_findlink(actionmaps, idx);
+ if (actionmap) {
+ XrActionMapItem *ami = ptr->data;
+ WM_xr_actionmap_item_ensure_unique(actionmap, ami);
+ }
+ }
+# else
+ UNUSED_VARS(bmain, ptr);
+# endif
+}
+
+static void rna_XrActionMapItem_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
+{
+# ifdef WITH_XR_OPENXR
+ XrActionMapItem *ami = ptr->data;
+ WM_xr_actionmap_item_properties_update_ot(ami);
+# else
+ UNUSED_VARS(ptr);
+# endif
+}
+
+static XrActionMap *rna_XrActionMap_new(wmXrData *xr, const char *name, bool replace_existing)
+{
+# ifdef WITH_XR_OPENXR
+ return WM_xr_actionmap_new(xr->runtime, name, replace_existing);
+# else
+ UNUSED_VARS(xr, name, replace_existing);
+ return NULL;
+# endif
+}
+
+static XrActionMap *rna_XrActionMap_new_from_actionmap(wmXrData *xr, XrActionMap *am_src)
+{
+# ifdef WITH_XR_OPENXR
+ return WM_xr_actionmap_add_copy(xr->runtime, am_src);
+# else
+ UNUSED_VARS(xr, am_src);
+ return NULL;
+# endif
+}
+
+static void rna_XrActionMap_remove(wmXrData *xr, ReportList *reports, PointerRNA *actionmap_ptr)
+{
+# ifdef WITH_XR_OPENXR
+ XrActionMap *actionmap = actionmap_ptr->data;
+ if (WM_xr_actionmap_remove(xr->runtime, actionmap) == false) {
+ BKE_reportf(reports, RPT_ERROR, "ActionMap '%s' cannot be removed", actionmap->name);
+ return;
+ }
+ RNA_POINTER_INVALIDATE(actionmap_ptr);
+# else
+ UNUSED_VARS(xr, reports, actionmap_ptr);
+# endif
+}
+
+static XrActionMap *rna_XrActionMap_find(wmXrData *xr, const char *name)
+{
+# ifdef WITH_XR_OPENXR
+ return WM_xr_actionmap_find(xr->runtime, name);
+# else
+ UNUSED_VARS(xr, name);
+ return NULL;
+# endif
+}
+
+static void rna_XrActionMap_name_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
+{
+# ifdef WITH_XR_OPENXR
+ wmWindowManager *wm = bmain->wm.first;
+ if (wm && wm->xr.runtime) {
+ XrActionMap *actionmap = ptr->data;
+ WM_xr_actionmap_ensure_unique(wm->xr.runtime, actionmap);
+ }
+# else
+ UNUSED_VARS(bmain, ptr);
+# endif
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+
# ifdef WITH_XR_OPENXR
static wmXrData *rna_XrSession_wm_xr_data_get(PointerRNA *ptr)
{
@@ -49,6 +496,10 @@ static wmXrData *rna_XrSession_wm_xr_data_get(PointerRNA *ptr)
}
# endif
+/* -------------------------------------------------------------------- */
+/** \name XR Session Settings
+ * \{ */
+
static bool rna_XrSessionSettings_use_positional_tracking_get(PointerRNA *ptr)
{
# ifdef WITH_XR_OPENXR
@@ -91,6 +542,12 @@ static void rna_XrSessionSettings_use_absolute_tracking_set(PointerRNA *ptr, boo
# endif
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name XR Session State
+ * \{ */
+
static bool rna_XrSessionState_is_running(bContext *C)
{
# ifdef WITH_XR_OPENXR
@@ -112,6 +569,303 @@ static void rna_XrSessionState_reset_to_base_pose(bContext *C)
# endif
}
+static bool rna_XrSessionState_action_set_create(bContext *C, XrActionMap *actionmap)
+{
+# ifdef WITH_XR_OPENXR
+ wmWindowManager *wm = CTX_wm_manager(C);
+ return WM_xr_action_set_create(&wm->xr, actionmap->name);
+# else
+ UNUSED_VARS(C, actionmap);
+ return false;
+# endif
+}
+
+static bool rna_XrSessionState_action_create(bContext *C,
+ XrActionMap *actionmap,
+ XrActionMapItem *ami)
+{
+# ifdef WITH_XR_OPENXR
+ wmWindowManager *wm = CTX_wm_manager(C);
+ unsigned int count_subaction_paths = 0;
+ const char *subaction_paths[2];
+
+ if (ami->user_path0[0]) {
+ subaction_paths[0] = ami->user_path0;
+ ++count_subaction_paths;
+
+ if (ami->user_path1[0]) {
+ subaction_paths[1] = ami->user_path1;
+ ++count_subaction_paths;
+ }
+ }
+ else {
+ if (ami->user_path1[0]) {
+ subaction_paths[0] = ami->user_path1;
+ ++count_subaction_paths;
+ }
+ else {
+ return false;
+ }
+ }
+
+ const bool is_float_action = (ami->type == XR_FLOAT_INPUT || ami->type == XR_VECTOR2F_INPUT);
+ const bool is_button_action = (is_float_action || ami->type == XR_BOOLEAN_INPUT);
+ wmOperatorType *ot = NULL;
+ IDProperty *op_properties = NULL;
+ int64_t haptic_duration_msec;
+
+ if (is_button_action) {
+ if (ami->op[0]) {
+ char idname[OP_MAX_TYPENAME];
+ WM_operator_bl_idname(idname, ami->op);
+ ot = WM_operatortype_find(idname, true);
+ if (ot) {
+ op_properties = ami->op_properties;
+ }
+ }
+
+ haptic_duration_msec = (int64_t)(ami->haptic_duration * 1000.0f);
+ }
+
+ return WM_xr_action_create(&wm->xr,
+ actionmap->name,
+ ami->name,
+ ami->type,
+ count_subaction_paths,
+ subaction_paths,
+ ot,
+ op_properties,
+ is_button_action ? ami->haptic_name : NULL,
+ is_button_action ? &haptic_duration_msec : NULL,
+ is_button_action ? &ami->haptic_frequency : NULL,
+ is_button_action ? &ami->haptic_amplitude : NULL,
+ ami->op_flag,
+ ami->action_flag,
+ ami->haptic_flag);
+# else
+ UNUSED_VARS(C, actionmap, ami);
+ return false;
+# endif
+}
+
+static bool rna_XrSessionState_action_binding_create(bContext *C,
+ XrActionMap *actionmap,
+ XrActionMapItem *ami,
+ XrActionMapBinding *amb)
+{
+# ifdef WITH_XR_OPENXR
+ wmWindowManager *wm = CTX_wm_manager(C);
+ unsigned int count_subaction_paths = 0;
+ const char *subaction_paths[2];
+ const char *component_paths[2];
+
+ if (ami->user_path0[0]) {
+ subaction_paths[0] = ami->user_path0;
+ component_paths[0] = amb->component_path0;
+ ++count_subaction_paths;
+
+ if (ami->user_path1[0]) {
+ subaction_paths[1] = ami->user_path1;
+ component_paths[1] = amb->component_path1;
+ ++count_subaction_paths;
+ }
+ }
+ else {
+ if (ami->user_path1[0]) {
+ subaction_paths[0] = ami->user_path1;
+ component_paths[0] = amb->component_path1;
+ ++count_subaction_paths;
+ }
+ else {
+ return false;
+ }
+ }
+
+ const bool is_float_action = (ami->type == XR_FLOAT_INPUT || ami->type == XR_VECTOR2F_INPUT);
+ const bool is_button_action = (is_float_action || ami->type == XR_BOOLEAN_INPUT);
+ const bool is_pose_action = (ami->type == XR_POSE_INPUT);
+ float float_thresholds[2];
+ eXrAxisFlag axis_flags[2];
+ wmXrPose poses[2];
+
+ if (is_float_action) {
+ float_thresholds[0] = float_thresholds[1] = amb->float_threshold;
+ }
+ if (is_button_action) {
+ axis_flags[0] = axis_flags[1] = amb->axis_flag;
+ }
+ if (is_pose_action) {
+ copy_v3_v3(poses[0].position, amb->pose_location);
+ eul_to_quat(poses[0].orientation_quat, amb->pose_rotation);
+ normalize_qt(poses[0].orientation_quat);
+ memcpy(&poses[1], &poses[0], sizeof(poses[1]));
+ }
+
+ return WM_xr_action_binding_create(&wm->xr,
+ actionmap->name,
+ ami->name,
+ amb->profile,
+ count_subaction_paths,
+ subaction_paths,
+ component_paths,
+ is_float_action ? float_thresholds : NULL,
+ is_button_action ? axis_flags : NULL,
+ is_pose_action ? poses : NULL);
+# else
+ UNUSED_VARS(C, actionmap, ami, amb);
+ return false;
+# endif
+}
+
+bool rna_XrSessionState_active_action_set_set(bContext *C, const char *action_set_name)
+{
+# ifdef WITH_XR_OPENXR
+ wmWindowManager *wm = CTX_wm_manager(C);
+ return WM_xr_active_action_set_set(&wm->xr, action_set_name);
+# else
+ UNUSED_VARS(C, action_set_name);
+ return false;
+# endif
+}
+
+bool rna_XrSessionState_controller_pose_actions_set(bContext *C,
+ const char *action_set_name,
+ const char *grip_action_name,
+ const char *aim_action_name)
+{
+# ifdef WITH_XR_OPENXR
+ wmWindowManager *wm = CTX_wm_manager(C);
+ return WM_xr_controller_pose_actions_set(
+ &wm->xr, action_set_name, grip_action_name, aim_action_name);
+# else
+ UNUSED_VARS(C, action_set_name, grip_action_name, aim_action_name);
+ return false;
+# endif
+}
+
+void rna_XrSessionState_action_state_get(bContext *C,
+ const char *action_set_name,
+ const char *action_name,
+ const char *user_path,
+ float r_state[2])
+{
+# ifdef WITH_XR_OPENXR
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wmXrActionState state;
+ if (WM_xr_action_state_get(&wm->xr, action_set_name, action_name, user_path, &state)) {
+ switch (state.type) {
+ case XR_BOOLEAN_INPUT:
+ r_state[0] = (float)state.state_boolean;
+ r_state[1] = 0.0f;
+ return;
+ case XR_FLOAT_INPUT:
+ r_state[0] = state.state_float;
+ r_state[1] = 0.0f;
+ return;
+ case XR_VECTOR2F_INPUT:
+ copy_v2_v2(r_state, state.state_vector2f);
+ return;
+ case XR_POSE_INPUT:
+ case XR_VIBRATION_OUTPUT:
+ BLI_assert_unreachable();
+ break;
+ }
+ }
+# else
+ UNUSED_VARS(C, action_set_name, action_name, user_path);
+# endif
+ zero_v2(r_state);
+}
+
+bool rna_XrSessionState_haptic_action_apply(bContext *C,
+ const char *action_set_name,
+ const char *action_name,
+ const char *user_path,
+ float duration,
+ float frequency,
+ float amplitude)
+{
+# ifdef WITH_XR_OPENXR
+ wmWindowManager *wm = CTX_wm_manager(C);
+ int64_t duration_msec = (int64_t)(duration * 1000.0f);
+ return WM_xr_haptic_action_apply(&wm->xr,
+ action_set_name,
+ action_name,
+ user_path[0] ? user_path : NULL,
+ &duration_msec,
+ &frequency,
+ &amplitude);
+# else
+ UNUSED_VARS(C, action_set_name, action_name, user_path, duration, frequency, amplitude);
+ return false;
+# endif
+}
+
+void rna_XrSessionState_haptic_action_stop(bContext *C,
+ const char *action_set_name,
+ const char *action_name,
+ const char *user_path)
+{
+# ifdef WITH_XR_OPENXR
+ wmWindowManager *wm = CTX_wm_manager(C);
+ WM_xr_haptic_action_stop(&wm->xr, action_set_name, action_name, user_path[0] ? user_path : NULL);
+# else
+ UNUSED_VARS(C, action_set_name, action_name, user_path);
+# endif
+}
+
+static void rna_XrSessionState_controller_grip_location_get(bContext *C,
+ int index,
+ float r_values[3])
+{
+# ifdef WITH_XR_OPENXR
+ const wmWindowManager *wm = CTX_wm_manager(C);
+ WM_xr_session_state_controller_grip_location_get(&wm->xr, index, r_values);
+# else
+ UNUSED_VARS(C, index);
+ zero_v3(r_values);
+# endif
+}
+
+static void rna_XrSessionState_controller_grip_rotation_get(bContext *C,
+ int index,
+ float r_values[4])
+{
+# ifdef WITH_XR_OPENXR
+ const wmWindowManager *wm = CTX_wm_manager(C);
+ WM_xr_session_state_controller_grip_rotation_get(&wm->xr, index, r_values);
+# else
+ UNUSED_VARS(C, index);
+ unit_qt(r_values);
+# endif
+}
+
+static void rna_XrSessionState_controller_aim_location_get(bContext *C,
+ int index,
+ float r_values[3])
+{
+# ifdef WITH_XR_OPENXR
+ const wmWindowManager *wm = CTX_wm_manager(C);
+ WM_xr_session_state_controller_aim_location_get(&wm->xr, index, r_values);
+# else
+ UNUSED_VARS(C, index);
+ zero_v3(r_values);
+# endif
+}
+
+static void rna_XrSessionState_controller_aim_rotation_get(bContext *C,
+ int index,
+ float r_values[4])
+{
+# ifdef WITH_XR_OPENXR
+ const wmWindowManager *wm = CTX_wm_manager(C);
+ WM_xr_session_state_controller_aim_rotation_get(&wm->xr, index, r_values);
+# else
+ UNUSED_VARS(C, index);
+ unit_qt(r_values);
+# endif
+}
+
static void rna_XrSessionState_viewer_pose_location_get(PointerRNA *ptr, float *r_values)
{
# ifdef WITH_XR_OPENXR
@@ -134,8 +888,490 @@ static void rna_XrSessionState_viewer_pose_rotation_get(PointerRNA *ptr, float *
# endif
}
+static void rna_XrSessionState_actionmaps_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+# ifdef WITH_XR_OPENXR
+ wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr);
+ ListBase *lb = WM_xr_actionmaps_get(xr->runtime);
+ rna_iterator_listbase_begin(iter, lb, NULL);
+# else
+ UNUSED_VARS(iter, ptr);
+# endif
+}
+
+static int rna_XrSessionState_active_actionmap_get(PointerRNA *ptr)
+{
+# ifdef WITH_XR_OPENXR
+ const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr);
+ return WM_xr_actionmap_active_index_get(xr->runtime);
+# else
+ UNUSED_VARS(ptr);
+ return -1;
+# endif
+}
+
+static void rna_XrSessionState_active_actionmap_set(PointerRNA *ptr, int value)
+{
+# ifdef WITH_XR_OPENXR
+ wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr);
+ WM_xr_actionmap_active_index_set(xr->runtime, (short)value);
+# else
+ UNUSED_VARS(ptr, value);
+# endif
+}
+
+static int rna_XrSessionState_selected_actionmap_get(PointerRNA *ptr)
+{
+# ifdef WITH_XR_OPENXR
+ const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr);
+ return WM_xr_actionmap_selected_index_get(xr->runtime);
+# else
+ UNUSED_VARS(ptr);
+ return -1;
+# endif
+}
+
+static void rna_XrSessionState_selected_actionmap_set(PointerRNA *ptr, int value)
+{
+# ifdef WITH_XR_OPENXR
+ wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr);
+ WM_xr_actionmap_selected_index_set(xr->runtime, (short)value);
+# else
+ UNUSED_VARS(ptr, value);
+# endif
+}
+
+/** \} */
+
#else /* RNA_RUNTIME */
+/* -------------------------------------------------------------------- */
+
+static const EnumPropertyItem rna_enum_xr_action_types[] = {
+ {XR_FLOAT_INPUT,
+ "FLOAT",
+ 0,
+ "Float",
+ "Float action, representing either a digital or analog button"},
+ {XR_VECTOR2F_INPUT,
+ "VECTOR2D",
+ 0,
+ "Vector2D",
+ "2D float vector action, representing a thumbstick or trackpad"},
+ {XR_POSE_INPUT,
+ "POSE",
+ 0,
+ "Pose",
+ "3D pose action, representing a controller's location and rotation"},
+ {XR_VIBRATION_OUTPUT,
+ "VIBRATION",
+ 0,
+ "Vibration",
+ "Haptic vibration output action, to be applied with a duration, frequency, and amplitude"},
+ {0, NULL, 0, NULL, NULL},
+};
+
+static const EnumPropertyItem rna_enum_xr_op_flags[] = {
+ {XR_OP_PRESS,
+ "PRESS",
+ 0,
+ "Press",
+ "Execute operator on button press (non-modal operators only)"},
+ {XR_OP_RELEASE,
+ "RELEASE",
+ 0,
+ "Release",
+ "Execute operator on button release (non-modal operators only)"},
+ {XR_OP_MODAL, "MODAL", 0, "Modal", "Use modal execution (modal operators only)"},
+ {0, NULL, 0, NULL, NULL},
+};
+
+static const EnumPropertyItem rna_enum_xr_haptic_flags[] = {
+ {XR_HAPTIC_PRESS, "PRESS", 0, "Press", "Apply haptics on button press"},
+ {XR_HAPTIC_RELEASE, "RELEASE", 0, "Release", "Apply haptics on button release"},
+ {XR_HAPTIC_PRESS | XR_HAPTIC_RELEASE,
+ "PRESS_RELEASE",
+ 0,
+ "Press Release",
+ "Apply haptics on button press and release"},
+ {XR_HAPTIC_REPEAT,
+ "REPEAT",
+ 0,
+ "Repeat",
+ "Apply haptics repeatedly for the duration of the button press"},
+ {0, NULL, 0, NULL, NULL},
+};
+
+static const EnumPropertyItem rna_enum_xr_axis0_flags[] = {
+ {0, "ANY", 0, "Any", "Use any axis region for operator execution"},
+ {XR_AXIS0_POS,
+ "POSITIVE",
+ 0,
+ "Positive",
+ "Use positive axis region only for operator execution"},
+ {XR_AXIS0_NEG,
+ "NEGATIVE",
+ 0,
+ "Negative",
+ "Use negative axis region only for operator execution"},
+ {0, NULL, 0, NULL, NULL},
+};
+
+static const EnumPropertyItem rna_enum_xr_axis1_flags[] = {
+ {0, "ANY", 0, "Any", "Use any axis region for operator execution"},
+ {XR_AXIS1_POS,
+ "POSITIVE",
+ 0,
+ "Positive",
+ "Use positive axis region only for operator execution"},
+ {XR_AXIS1_NEG,
+ "NEGATIVE",
+ 0,
+ "Negative",
+ "Use negative axis region only for operator execution"},
+ {0, NULL, 0, NULL, NULL},
+};
+
+/* -------------------------------------------------------------------- */
+/** \name XR Action Map
+ * \{ */
+
+static void rna_def_xr_actionmap_bindings(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ StructRNA *srna;
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ RNA_def_property_srna(cprop, "XrActionMapBindings");
+ srna = RNA_def_struct(brna, "XrActionMapBindings", NULL);
+ RNA_def_struct_sdna(srna, "XrActionMapItem");
+ RNA_def_struct_ui_text(srna, "XR Action Map Bindings", "Collection of XR action map bindings");
+
+ func = RNA_def_function(srna, "new", "rna_XrActionMapBinding_new");
+ parm = RNA_def_string(func, "name", NULL, MAX_NAME, "Name of the action map binding", "");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_boolean(func,
+ "replace_existing",
+ true,
+ "Replace Existing",
+ "Replace any existing binding with the same name");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_pointer(
+ func, "binding", "XrActionMapBinding", "Binding", "Added action map binding");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "new_from_binding", "rna_XrActionMapBinding_new_from_binding");
+ parm = RNA_def_pointer(
+ func, "binding", "XrActionMapBinding", "Binding", "Binding to use as a reference");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_pointer(
+ func, "result", "XrActionMapBinding", "Binding", "Added action map binding");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "remove", "rna_XrActionMapBinding_remove");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ parm = RNA_def_pointer(func, "binding", "XrActionMapBinding", "Binding", "");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
+ RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
+
+ func = RNA_def_function(srna, "find", "rna_XrActionMapBinding_find");
+ parm = RNA_def_string(func, "name", NULL, MAX_NAME, "Name", "");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_pointer(func,
+ "binding",
+ "XrActionMapBinding",
+ "Binding",
+ "The action map binding with the given name");
+ RNA_def_function_return(func, parm);
+}
+
+static void rna_def_xr_actionmap_items(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ StructRNA *srna;
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ RNA_def_property_srna(cprop, "XrActionMapItems");
+ srna = RNA_def_struct(brna, "XrActionMapItems", NULL);
+ RNA_def_struct_sdna(srna, "XrActionMap");
+ RNA_def_struct_ui_text(srna, "XR Action Map Items", "Collection of XR action map items");
+
+ func = RNA_def_function(srna, "new", "rna_XrActionMapItem_new");
+ parm = RNA_def_string(func, "name", NULL, MAX_NAME, "Name of the action map item", "");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_boolean(func,
+ "replace_existing",
+ true,
+ "Replace Existing",
+ "Replace any existing item with the same name");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_pointer(func, "item", "XrActionMapItem", "Item", "Added action map item");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "new_from_item", "rna_XrActionMapItem_new_from_item");
+ parm = RNA_def_pointer(func, "item", "XrActionMapItem", "Item", "Item to use as a reference");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_pointer(func, "result", "XrActionMapItem", "Item", "Added action map item");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "remove", "rna_XrActionMapItem_remove");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ parm = RNA_def_pointer(func, "item", "XrActionMapItem", "Item", "");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
+ RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
+
+ func = RNA_def_function(srna, "find", "rna_XrActionMapItem_find");
+ parm = RNA_def_string(func, "name", NULL, MAX_NAME, "Name", "");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_pointer(
+ func, "item", "XrActionMapItem", "Item", "The action map item with the given name");
+ RNA_def_function_return(func, parm);
+}
+
+static void rna_def_xr_actionmaps(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ StructRNA *srna;
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ RNA_def_property_srna(cprop, "XrActionMaps");
+ srna = RNA_def_struct(brna, "XrActionMaps", NULL);
+ RNA_def_struct_sdna(srna, "wmXrData");
+ RNA_def_struct_ui_text(srna, "XR Action Maps", "Collection of XR action maps");
+
+ func = RNA_def_function(srna, "new", "rna_XrActionMap_new");
+ parm = RNA_def_string(func, "name", NULL, MAX_NAME, "Name", "");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_boolean(func,
+ "replace_existing",
+ true,
+ "Replace Existing",
+ "Replace any existing actionmap with the same name");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_pointer(func, "actionmap", "XrActionMap", "Action Map", "Added action map");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "new_from_actionmap", "rna_XrActionMap_new_from_actionmap");
+ parm = RNA_def_pointer(
+ func, "actionmap", "XrActionMap", "Action Map", "Action map to use as a reference");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_pointer(func, "result", "XrActionMap", "Action Map", "Added action map");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "remove", "rna_XrActionMap_remove");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ parm = RNA_def_pointer(func, "actionmap", "XrActionMap", "Action Map", "Removed action map");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
+ RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
+
+ func = RNA_def_function(srna, "find", "rna_XrActionMap_find");
+ parm = RNA_def_string(func, "name", NULL, MAX_NAME, "Name", "");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_pointer(
+ func, "actionmap", "XrActionMap", "Action Map", "The action map with the given name");
+ RNA_def_function_return(func, parm);
+}
+
+static void rna_def_xr_actionmap(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ /* XrActionMap */
+ srna = RNA_def_struct(brna, "XrActionMap", NULL);
+ RNA_def_struct_sdna(srna, "XrActionMap");
+ RNA_def_struct_ui_text(srna, "XR Action Map", "");
+
+ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Name", "Name of the action map");
+ RNA_def_property_update(prop, 0, "rna_XrActionMap_name_update");
+ RNA_def_struct_name_property(srna, prop);
+
+ prop = RNA_def_property(srna, "actionmap_items", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "items", NULL);
+ RNA_def_property_struct_type(prop, "XrActionMapItem");
+ RNA_def_property_ui_text(
+ prop,
+ "Items",
+ "Items in the action map, mapping an XR event to an operator, pose, or haptic output");
+ rna_def_xr_actionmap_items(brna, prop);
+
+ prop = RNA_def_property(srna, "selected_item", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "selitem");
+ RNA_def_property_ui_text(prop, "Selected Item", "");
+
+ /* XrActionMapItem */
+ srna = RNA_def_struct(brna, "XrActionMapItem", NULL);
+ RNA_def_struct_sdna(srna, "XrActionMapItem");
+ RNA_def_struct_ui_text(srna, "XR Action Map Item", "");
+
+ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Name", "Name of the action map item");
+ RNA_def_property_update(prop, 0, "rna_XrActionMapItem_name_update");
+ RNA_def_struct_name_property(srna, prop);
+
+ prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_enum_xr_action_types);
+ RNA_def_property_ui_text(prop, "Type", "Action type");
+ RNA_def_property_update(prop, 0, "rna_XrActionMapItem_update");
+
+ prop = RNA_def_property(srna, "user_path0", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_maxlength(prop, 64);
+ RNA_def_property_ui_text(prop, "User Path 0", "OpenXR user path");
+
+ prop = RNA_def_property(srna, "user_path1", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_maxlength(prop, 64);
+ RNA_def_property_ui_text(prop, "User Path 1", "OpenXR user path");
+
+ prop = RNA_def_property(srna, "op", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_maxlength(prop, OP_MAX_TYPENAME);
+ RNA_def_property_ui_text(prop, "Operator", "Identifier of operator to call on action event");
+ RNA_def_property_update(prop, 0, "rna_XrActionMapItem_update");
+
+ prop = RNA_def_property(srna, "op_name", PROP_STRING, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(
+ prop, "Operator Name", "Name of operator (translated) to call on action event");
+ RNA_def_property_string_funcs(
+ prop, "rna_XrActionMapItem_op_name_get", "rna_XrActionMapItem_op_name_length", NULL);
+
+ prop = RNA_def_property(srna, "op_properties", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "OperatorProperties");
+ RNA_def_property_pointer_funcs(prop, "rna_XrActionMapItem_op_properties_get", NULL, NULL, NULL);
+ RNA_def_property_ui_text(
+ prop, "Operator Properties", "Properties to set when the operator is called");
+ RNA_def_property_update(prop, 0, "rna_XrActionMapItem_update");
+
+ prop = RNA_def_property(srna, "op_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "op_flag");
+ RNA_def_property_enum_items(prop, rna_enum_xr_op_flags);
+ RNA_def_property_ui_text(prop, "Operator Mode", "Operator execution mode");
+
+ prop = RNA_def_property(srna, "bimanual", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_funcs(
+ prop, "rna_XrActionMapItem_bimanual_get", "rna_XrActionMapItem_bimanual_set");
+ RNA_def_property_ui_text(
+ prop, "Bimanual", "The action depends on the states/poses of both user paths");
+
+ prop = RNA_def_property(srna, "pose_is_controller_grip", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_funcs(prop,
+ "rna_XrActionMapItem_pose_is_controller_grip_get",
+ "rna_XrActionMapItem_pose_is_controller_grip_set");
+ RNA_def_property_ui_text(
+ prop, "Is Controller Grip", "The action poses will be used for the VR controller grips");
+
+ prop = RNA_def_property(srna, "pose_is_controller_aim", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_funcs(prop,
+ "rna_XrActionMapItem_pose_is_controller_aim_get",
+ "rna_XrActionMapItem_pose_is_controller_aim_set");
+ RNA_def_property_ui_text(
+ prop, "Is Controller Aim", "The action poses will be used for the VR controller aims");
+
+ prop = RNA_def_property(srna, "haptic_name", PROP_STRING, PROP_NONE);
+ RNA_def_property_ui_text(
+ prop, "Haptic Name", "Name of the haptic action to apply when executing this action");
+
+ prop = RNA_def_property(srna, "haptic_match_user_paths", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_funcs(prop,
+ "rna_XrActionMapItem_haptic_match_user_paths_get",
+ "rna_XrActionMapItem_haptic_match_user_paths_set");
+ RNA_def_property_ui_text(
+ prop,
+ "Haptic Match User Paths",
+ "Apply haptics to the same user paths for the haptic action and this action");
+
+ prop = RNA_def_property(srna, "haptic_duration", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0, FLT_MAX);
+ RNA_def_property_ui_text(prop,
+ "Haptic Duration",
+ "Haptic duration in seconds. 0.0 is the minimum supported duration");
+
+ prop = RNA_def_property(srna, "haptic_frequency", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0, FLT_MAX);
+ RNA_def_property_ui_text(prop,
+ "Haptic Frequency",
+ "Frequency of the haptic vibration in hertz. 0.0 specifies the OpenXR "
+ "runtime's default frequency");
+
+ prop = RNA_def_property(srna, "haptic_amplitude", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0, 1.0);
+ RNA_def_property_ui_text(
+ prop, "Haptic Amplitude", "Intensity of the haptic vibration, ranging from 0.0 to 1.0");
+
+ prop = RNA_def_property(srna, "haptic_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_enum_xr_haptic_flags);
+ RNA_def_property_enum_funcs(
+ prop, "rna_XrActionMapItem_haptic_mode_get", "rna_XrActionMapItem_haptic_mode_set", NULL);
+ RNA_def_property_ui_text(prop, "Haptic mode", "Haptic application mode");
+
+ prop = RNA_def_property(srna, "bindings", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_type(prop, "XrActionMapBinding");
+ RNA_def_property_ui_text(
+ prop, "Bindings", "Bindings for the action map item, mapping the action to an XR input");
+ rna_def_xr_actionmap_bindings(brna, prop);
+
+ prop = RNA_def_property(srna, "selected_binding", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "selbinding");
+ RNA_def_property_ui_text(prop, "Selected Binding", "Currently selected binding");
+
+ /* XrActionMapBinding */
+ srna = RNA_def_struct(brna, "XrActionMapBinding", NULL);
+ RNA_def_struct_sdna(srna, "XrActionMapBinding");
+ RNA_def_struct_ui_text(srna, "XR Action Map Binding", "Binding in an XR action map item");
+
+ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Name", "Name of the action map binding");
+ RNA_def_property_update(prop, 0, "rna_XrActionMapBinding_name_update");
+ RNA_def_struct_name_property(srna, prop);
+
+ prop = RNA_def_property(srna, "profile", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_maxlength(prop, 256);
+ RNA_def_property_ui_text(prop, "Profile", "OpenXR interaction profile path");
+
+ prop = RNA_def_property(srna, "component_path0", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_maxlength(prop, 192);
+ RNA_def_property_ui_text(prop, "Component Path 0", "OpenXR component path");
+
+ prop = RNA_def_property(srna, "component_path1", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_maxlength(prop, 192);
+ RNA_def_property_ui_text(prop, "Component Path 1", "OpenXR component path");
+
+ prop = RNA_def_property(srna, "threshold", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "float_threshold");
+ RNA_def_property_range(prop, 0.0, 1.0);
+ RNA_def_property_ui_text(prop, "Threshold", "Input threshold for button/axis actions");
+
+ prop = RNA_def_property(srna, "axis0_region", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_enum_xr_axis0_flags);
+ RNA_def_property_enum_funcs(prop,
+ "rna_XrActionMapBinding_axis0_region_get",
+ "rna_XrActionMapBinding_axis0_region_set",
+ NULL);
+ RNA_def_property_ui_text(
+ prop, "Axis 0 Region", "Action execution region for the first input axis");
+
+ prop = RNA_def_property(srna, "axis1_region", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_enum_xr_axis1_flags);
+ RNA_def_property_enum_funcs(prop,
+ "rna_XrActionMapBinding_axis1_region_get",
+ "rna_XrActionMapBinding_axis1_region_set",
+ NULL);
+ RNA_def_property_ui_text(
+ prop, "Axis 1 Region", "Action execution region for the second input axis");
+
+ prop = RNA_def_property(srna, "pose_location", PROP_FLOAT, PROP_TRANSLATION);
+ RNA_def_property_ui_text(prop, "Pose Location Offset", "");
+
+ prop = RNA_def_property(srna, "pose_rotation", PROP_FLOAT, PROP_EULER);
+ RNA_def_property_ui_text(prop, "Pose Rotation Offset", "");
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name XR Session Settings
+ * \{ */
+
static void rna_def_xr_session_settings(BlenderRNA *brna)
{
StructRNA *srna;
@@ -241,6 +1477,12 @@ static void rna_def_xr_session_settings(BlenderRNA *brna)
RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name XR Session State
+ * \{ */
+
static void rna_def_xr_session_state(BlenderRNA *brna)
{
StructRNA *srna;
@@ -265,6 +1507,260 @@ static void rna_def_xr_session_state(BlenderRNA *brna)
parm = RNA_def_pointer(func, "context", "Context", "", "");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ func = RNA_def_function(srna, "action_set_create", "rna_XrSessionState_action_set_create");
+ RNA_def_function_ui_description(func, "Create a VR action set");
+ RNA_def_function_flag(func, FUNC_NO_SELF);
+ parm = RNA_def_pointer(func, "context", "Context", "", "");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_pointer(func, "actionmap", "XrActionMap", "", "");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_boolean(func, "result", 0, "Result", "");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "action_create", "rna_XrSessionState_action_create");
+ RNA_def_function_ui_description(func, "Create a VR action");
+ RNA_def_function_flag(func, FUNC_NO_SELF);
+ parm = RNA_def_pointer(func, "context", "Context", "", "");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_pointer(func, "actionmap", "XrActionMap", "", "");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_pointer(func, "actionmap_item", "XrActionMapItem", "", "");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_boolean(func, "result", 0, "Result", "");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(
+ srna, "action_binding_create", "rna_XrSessionState_action_binding_create");
+ RNA_def_function_ui_description(func, "Create a VR action binding");
+ RNA_def_function_flag(func, FUNC_NO_SELF);
+ parm = RNA_def_pointer(func, "context", "Context", "", "");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_pointer(func, "actionmap", "XrActionMap", "", "");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_pointer(func, "actionmap_item", "XrActionMapItem", "", "");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_pointer(func, "actionmap_binding", "XrActionMapBinding", "", "");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_boolean(func, "result", 0, "Result", "");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(
+ srna, "active_action_set_set", "rna_XrSessionState_active_action_set_set");
+ RNA_def_function_ui_description(func, "Set the active VR action set");
+ RNA_def_function_flag(func, FUNC_NO_SELF);
+ parm = RNA_def_pointer(func, "context", "Context", "", "");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_string(func, "action_set", NULL, 64, "Action Set", "Action set name");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_boolean(func, "result", 0, "Result", "");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(
+ srna, "controller_pose_actions_set", "rna_XrSessionState_controller_pose_actions_set");
+ RNA_def_function_ui_description(func, "Set the actions that determine the VR controller poses");
+ RNA_def_function_flag(func, FUNC_NO_SELF);
+ parm = RNA_def_pointer(func, "context", "Context", "", "");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_string(func, "action_set", NULL, 64, "Action Set", "Action set name");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_string(func,
+ "grip_action",
+ NULL,
+ 64,
+ "Grip Action",
+ "Name of the action representing the controller grips");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_string(func,
+ "aim_action",
+ NULL,
+ 64,
+ "Aim Action",
+ "Name of the action representing the controller aims");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_boolean(func, "result", 0, "Result", "");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "action_state_get", "rna_XrSessionState_action_state_get");
+ RNA_def_function_ui_description(func, "Get the current state of a VR action");
+ RNA_def_function_flag(func, FUNC_NO_SELF);
+ parm = RNA_def_pointer(func, "context", "Context", "", "");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_string(func, "action_set_name", NULL, 64, "Action Set", "Action set name");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_string(func, "action_name", NULL, 64, "Action", "Action name");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_string(func, "user_path", NULL, 64, "User Path", "OpenXR user path");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_float_array(
+ func,
+ "state",
+ 2,
+ NULL,
+ -FLT_MAX,
+ FLT_MAX,
+ "Action State",
+ "Current state of the VR action. Second float value is only set for 2D vector type actions",
+ -FLT_MAX,
+ FLT_MAX);
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_OUTPUT);
+
+ func = RNA_def_function(srna, "haptic_action_apply", "rna_XrSessionState_haptic_action_apply");
+ RNA_def_function_ui_description(func, "Apply a VR haptic action");
+ RNA_def_function_flag(func, FUNC_NO_SELF);
+ parm = RNA_def_pointer(func, "context", "Context", "", "");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_string(func, "action_set_name", NULL, 64, "Action Set", "Action set name");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_string(func, "action_name", NULL, 64, "Action", "Action name");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_string(
+ func,
+ "user_path",
+ NULL,
+ 64,
+ "User Path",
+ "Optional OpenXR user path. If not set, the action will be applied to all paths");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_float(func,
+ "duration",
+ 0.0f,
+ 0.0f,
+ FLT_MAX,
+ "Duration",
+ "Haptic duration in seconds. 0.0 is the minimum supported duration",
+ 0.0f,
+ FLT_MAX);
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_float(func,
+ "frequency",
+ 0.0f,
+ 0.0f,
+ FLT_MAX,
+ "Frequency",
+ "Frequency of the haptic vibration in hertz. 0.0 specifies the OpenXR "
+ "runtime's default frequency",
+ 0.0f,
+ FLT_MAX);
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_float(func,
+ "amplitude",
+ 1.0f,
+ 0.0f,
+ 1.0f,
+ "Amplitude",
+ "Haptic amplitude, ranging from 0.0 to 1.0",
+ 0.0f,
+ 1.0f);
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_boolean(func, "result", 0, "Result", "");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "haptic_action_stop", "rna_XrSessionState_haptic_action_stop");
+ RNA_def_function_ui_description(func, "Stop a VR haptic action");
+ RNA_def_function_flag(func, FUNC_NO_SELF);
+ parm = RNA_def_pointer(func, "context", "Context", "", "");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_string(func, "action_set_name", NULL, 64, "Action Set", "Action set name");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_string(func, "action_name", NULL, 64, "Action", "Action name");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_string(
+ func,
+ "user_path",
+ NULL,
+ 64,
+ "User Path",
+ "Optional OpenXR user path. If not set, the action will be stopped for all paths");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+
+ func = RNA_def_function(
+ srna, "controller_grip_location_get", "rna_XrSessionState_controller_grip_location_get");
+ RNA_def_function_ui_description(func,
+ "Get the last known controller grip location in world space");
+ RNA_def_function_flag(func, FUNC_NO_SELF);
+ parm = RNA_def_pointer(func, "context", "Context", "", "");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_int(func, "index", 0, 0, 255, "Index", "Controller index", 0, 255);
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_float_translation(func,
+ "location",
+ 3,
+ NULL,
+ -FLT_MAX,
+ FLT_MAX,
+ "Location",
+ "Controller grip location",
+ -FLT_MAX,
+ FLT_MAX);
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_OUTPUT);
+
+ func = RNA_def_function(
+ srna, "controller_grip_rotation_get", "rna_XrSessionState_controller_grip_rotation_get");
+ RNA_def_function_ui_description(
+ func, "Get the last known controller grip rotation (quaternion) in world space");
+ RNA_def_function_flag(func, FUNC_NO_SELF);
+ parm = RNA_def_pointer(func, "context", "Context", "", "");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_int(func, "index", 0, 0, 255, "Index", "Controller index", 0, 255);
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_float_vector(func,
+ "rotation",
+ 4,
+ NULL,
+ -FLT_MAX,
+ FLT_MAX,
+ "Rotation",
+ "Controller grip quaternion rotation",
+ -FLT_MAX,
+ FLT_MAX);
+ parm->subtype = PROP_QUATERNION;
+ RNA_def_property_ui_range(parm, -FLT_MAX, FLT_MAX, 1, 5);
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_OUTPUT);
+
+ func = RNA_def_function(
+ srna, "controller_aim_location_get", "rna_XrSessionState_controller_aim_location_get");
+ RNA_def_function_ui_description(func,
+ "Get the last known controller aim location in world space");
+ RNA_def_function_flag(func, FUNC_NO_SELF);
+ parm = RNA_def_pointer(func, "context", "Context", "", "");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_int(func, "index", 0, 0, 255, "Index", "Controller index", 0, 255);
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_float_translation(func,
+ "location",
+ 3,
+ NULL,
+ -FLT_MAX,
+ FLT_MAX,
+ "Location",
+ "Controller aim location",
+ -FLT_MAX,
+ FLT_MAX);
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_OUTPUT);
+
+ func = RNA_def_function(
+ srna, "controller_aim_rotation_get", "rna_XrSessionState_controller_aim_rotation_get");
+ RNA_def_function_ui_description(
+ func, "Get the last known controller aim rotation (quaternion) in world space");
+ RNA_def_function_flag(func, FUNC_NO_SELF);
+ parm = RNA_def_pointer(func, "context", "Context", "", "");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
+ parm = RNA_def_int(func, "index", 0, 0, 255, "Index", "Controller index", 0, 255);
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ parm = RNA_def_float_vector(func,
+ "rotation",
+ 4,
+ NULL,
+ -FLT_MAX,
+ FLT_MAX,
+ "Rotation",
+ "Controller aim quaternion rotation",
+ -FLT_MAX,
+ FLT_MAX);
+ parm->subtype = PROP_QUATERNION;
+ RNA_def_property_ui_range(parm, -FLT_MAX, FLT_MAX, 1, 5);
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_OUTPUT);
+
prop = RNA_def_property(srna, "viewer_pose_location", PROP_FLOAT, PROP_TRANSLATION);
RNA_def_property_array(prop, 3);
RNA_def_property_float_funcs(prop, "rna_XrSessionState_viewer_pose_location_get", NULL, NULL);
@@ -282,12 +1778,43 @@ static void rna_def_xr_session_state(BlenderRNA *brna)
prop,
"Viewer Pose Rotation",
"Last known rotation of the viewer pose (center between the eyes) in world space");
+
+ prop = RNA_def_property(srna, "actionmaps", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_funcs(prop,
+ "rna_XrSessionState_actionmaps_begin",
+ "rna_iterator_listbase_next",
+ "rna_iterator_listbase_end",
+ "rna_iterator_listbase_get",
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ RNA_def_property_struct_type(prop, "XrActionMap");
+ RNA_def_property_ui_text(prop, "XR Action Maps", "");
+ rna_def_xr_actionmaps(brna, prop);
+
+ prop = RNA_def_property(srna, "active_actionmap", PROP_INT, PROP_NONE);
+ RNA_def_property_int_funcs(prop,
+ "rna_XrSessionState_active_actionmap_get",
+ "rna_XrSessionState_active_actionmap_set",
+ NULL);
+ RNA_def_property_ui_text(prop, "Active Action Map", "");
+
+ prop = RNA_def_property(srna, "selected_actionmap", PROP_INT, PROP_NONE);
+ RNA_def_property_int_funcs(prop,
+ "rna_XrSessionState_selected_actionmap_get",
+ "rna_XrSessionState_selected_actionmap_set",
+ NULL);
+ RNA_def_property_ui_text(prop, "Selected Action Map", "");
}
+/** \} */
+
void RNA_def_xr(BlenderRNA *brna)
{
RNA_define_animate_sdna(false);
+ rna_def_xr_actionmap(brna);
rna_def_xr_session_settings(brna);
rna_def_xr_session_state(brna);
diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt
index 0138dd0c3ad..d9b9fa96d04 100644
--- a/source/blender/modifiers/CMakeLists.txt
+++ b/source/blender/modifiers/CMakeLists.txt
@@ -141,6 +141,16 @@ if(WITH_ALEMBIC)
)
endif()
+if(WITH_USD)
+ add_definitions(-DWITH_USD)
+ list(APPEND INC
+ ../io/usd
+ )
+ list(APPEND LIB
+ bf_usd
+ )
+endif()
+
if(WITH_MOD_REMESH)
list(APPEND INC
../../../intern/dualcon
diff --git a/source/blender/modifiers/intern/MOD_build.c b/source/blender/modifiers/intern/MOD_build.c
index 52f21e3d3d0..a344a15b0c1 100644
--- a/source/blender/modifiers/intern/MOD_build.c
+++ b/source/blender/modifiers/intern/MOD_build.c
@@ -61,7 +61,9 @@ static void initData(ModifierData *md)
MEMCPY_STRUCT_AFTER(bmd, DNA_struct_default_get(BuildModifierData), modifier);
}
-static bool dependsOnTime(ModifierData *UNUSED(md))
+static bool dependsOnTime(struct Scene *UNUSED(scene),
+ ModifierData *UNUSED(md),
+ const int UNUSED(dag_eval_mode))
{
return true;
}
diff --git a/source/blender/modifiers/intern/MOD_cloth.c b/source/blender/modifiers/intern/MOD_cloth.c
index 4487adcfdda..fa2f70e1a9c 100644
--- a/source/blender/modifiers/intern/MOD_cloth.c
+++ b/source/blender/modifiers/intern/MOD_cloth.c
@@ -220,7 +220,9 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla
tclmd->solver_result = NULL;
}
-static bool dependsOnTime(ModifierData *UNUSED(md))
+static bool dependsOnTime(struct Scene *UNUSED(scene),
+ ModifierData *UNUSED(md),
+ const int UNUSED(dag_eval_mode))
{
return true;
}
diff --git a/source/blender/modifiers/intern/MOD_collision.c b/source/blender/modifiers/intern/MOD_collision.c
index 5dd57469914..e7d5fe056c5 100644
--- a/source/blender/modifiers/intern/MOD_collision.c
+++ b/source/blender/modifiers/intern/MOD_collision.c
@@ -95,7 +95,9 @@ static void freeData(ModifierData *md)
}
}
-static bool dependsOnTime(ModifierData *UNUSED(md))
+static bool dependsOnTime(struct Scene *UNUSED(scene),
+ ModifierData *UNUSED(md),
+ const int UNUSED(dag_eval_mode))
{
return true;
}
diff --git a/source/blender/modifiers/intern/MOD_displace.c b/source/blender/modifiers/intern/MOD_displace.c
index a7ac9f618af..07da18f990d 100644
--- a/source/blender/modifiers/intern/MOD_displace.c
+++ b/source/blender/modifiers/intern/MOD_displace.c
@@ -95,7 +95,9 @@ static void requiredDataMask(Object *UNUSED(ob),
}
}
-static bool dependsOnTime(ModifierData *md)
+static bool dependsOnTime(struct Scene *UNUSED(scene),
+ ModifierData *md,
+ const int UNUSED(dag_eval_mode))
{
DisplaceModifierData *dmd = (DisplaceModifierData *)md;
diff --git a/source/blender/modifiers/intern/MOD_dynamicpaint.c b/source/blender/modifiers/intern/MOD_dynamicpaint.c
index 8b1d541d45d..77ae5c4b6f1 100644
--- a/source/blender/modifiers/intern/MOD_dynamicpaint.c
+++ b/source/blender/modifiers/intern/MOD_dynamicpaint.c
@@ -155,7 +155,9 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
}
}
-static bool dependsOnTime(ModifierData *UNUSED(md))
+static bool dependsOnTime(struct Scene *UNUSED(scene),
+ ModifierData *UNUSED(md),
+ const int UNUSED(dag_eval_mode))
{
return true;
}
diff --git a/source/blender/modifiers/intern/MOD_explode.c b/source/blender/modifiers/intern/MOD_explode.c
index bf197dca7e5..493b59b3a1a 100644
--- a/source/blender/modifiers/intern/MOD_explode.c
+++ b/source/blender/modifiers/intern/MOD_explode.c
@@ -86,7 +86,9 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla
temd->facepa = NULL;
}
-static bool dependsOnTime(ModifierData *UNUSED(md))
+static bool dependsOnTime(struct Scene *UNUSED(scene),
+ ModifierData *UNUSED(md),
+ const int UNUSED(dag_eval_mode))
{
return true;
}
diff --git a/source/blender/modifiers/intern/MOD_fluid.c b/source/blender/modifiers/intern/MOD_fluid.c
index 36d2ab2a11a..a14d582063a 100644
--- a/source/blender/modifiers/intern/MOD_fluid.c
+++ b/source/blender/modifiers/intern/MOD_fluid.c
@@ -132,7 +132,9 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
#endif /* WITH_FLUID */
}
-static bool dependsOnTime(ModifierData *UNUSED(md))
+static bool dependsOnTime(struct Scene *UNUSED(scene),
+ ModifierData *UNUSED(md),
+ const int UNUSED(dag_eval_mode))
{
return true;
}
diff --git a/source/blender/modifiers/intern/MOD_laplaciansmooth.c b/source/blender/modifiers/intern/MOD_laplaciansmooth.c
index 63495c4104e..a36a8c386b4 100644
--- a/source/blender/modifiers/intern/MOD_laplaciansmooth.c
+++ b/source/blender/modifiers/intern/MOD_laplaciansmooth.c
@@ -66,7 +66,7 @@ struct BLaplacianSystem {
int numVerts; /* Number of verts. */
short *numNeFa; /* Number of neighbors faces around vertice. */
short *numNeEd; /* Number of neighbors Edges around vertice. */
- short *zerola; /* Is zero area or length. */
+ bool *zerola; /* Is zero area or length. */
/* Pointers to data. */
float (*vertexCos)[3];
@@ -130,7 +130,7 @@ static void memset_laplacian_system(LaplacianSystem *sys, int val)
memset(sys->ring_areas, val, sizeof(float) * sys->numVerts);
memset(sys->vlengths, val, sizeof(float) * sys->numVerts);
memset(sys->vweights, val, sizeof(float) * sys->numVerts);
- memset(sys->zerola, val, sizeof(short) * sys->numVerts);
+ memset(sys->zerola, val, sizeof(bool) * sys->numVerts);
}
static LaplacianSystem *init_laplacian_system(int a_numEdges,
@@ -152,7 +152,7 @@ static LaplacianSystem *init_laplacian_system(int a_numEdges,
sys->ring_areas = MEM_calloc_arrayN(sys->numVerts, sizeof(float), __func__);
sys->vlengths = MEM_calloc_arrayN(sys->numVerts, sizeof(float), __func__);
sys->vweights = MEM_calloc_arrayN(sys->numVerts, sizeof(float), __func__);
- sys->zerola = MEM_calloc_arrayN(sys->numVerts, sizeof(short), __func__);
+ sys->zerola = MEM_calloc_arrayN(sys->numVerts, sizeof(bool), __func__);
return sys;
}
@@ -225,8 +225,8 @@ static void init_laplacian_matrix(LaplacianSystem *sys)
sys->numNeEd[idv2] = sys->numNeEd[idv2] + 1;
w1 = len_v3v3(v1, v2);
if (w1 < sys->min_area) {
- sys->zerola[idv1] = 1;
- sys->zerola[idv2] = 1;
+ sys->zerola[idv1] = true;
+ sys->zerola[idv2] = true;
}
else {
w1 = 1.0f / w1;
@@ -253,7 +253,7 @@ static void init_laplacian_matrix(LaplacianSystem *sys)
areaf = area_tri_v3(v_prev, v_curr, v_next);
if (areaf < sys->min_area) {
- sys->zerola[l_curr->v] = 1;
+ sys->zerola[l_curr->v] = true;
}
sys->ring_areas[l_prev->v] += areaf;
@@ -300,7 +300,7 @@ static void fill_laplacian_matrix(LaplacianSystem *sys)
const uint l_curr_index = l_curr - sys->mloop;
/* Is ring if number of faces == number of edges around vertice. */
- if (sys->numNeEd[l_curr->v] == sys->numNeFa[l_curr->v] && sys->zerola[l_curr->v] == 0) {
+ if (sys->numNeEd[l_curr->v] == sys->numNeFa[l_curr->v] && sys->zerola[l_curr->v] == false) {
EIG_linear_solver_matrix_add(sys->context,
l_curr->v,
l_next->v,
@@ -310,7 +310,7 @@ static void fill_laplacian_matrix(LaplacianSystem *sys)
l_prev->v,
sys->fweights[l_curr_index][1] * sys->vweights[l_curr->v]);
}
- if (sys->numNeEd[l_next->v] == sys->numNeFa[l_next->v] && sys->zerola[l_next->v] == 0) {
+ if (sys->numNeEd[l_next->v] == sys->numNeFa[l_next->v] && sys->zerola[l_next->v] == false) {
EIG_linear_solver_matrix_add(sys->context,
l_next->v,
l_curr->v,
@@ -320,7 +320,7 @@ static void fill_laplacian_matrix(LaplacianSystem *sys)
l_prev->v,
sys->fweights[l_curr_index][0] * sys->vweights[l_next->v]);
}
- if (sys->numNeEd[l_prev->v] == sys->numNeFa[l_prev->v] && sys->zerola[l_prev->v] == 0) {
+ if (sys->numNeEd[l_prev->v] == sys->numNeFa[l_prev->v] && sys->zerola[l_prev->v] == false) {
EIG_linear_solver_matrix_add(sys->context,
l_prev->v,
l_curr->v,
@@ -338,7 +338,7 @@ static void fill_laplacian_matrix(LaplacianSystem *sys)
idv2 = sys->medges[i].v2;
/* Is boundary */
if (sys->numNeEd[idv1] != sys->numNeFa[idv1] && sys->numNeEd[idv2] != sys->numNeFa[idv2] &&
- sys->zerola[idv1] == 0 && sys->zerola[idv2] == 0) {
+ sys->zerola[idv1] == false && sys->zerola[idv2] == false) {
EIG_linear_solver_matrix_add(
sys->context, idv1, idv2, sys->eweights[i] * sys->vlengths[idv1]);
EIG_linear_solver_matrix_add(
@@ -358,7 +358,7 @@ static void validate_solution(LaplacianSystem *sys, short flag, float lambda, fl
sys->vert_centroid, sys->vertexCos, sys->mpoly, sys->numPolys, sys->mloop);
}
for (i = 0; i < sys->numVerts; i++) {
- if (sys->zerola[i] == 0) {
+ if (sys->zerola[i] == false) {
lam = sys->numNeEd[i] == sys->numNeFa[i] ? (lambda >= 0.0f ? 1.0f : -1.0f) :
(lambda_border >= 0.0f ? 1.0f : -1.0f);
if (flag & MOD_LAPLACIANSMOOTH_X) {
@@ -442,7 +442,7 @@ static void laplaciansmoothModifier_do(
wpaint = 1.0f;
}
- if (sys->zerola[i] == 0) {
+ if (sys->zerola[i] == false) {
if (smd->flag & MOD_LAPLACIANSMOOTH_NORMALIZED) {
w = sys->vweights[i];
sys->vweights[i] = (w == 0.0f) ? 0.0f : -fabsf(smd->lambda) * wpaint / w;
diff --git a/source/blender/modifiers/intern/MOD_meshcache.c b/source/blender/modifiers/intern/MOD_meshcache.c
index e0507320628..6ef64ad8bc9 100644
--- a/source/blender/modifiers/intern/MOD_meshcache.c
+++ b/source/blender/modifiers/intern/MOD_meshcache.c
@@ -63,7 +63,9 @@ static void initData(ModifierData *md)
MEMCPY_STRUCT_AFTER(mcmd, DNA_struct_default_get(MeshCacheModifierData), modifier);
}
-static bool dependsOnTime(ModifierData *md)
+static bool dependsOnTime(struct Scene *UNUSED(scene),
+ ModifierData *md,
+ const int UNUSED(dag_eval_mode))
{
MeshCacheModifierData *mcmd = (MeshCacheModifierData *)md;
return (mcmd->play_mode == MOD_MESHCACHE_PLAY_CFEA);
diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.c b/source/blender/modifiers/intern/MOD_meshsequencecache.c
index c2f9cd8c867..259c1cb2417 100644
--- a/source/blender/modifiers/intern/MOD_meshsequencecache.c
+++ b/source/blender/modifiers/intern/MOD_meshsequencecache.c
@@ -20,6 +20,7 @@
#include <string.h>
+#include "BLI_math_vector.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
@@ -39,6 +40,8 @@
#include "BKE_cachefile.h"
#include "BKE_context.h"
#include "BKE_lib_query.h"
+#include "BKE_mesh.h"
+#include "BKE_object.h"
#include "BKE_scene.h"
#include "BKE_screen.h"
@@ -55,18 +58,29 @@
#include "MOD_modifiertypes.h"
#include "MOD_ui_common.h"
-#ifdef WITH_ALEMBIC
-# include "ABC_alembic.h"
+#if defined(WITH_USD) || defined(WITH_ALEMBIC)
# include "BKE_global.h"
# include "BKE_lib_id.h"
#endif
+#ifdef WITH_ALEMBIC
+# include "ABC_alembic.h"
+#endif
+
+#ifdef WITH_USD
+# include "usd.h"
+#endif
+
static void initData(ModifierData *md)
{
MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md;
BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(mcmd, modifier));
+ mcmd->cache_file = NULL;
+ mcmd->object_path[0] = '\0';
+ mcmd->read_flag = MOD_MESHSEQ_READ_ALL;
+
MEMCPY_STRUCT_AFTER(mcmd, DNA_struct_default_get(MeshSeqCacheModifierData), modifier);
}
@@ -107,9 +121,47 @@ static bool isDisabled(const struct Scene *UNUSED(scene),
return (mcmd->cache_file == NULL) || (mcmd->object_path[0] == '\0');
}
+static Mesh *generate_bounding_box_mesh(Object *object, Mesh *org_mesh)
+{
+ BoundBox *bb = BKE_object_boundbox_get(object);
+ Mesh *result = BKE_mesh_new_nomain_from_template(org_mesh, 8, 0, 0, 24, 6);
+
+ MVert *mvert = result->mvert;
+ for (int i = 0; i < 8; ++i) {
+ copy_v3_v3(mvert[i].co, bb->vec[i]);
+ }
+
+ /* See DNA_object_types.h for the diagram showing the order of the vertices for a BoundBox. */
+ static unsigned int loops_v[6][4] = {
+ {0, 4, 5, 1},
+ {4, 7, 6, 5},
+ {7, 3, 2, 6},
+ {3, 0, 1, 2},
+ {1, 5, 6, 2},
+ {3, 7, 4, 0},
+ };
+
+ MLoop *mloop = result->mloop;
+ for (int i = 0; i < 6; ++i) {
+ for (int j = 0; j < 4; ++j, ++mloop) {
+ mloop->v = loops_v[i][j];
+ }
+ }
+
+ MPoly *mpoly = result->mpoly;
+ for (int i = 0; i < 6; ++i) {
+ mpoly[i].loopstart = i * 4;
+ mpoly[i].totloop = 4;
+ }
+
+ BKE_mesh_calc_edges(result, false, false);
+
+ return result;
+}
+
static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
{
-#ifdef WITH_ALEMBIC
+#if defined(WITH_USD) || defined(WITH_ALEMBIC)
MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md;
/* Only used to check whether we are operating on org data or not... */
@@ -127,16 +179,38 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
BKE_cachefile_reader_open(cache_file, &mcmd->reader, ctx->object, mcmd->object_path);
if (!mcmd->reader) {
BKE_modifier_set_error(
- ctx->object, md, "Could not create Alembic reader for file %s", cache_file->filepath);
+ ctx->object, md, "Could not create reader for file %s", cache_file->filepath);
return mesh;
}
}
- /* If this invocation is for the ORCO mesh, and the mesh in Alembic hasn't changed topology, we
+ /* Do not process data if using a render procedural, return a box instead for displaying in the
+ * viewport. */
+ if (BKE_cache_file_uses_render_procedural(cache_file, scene, DEG_get_mode(ctx->depsgraph))) {
+ return generate_bounding_box_mesh(ctx->object, org_mesh);
+ }
+
+ /* If this invocation is for the ORCO mesh, and the mesh hasn't changed topology, we
* must return the mesh as-is instead of deforming it. */
- if (ctx->flag & MOD_APPLY_ORCO &&
- !ABC_mesh_topology_changed(mcmd->reader, ctx->object, mesh, time, &err_str)) {
- return mesh;
+ if (ctx->flag & MOD_APPLY_ORCO) {
+ switch (cache_file->type) {
+ case CACHEFILE_TYPE_ALEMBIC:
+# ifdef WITH_ALEMBIC
+ if (!ABC_mesh_topology_changed(mcmd->reader, ctx->object, mesh, time, &err_str)) {
+ return mesh;
+ }
+# endif
+ break;
+ case CACHEFILE_TYPE_USD:
+# ifdef WITH_USD
+ if (!USD_mesh_topology_changed(mcmd->reader, ctx->object, mesh, time, &err_str)) {
+ return mesh;
+ }
+# endif
+ break;
+ case CACHE_FILE_TYPE_INVALID:
+ break;
+ }
}
if (me != NULL) {
@@ -156,7 +230,23 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
}
}
- Mesh *result = ABC_read_mesh(mcmd->reader, ctx->object, mesh, time, &err_str, mcmd->read_flag);
+ Mesh *result = NULL;
+
+ switch (cache_file->type) {
+ case CACHEFILE_TYPE_ALEMBIC:
+# ifdef WITH_ALEMBIC
+ result = ABC_read_mesh(mcmd->reader, ctx->object, mesh, time, &err_str, mcmd->read_flag);
+# endif
+ break;
+ case CACHEFILE_TYPE_USD:
+# ifdef WITH_USD
+ result = USD_read_mesh(
+ mcmd->reader, ctx->object, mesh, time * FPS, &err_str, mcmd->read_flag);
+# endif
+ break;
+ case CACHE_FILE_TYPE_INVALID:
+ break;
+ }
mcmd->velocity_delta = 1.0f;
if (mcmd->cache_file->velocity_unit == CACHEFILE_VELOCITY_UNIT_SECOND) {
@@ -180,18 +270,20 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
return result ? result : mesh;
#else
- UNUSED_VARS(ctx, md);
+ UNUSED_VARS(ctx, md, generate_bounding_box_mesh);
return mesh;
#endif
}
-static bool dependsOnTime(ModifierData *md)
+static bool dependsOnTime(Scene *scene, ModifierData *md, const int dag_eval_mode)
{
-#ifdef WITH_ALEMBIC
+#if defined(WITH_USD) || defined(WITH_ALEMBIC)
MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md;
- return (mcmd->cache_file != NULL);
+ /* Do not evaluate animations if using the render engine procedural. */
+ return (mcmd->cache_file != NULL) &&
+ !BKE_cache_file_uses_render_procedural(mcmd->cache_file, scene, dag_eval_mode);
#else
- UNUSED_VARS(md);
+ UNUSED_VARS(scene, md, dag_eval_mode);
return false;
#endif
}
diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc
index 5fa11ffdd10..620c7ef438a 100644
--- a/source/blender/modifiers/intern/MOD_nodes.cc
+++ b/source/blender/modifiers/intern/MOD_nodes.cc
@@ -84,7 +84,8 @@
#include "NOD_derived_node_tree.hh"
#include "NOD_geometry.h"
#include "NOD_geometry_nodes_eval_log.hh"
-#include "NOD_node_tree_multi_function.hh"
+
+#include "FN_multi_function.hh"
using blender::destruct_ptr;
using blender::float3;
@@ -721,17 +722,17 @@ static void initialize_group_input(NodesModifierData &nmd,
return;
}
if (nmd.settings.properties == nullptr) {
- blender::nodes::socket_cpp_value_get(socket, r_value);
+ socket.typeinfo->get_geometry_nodes_cpp_value(socket, r_value);
return;
}
const IDProperty *property = IDP_GetPropertyFromGroup(nmd.settings.properties,
socket.identifier);
if (property == nullptr) {
- blender::nodes::socket_cpp_value_get(socket, r_value);
+ socket.typeinfo->get_geometry_nodes_cpp_value(socket, r_value);
return;
}
if (!property_type->is_correct_type(*property)) {
- blender::nodes::socket_cpp_value_get(socket, r_value);
+ socket.typeinfo->get_geometry_nodes_cpp_value(socket, r_value);
return;
}
property_type->init_cpp_value(*property, r_value);
@@ -858,7 +859,7 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree,
{
blender::ResourceScope scope;
blender::LinearAllocator<> &allocator = scope.linear_allocator();
- blender::nodes::MultiFunctionByNode mf_by_node = get_multi_function_per_node(tree, scope);
+ blender::nodes::NodeMultiFunctions mf_by_node{tree, scope};
Map<DOutputSocket, GMutablePointer> group_inputs;
@@ -883,7 +884,7 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree,
/* Initialize remaining group inputs. */
for (const OutputSocketRef *socket : remaining_input_sockets) {
- const CPPType &cpp_type = *blender::nodes::socket_cpp_type_get(*socket->typeinfo());
+ const CPPType &cpp_type = *socket->typeinfo()->get_geometry_nodes_cpp_type();
void *value_in = allocator.allocate(cpp_type.size(), cpp_type.alignment());
initialize_group_input(*nmd, *socket->bsocket(), cpp_type, value_in);
group_inputs.add_new({root_context, socket}, {cpp_type, value_in});
diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
index e652eb8353d..5646e37707c 100644
--- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
+++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
+ * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
@@ -294,7 +294,11 @@ class LockedNode : NonCopyable, NonMovable {
static const CPPType *get_socket_cpp_type(const SocketRef &socket)
{
- const CPPType *type = nodes::socket_cpp_type_get(*socket.typeinfo());
+ const bNodeSocketType *typeinfo = socket.typeinfo();
+ if (typeinfo->get_geometry_nodes_cpp_type == nullptr) {
+ return nullptr;
+ }
+ const CPPType *type = typeinfo->get_geometry_nodes_cpp_type();
if (type == nullptr) {
return nullptr;
}
@@ -310,6 +314,12 @@ static const CPPType *get_socket_cpp_type(const DSocket socket)
return get_socket_cpp_type(*socket.socket_ref());
}
+static void get_socket_value(const SocketRef &socket, void *r_value)
+{
+ const bNodeSocketType *typeinfo = socket.typeinfo();
+ typeinfo->get_geometry_nodes_cpp_value(*socket.bsocket(), r_value);
+}
+
static bool node_supports_laziness(const DNode node)
{
return node->typeinfo()->geometry_node_execute_supports_laziness;
@@ -826,7 +836,7 @@ class GeometryNodesEvaluator {
}
/* Use the multi-function implementation if it exists. */
- const MultiFunction *multi_function = params_.mf_by_node->lookup_default(node, nullptr);
+ const MultiFunction *multi_function = params_.mf_by_node->try_get(node);
if (multi_function != nullptr) {
this->execute_multi_function_node(node, *multi_function, node_state);
return;
@@ -1235,14 +1245,8 @@ class GeometryNodesEvaluator {
void *buffer = allocator.allocate(to_type.size(), to_type.alignment());
GMutablePointer value{to_type, buffer};
- if (conversions_.is_convertible(from_type, to_type)) {
- /* Do the conversion if possible. */
- conversions_.convert_to_uninitialized(from_type, to_type, value_to_forward.get(), buffer);
- }
- else {
- /* Cannot convert, use default value instead. */
- to_type.copy_construct(to_type.default_value(), buffer);
- }
+ this->convert_value(from_type, to_type, value_to_forward.get(), buffer);
+
/* Multi input socket values are logged once all values are available. */
if (!to_socket->is_multi_input_socket()) {
this->log_socket_value({to_socket}, value);
@@ -1363,25 +1367,36 @@ class GeometryNodesEvaluator {
{
LinearAllocator<> &allocator = local_allocators_.local();
- bNodeSocket *bsocket = socket->bsocket();
const CPPType &type = *get_socket_cpp_type(socket);
void *buffer = allocator.allocate(type.size(), type.alignment());
- blender::nodes::socket_cpp_value_get(*bsocket, buffer);
+ get_socket_value(*socket.socket_ref(), buffer);
if (type == required_type) {
return {type, buffer};
}
- if (conversions_.is_convertible(type, required_type)) {
- /* Convert the loaded value to the required type if possible. */
- void *converted_buffer = allocator.allocate(required_type.size(), required_type.alignment());
- conversions_.convert_to_uninitialized(type, required_type, buffer, converted_buffer);
- type.destruct(buffer);
- return {required_type, converted_buffer};
+ void *converted_buffer = allocator.allocate(required_type.size(), required_type.alignment());
+ this->convert_value(type, required_type, buffer, converted_buffer);
+ return {required_type, converted_buffer};
+ }
+
+ void convert_value(const CPPType &from_type,
+ const CPPType &to_type,
+ const void *from_value,
+ void *to_value)
+ {
+ if (from_type == to_type) {
+ from_type.copy_construct(from_value, to_value);
+ return;
+ }
+
+ if (conversions_.is_convertible(from_type, to_type)) {
+ /* Do the conversion if possible. */
+ conversions_.convert_to_uninitialized(from_type, to_type, from_value, to_value);
+ }
+ else {
+ /* Cannot convert, use default value instead. */
+ to_type.copy_construct(to_type.default_value(), to_value);
}
- /* Use a default fallback value when the loaded type is not compatible. */
- void *default_buffer = allocator.allocate(required_type.size(), required_type.alignment());
- required_type.copy_construct(required_type.default_value(), default_buffer);
- return {required_type, default_buffer};
}
NodeState &get_node_state(const DNode node)
diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.hh b/source/blender/modifiers/intern/MOD_nodes_evaluator.hh
index f4ee6242dcb..5151be07aa2 100644
--- a/source/blender/modifiers/intern/MOD_nodes_evaluator.hh
+++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.hh
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
+ * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
@@ -20,12 +20,14 @@
#include "NOD_derived_node_tree.hh"
#include "NOD_geometry_nodes_eval_log.hh"
-#include "NOD_node_tree_multi_function.hh"
+#include "NOD_multi_function.hh"
#include "FN_generic_pointer.hh"
#include "DNA_modifier_types.h"
+#include "FN_multi_function.hh"
+
namespace geo_log = blender::nodes::geometry_nodes_eval_log;
namespace blender::modifiers::geometry_nodes {
@@ -45,7 +47,7 @@ struct GeometryNodesEvaluationParams {
* necessary in all cases. Sometimes `log_socket_value_fn` might just want to look at the value
* and then it can be freed. */
Vector<DSocket> force_compute_sockets;
- nodes::MultiFunctionByNode *mf_by_node;
+ nodes::NodeMultiFunctions *mf_by_node;
const NodesModifierData *modifier_;
Depsgraph *depsgraph;
Object *self_object;
diff --git a/source/blender/modifiers/intern/MOD_normal_edit.c b/source/blender/modifiers/intern/MOD_normal_edit.c
index e7750f0a0d1..1dbdcf87d63 100644
--- a/source/blender/modifiers/intern/MOD_normal_edit.c
+++ b/source/blender/modifiers/intern/MOD_normal_edit.c
@@ -556,15 +556,13 @@ static Mesh *normalEditModifier_do(NormalEditModifierData *enmd,
polynors = CustomData_add_layer(pdata, CD_NORMAL, CD_CALLOC, NULL, num_polys);
CustomData_set_layer_flag(pdata, CD_NORMAL, CD_FLAG_TEMPORARY);
}
- BKE_mesh_calc_normals_poly(mvert,
- NULL,
- num_verts,
- mloop,
- mpoly,
- num_loops,
- num_polys,
- polynors,
- (result->runtime.cd_dirty_vert & CD_MASK_NORMAL) ? false : true);
+ if (result->runtime.cd_dirty_vert & CD_MASK_NORMAL) {
+ BKE_mesh_calc_normals_poly_and_vertex(
+ mvert, num_verts, mloop, num_loops, mpoly, num_polys, polynors, NULL);
+ }
+ else {
+ BKE_mesh_calc_normals_poly(mvert, num_verts, mloop, num_loops, mpoly, num_polys, polynors);
+ }
result->runtime.cd_dirty_vert &= ~CD_MASK_NORMAL;
diff --git a/source/blender/modifiers/intern/MOD_ocean.c b/source/blender/modifiers/intern/MOD_ocean.c
index 8f3206da5be..1c502b94bdb 100644
--- a/source/blender/modifiers/intern/MOD_ocean.c
+++ b/source/blender/modifiers/intern/MOD_ocean.c
@@ -95,8 +95,9 @@ static void initData(ModifierData *md)
BKE_modifier_path_init(omd->cachepath, sizeof(omd->cachepath), "cache_ocean");
omd->ocean = BKE_ocean_add();
- BKE_ocean_init_from_modifier(omd->ocean, omd, omd->viewport_resolution);
- simulate_ocean_modifier(omd);
+ if (BKE_ocean_init_from_modifier(omd->ocean, omd, omd->viewport_resolution)) {
+ simulate_ocean_modifier(omd);
+ }
#else /* WITH_OCEANSIM */
UNUSED_VARS(md);
#endif /* WITH_OCEANSIM */
@@ -132,8 +133,9 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla
tomd->oceancache = NULL;
tomd->ocean = BKE_ocean_add();
- BKE_ocean_init_from_modifier(tomd->ocean, tomd, tomd->viewport_resolution);
- simulate_ocean_modifier(tomd);
+ if (BKE_ocean_init_from_modifier(tomd->ocean, tomd, tomd->viewport_resolution)) {
+ simulate_ocean_modifier(tomd);
+ }
#else /* WITH_OCEANSIM */
/* unused */
(void)md;
@@ -323,6 +325,10 @@ static Mesh *generate_ocean_geometry(OceanModifierData *omd, Mesh *mesh_orig, co
static Mesh *doOcean(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
{
OceanModifierData *omd = (OceanModifierData *)md;
+ if (omd->ocean && !BKE_ocean_is_valid(omd->ocean)) {
+ BKE_modifier_set_error(ctx->object, md, "Failed to allocate memory");
+ return mesh;
+ }
int cfra_scene = (int)DEG_get_ctime(ctx->depsgraph);
Object *ob = ctx->object;
bool allocated_ocean = false;
diff --git a/source/blender/modifiers/intern/MOD_softbody.c b/source/blender/modifiers/intern/MOD_softbody.c
index d7d2f948955..4187f9087a0 100644
--- a/source/blender/modifiers/intern/MOD_softbody.c
+++ b/source/blender/modifiers/intern/MOD_softbody.c
@@ -62,7 +62,9 @@ static void deformVerts(ModifierData *UNUSED(md),
ctx->depsgraph, scene, ctx->object, DEG_get_ctime(ctx->depsgraph), vertexCos, numVerts);
}
-static bool dependsOnTime(ModifierData *UNUSED(md))
+static bool dependsOnTime(struct Scene *UNUSED(scene),
+ ModifierData *UNUSED(md),
+ const int UNUSED(dag_eval_mode))
{
return true;
}
diff --git a/source/blender/modifiers/intern/MOD_solidify_extrude.c b/source/blender/modifiers/intern/MOD_solidify_extrude.c
index e97190b1878..00fa6e24a64 100644
--- a/source/blender/modifiers/intern/MOD_solidify_extrude.c
+++ b/source/blender/modifiers/intern/MOD_solidify_extrude.c
@@ -259,14 +259,12 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
/* calculate only face normals */
poly_nors = MEM_malloc_arrayN(numPolys, sizeof(*poly_nors), __func__);
BKE_mesh_calc_normals_poly(orig_mvert,
- NULL,
(int)numVerts,
orig_mloop,
- orig_mpoly,
(int)numLoops,
+ orig_mpoly,
(int)numPolys,
- poly_nors,
- true);
+ poly_nors);
}
STACK_INIT(new_vert_arr, numVerts * 2);
@@ -507,8 +505,7 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
/* NOTE: copied vertex layers don't have flipped normals yet. do this after applying offset. */
if ((smd->flag & MOD_SOLIDIFY_EVEN) == 0) {
/* no even thickness, very simple */
- float scalar_short;
- float scalar_short_vgroup;
+ float ofs_new_vgroup;
/* for clamping */
float *vert_lens = NULL;
@@ -597,7 +594,7 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
uint i_orig, i_end;
bool do_shell_align;
- scalar_short = scalar_short_vgroup = ofs_new / 32767.0f;
+ ofs_new_vgroup = ofs_new;
INIT_VERT_ARRAY_OFFSETS(false);
@@ -606,36 +603,40 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
if (dvert) {
MDeformVert *dv = &dvert[i];
if (defgrp_invert) {
- scalar_short_vgroup = 1.0f - BKE_defvert_find_weight(dv, defgrp_index);
+ ofs_new_vgroup = 1.0f - BKE_defvert_find_weight(dv, defgrp_index);
}
else {
- scalar_short_vgroup = BKE_defvert_find_weight(dv, defgrp_index);
+ ofs_new_vgroup = BKE_defvert_find_weight(dv, defgrp_index);
}
- scalar_short_vgroup = (offset_fac_vg + (scalar_short_vgroup * offset_fac_vg_inv)) *
- scalar_short;
+ ofs_new_vgroup = (offset_fac_vg + (ofs_new_vgroup * offset_fac_vg_inv)) * ofs_new;
}
if (do_clamp && offset > FLT_EPSILON) {
/* always reset because we may have set before */
if (dvert == NULL) {
- scalar_short_vgroup = scalar_short;
+ ofs_new_vgroup = ofs_new;
}
if (do_angle_clamp) {
float cos_ang = cosf(((2 * M_PI) - vert_angs[i]) * 0.5f);
if (cos_ang > 0) {
float max_off = sqrtf(vert_lens[i]) * 0.5f / cos_ang;
if (max_off < offset * 0.5f) {
- scalar_short_vgroup *= max_off / offset * 2;
+ ofs_new_vgroup *= max_off / offset * 2;
}
}
}
else {
if (vert_lens[i] < offset_sq) {
float scalar = sqrtf(vert_lens[i]) / offset;
- scalar_short_vgroup *= scalar;
+ ofs_new_vgroup *= scalar;
}
}
}
- madd_v3v3short_fl(mv->co, mv->no, scalar_short_vgroup);
+ if (vert_nors) {
+ madd_v3_v3fl(mv->co, vert_nors[i], ofs_new_vgroup);
+ }
+ else {
+ madd_v3v3short_fl(mv->co, mv->no, ofs_new_vgroup / 32767.0f);
+ }
}
}
@@ -643,7 +644,7 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
uint i_orig, i_end;
bool do_shell_align;
- scalar_short = scalar_short_vgroup = ofs_orig / 32767.0f;
+ ofs_new_vgroup = ofs_orig;
/* as above but swapped */
INIT_VERT_ARRAY_OFFSETS(true);
@@ -653,36 +654,40 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
if (dvert) {
MDeformVert *dv = &dvert[i];
if (defgrp_invert) {
- scalar_short_vgroup = 1.0f - BKE_defvert_find_weight(dv, defgrp_index);
+ ofs_new_vgroup = 1.0f - BKE_defvert_find_weight(dv, defgrp_index);
}
else {
- scalar_short_vgroup = BKE_defvert_find_weight(dv, defgrp_index);
+ ofs_new_vgroup = BKE_defvert_find_weight(dv, defgrp_index);
}
- scalar_short_vgroup = (offset_fac_vg + (scalar_short_vgroup * offset_fac_vg_inv)) *
- scalar_short;
+ ofs_new_vgroup = (offset_fac_vg + (ofs_new_vgroup * offset_fac_vg_inv)) * ofs_orig;
}
if (do_clamp && offset > FLT_EPSILON) {
/* always reset because we may have set before */
if (dvert == NULL) {
- scalar_short_vgroup = scalar_short;
+ ofs_new_vgroup = ofs_orig;
}
if (do_angle_clamp) {
float cos_ang = cosf(vert_angs[i_orig] * 0.5f);
if (cos_ang > 0) {
float max_off = sqrtf(vert_lens[i]) * 0.5f / cos_ang;
if (max_off < offset * 0.5f) {
- scalar_short_vgroup *= max_off / offset * 2;
+ ofs_new_vgroup *= max_off / offset * 2;
}
}
}
else {
if (vert_lens[i] < offset_sq) {
float scalar = sqrtf(vert_lens[i]) / offset;
- scalar_short_vgroup *= scalar;
+ ofs_new_vgroup *= scalar;
}
}
}
- madd_v3v3short_fl(mv->co, mv->no, scalar_short_vgroup);
+ if (vert_nors) {
+ madd_v3_v3fl(mv->co, vert_nors[i], ofs_new_vgroup);
+ }
+ else {
+ madd_v3v3short_fl(mv->co, mv->no, ofs_new_vgroup / 32767.0f);
+ }
}
}
diff --git a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c
index b872f04b60f..5b4716a1a43 100644
--- a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c
+++ b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c
@@ -71,6 +71,15 @@ static float angle_signed_on_axis_normalized_v3v3_v3(const float n[3],
return angle;
}
+static float clamp_nonzero(const float value, const float epsilon)
+{
+ BLI_assert(!(epsilon < 0.0f));
+ if (value < 0.0f) {
+ return min_ff(value, -epsilon);
+ }
+ return max_ff(value, epsilon);
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -164,8 +173,8 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
const float ofs_front = (smd->offset_fac + 1.0f) * 0.5f * smd->offset;
const float ofs_back = ofs_front - smd->offset * smd->offset_fac;
- const float ofs_front_clamped = max_ff(1e-5f, fabsf(smd->offset > 0 ? ofs_front : ofs_back));
- const float ofs_back_clamped = max_ff(1e-5f, fabsf(smd->offset > 0 ? ofs_back : ofs_front));
+ const float ofs_front_clamped = clamp_nonzero(smd->offset > 0 ? ofs_front : ofs_back, 1e-5f);
+ const float ofs_back_clamped = clamp_nonzero(smd->offset > 0 ? ofs_back : ofs_front, 1e-5f);
const float offset_fac_vg = smd->offset_fac_vg;
const float offset_fac_vg_inv = 1.0f - smd->offset_fac_vg;
const float offset = fabsf(smd->offset) * smd->offset_clamp;
@@ -202,15 +211,8 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
/* Calculate only face normals. */
poly_nors = MEM_malloc_arrayN(numPolys, sizeof(*poly_nors), __func__);
- BKE_mesh_calc_normals_poly(orig_mvert,
- NULL,
- (int)numVerts,
- orig_mloop,
- orig_mpoly,
- (int)numLoops,
- (int)numPolys,
- poly_nors,
- true);
+ BKE_mesh_calc_normals_poly(
+ orig_mvert, (int)numVerts, orig_mloop, (int)numLoops, orig_mpoly, (int)numPolys, poly_nors);
NewFaceRef *face_sides_arr = MEM_malloc_arrayN(
numPolys * 2, sizeof(*face_sides_arr), "face_sides_arr in solidify");
diff --git a/source/blender/modifiers/intern/MOD_subsurf.c b/source/blender/modifiers/intern/MOD_subsurf.c
index ce427281db3..db0b769684e 100644
--- a/source/blender/modifiers/intern/MOD_subsurf.c
+++ b/source/blender/modifiers/intern/MOD_subsurf.c
@@ -358,13 +358,8 @@ static bool get_show_adaptive_options(const bContext *C, Panel *panel)
/* Don't show adaptive options if the cycles experimental feature set is disabled. */
Scene *scene = CTX_data_scene(C);
- PointerRNA scene_ptr;
- RNA_id_pointer_create(&scene->id, &scene_ptr);
- if (BKE_scene_uses_cycles(scene)) {
- PointerRNA cycles_ptr = RNA_pointer_get(&scene_ptr, "cycles");
- if (RNA_enum_get(&cycles_ptr, "feature_set") != 1) { /* EXPERIMENTAL */
- return false;
- }
+ if (!BKE_scene_uses_cycles_experimental_features(scene)) {
+ return false;
}
return true;
diff --git a/source/blender/modifiers/intern/MOD_surface.c b/source/blender/modifiers/intern/MOD_surface.c
index bfd4cd81803..3f2d0a06db8 100644
--- a/source/blender/modifiers/intern/MOD_surface.c
+++ b/source/blender/modifiers/intern/MOD_surface.c
@@ -98,7 +98,9 @@ static void freeData(ModifierData *md)
}
}
-static bool dependsOnTime(ModifierData *UNUSED(md))
+static bool dependsOnTime(struct Scene *UNUSED(scene),
+ ModifierData *UNUSED(md),
+ const int UNUSED(dag_eval_mode))
{
return true;
}
diff --git a/source/blender/modifiers/intern/MOD_volume_displace.cc b/source/blender/modifiers/intern/MOD_volume_displace.cc
index af4b31d6bfc..fcf75040a9a 100644
--- a/source/blender/modifiers/intern/MOD_volume_displace.cc
+++ b/source/blender/modifiers/intern/MOD_volume_displace.cc
@@ -95,7 +95,9 @@ static void foreachTexLink(ModifierData *md, Object *ob, TexWalkFunc walk, void
walk(userData, ob, md, "texture");
}
-static bool dependsOnTime(ModifierData *md)
+static bool dependsOnTime(struct Scene *UNUSED(scene),
+ ModifierData *md,
+ const int UNUSED(dag_eval_mode))
{
VolumeDisplaceModifierData *vdmd = reinterpret_cast<VolumeDisplaceModifierData *>(md);
if (vdmd->texture) {
diff --git a/source/blender/modifiers/intern/MOD_warp.c b/source/blender/modifiers/intern/MOD_warp.c
index 3bebc52c503..25e33b22bde 100644
--- a/source/blender/modifiers/intern/MOD_warp.c
+++ b/source/blender/modifiers/intern/MOD_warp.c
@@ -116,7 +116,9 @@ static void matrix_from_obj_pchan(float mat[4][4],
}
}
-static bool dependsOnTime(ModifierData *md)
+static bool dependsOnTime(struct Scene *UNUSED(scene),
+ ModifierData *md,
+ const int UNUSED(dag_eval_mode))
{
WarpModifierData *wmd = (WarpModifierData *)md;
diff --git a/source/blender/modifiers/intern/MOD_wave.c b/source/blender/modifiers/intern/MOD_wave.c
index cf4c195c66d..03f8e3a1dfb 100644
--- a/source/blender/modifiers/intern/MOD_wave.c
+++ b/source/blender/modifiers/intern/MOD_wave.c
@@ -70,7 +70,9 @@ static void initData(ModifierData *md)
MEMCPY_STRUCT_AFTER(wmd, DNA_struct_default_get(WaveModifierData), modifier);
}
-static bool dependsOnTime(ModifierData *UNUSED(md))
+static bool dependsOnTime(struct Scene *UNUSED(scene),
+ ModifierData *UNUSED(md),
+ const int UNUSED(dag_eval_mode))
{
return true;
}
diff --git a/source/blender/modifiers/intern/MOD_weighted_normal.c b/source/blender/modifiers/intern/MOD_weighted_normal.c
index 3b147c69716..1ee64b935b7 100644
--- a/source/blender/modifiers/intern/MOD_weighted_normal.c
+++ b/source/blender/modifiers/intern/MOD_weighted_normal.c
@@ -615,8 +615,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
polynors = CustomData_add_layer(pdata, CD_NORMAL, CD_CALLOC, NULL, numPolys);
CustomData_set_layer_flag(pdata, CD_NORMAL, CD_FLAG_TEMPORARY);
}
- BKE_mesh_calc_normals_poly(
- mvert, NULL, numVerts, mloop, mpoly, numLoops, numPolys, polynors, false);
+ BKE_mesh_calc_normals_poly_and_vertex(
+ mvert, numVerts, mloop, numLoops, mpoly, numPolys, polynors, NULL);
const float split_angle = mesh->smoothresh;
short(*clnors)[2];
diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.c b/source/blender/modifiers/intern/MOD_weightvgedit.c
index 093fa118ee0..a9d01c64ff1 100644
--- a/source/blender/modifiers/intern/MOD_weightvgedit.c
+++ b/source/blender/modifiers/intern/MOD_weightvgedit.c
@@ -112,7 +112,9 @@ static void requiredDataMask(Object *UNUSED(ob),
/* No need to ask for CD_PREVIEW_MLOOPCOL... */
}
-static bool dependsOnTime(ModifierData *md)
+static bool dependsOnTime(struct Scene *UNUSED(scene),
+ ModifierData *md,
+ const int UNUSED(dag_eval_mode))
{
WeightVGEditModifierData *wmd = (WeightVGEditModifierData *)md;
diff --git a/source/blender/modifiers/intern/MOD_weightvgmix.c b/source/blender/modifiers/intern/MOD_weightvgmix.c
index 7aae089fa18..b369b82ebb7 100644
--- a/source/blender/modifiers/intern/MOD_weightvgmix.c
+++ b/source/blender/modifiers/intern/MOD_weightvgmix.c
@@ -154,7 +154,9 @@ static void requiredDataMask(Object *UNUSED(ob),
/* No need to ask for CD_PREVIEW_MLOOPCOL... */
}
-static bool dependsOnTime(ModifierData *md)
+static bool dependsOnTime(struct Scene *UNUSED(scene),
+ ModifierData *md,
+ const int UNUSED(dag_eval_mode))
{
WeightVGMixModifierData *wmd = (WeightVGMixModifierData *)md;
diff --git a/source/blender/modifiers/intern/MOD_weightvgproximity.c b/source/blender/modifiers/intern/MOD_weightvgproximity.c
index 6e78774269a..7ee19e1c537 100644
--- a/source/blender/modifiers/intern/MOD_weightvgproximity.c
+++ b/source/blender/modifiers/intern/MOD_weightvgproximity.c
@@ -362,7 +362,9 @@ static void requiredDataMask(Object *UNUSED(ob),
/* No need to ask for CD_PREVIEW_MLOOPCOL... */
}
-static bool dependsOnTime(ModifierData *md)
+static bool dependsOnTime(struct Scene *UNUSED(scene),
+ ModifierData *md,
+ const int UNUSED(dag_eval_mode))
{
WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData *)md;
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index 36e5be6a292..8680fcee49a 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -175,7 +175,9 @@ set(SRC
geometry/nodes/node_geo_curve_primitive_star.cc
geometry/nodes/node_geo_curve_resample.cc
geometry/nodes/node_geo_curve_reverse.cc
+ geometry/nodes/node_geo_curve_select_by_handle_type.cc
geometry/nodes/node_geo_curve_set_handles.cc
+ geometry/nodes/node_geo_curve_spline_type.cc
geometry/nodes/node_geo_curve_subdivide.cc
geometry/nodes/node_geo_curve_to_mesh.cc
geometry/nodes/node_geo_curve_to_points.cc
@@ -343,11 +345,10 @@ set(SRC
intern/node_common.c
intern/node_exec.cc
intern/node_geometry_exec.cc
+ intern/node_multi_function.cc
intern/node_socket.cc
- intern/node_tree_multi_function.cc
intern/node_tree_ref.cc
intern/node_util.c
- intern/type_callbacks.cc
intern/type_conversions.cc
composite/node_composite_util.h
@@ -364,13 +365,12 @@ set(SRC
NOD_geometry_exec.hh
NOD_geometry_nodes_eval_log.hh
NOD_math_functions.hh
- NOD_node_tree_multi_function.hh
+ NOD_multi_function.hh
NOD_node_tree_ref.hh
NOD_shader.h
NOD_socket.h
NOD_static_types.h
NOD_texture.h
- NOD_type_callbacks.hh
NOD_type_conversions.hh
intern/node_common.h
intern/node_exec.h
diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h
index 868fcbb33af..856d787c8d0 100644
--- a/source/blender/nodes/NOD_geometry.h
+++ b/source/blender/nodes/NOD_geometry.h
@@ -63,6 +63,7 @@ void register_node_type_geo_curve_primitive_star(void);
void register_node_type_geo_curve_resample(void);
void register_node_type_geo_curve_reverse(void);
void register_node_type_geo_curve_set_handles(void);
+void register_node_type_geo_curve_spline_type(void);
void register_node_type_geo_curve_subdivide(void);
void register_node_type_geo_curve_to_mesh(void);
void register_node_type_geo_curve_to_points(void);
@@ -94,6 +95,7 @@ void register_node_type_geo_point_translate(void);
void register_node_type_geo_points_to_volume(void);
void register_node_type_geo_raycast(void);
void register_node_type_geo_sample_texture(void);
+void register_node_type_geo_select_by_handle_type(void);
void register_node_type_geo_select_by_material(void);
void register_node_type_geo_separate_components(void);
void register_node_type_geo_subdivision_surface(void);
diff --git a/source/blender/nodes/NOD_multi_function.hh b/source/blender/nodes/NOD_multi_function.hh
new file mode 100644
index 00000000000..2f4b104fb4c
--- /dev/null
+++ b/source/blender/nodes/NOD_multi_function.hh
@@ -0,0 +1,130 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include "FN_multi_function.hh"
+
+#include "DNA_node_types.h"
+
+#include "NOD_derived_node_tree.hh"
+
+namespace blender::nodes {
+
+using namespace fn::multi_function_types;
+
+class NodeMultiFunctions;
+
+/**
+ * Utility class to help nodes build a multi-function for themselves.
+ */
+class NodeMultiFunctionBuilder : NonCopyable, NonMovable {
+ private:
+ ResourceScope &resource_scope_;
+ bNode &node_;
+ bNodeTree &tree_;
+ const MultiFunction *built_fn_ = nullptr;
+
+ friend NodeMultiFunctions;
+
+ public:
+ NodeMultiFunctionBuilder(ResourceScope &resource_scope, bNode &node, bNodeTree &tree);
+
+ /**
+ * Assign a multi-function for the current node. The input and output parameters of the function
+ * have to match the available sockets in the node.
+ */
+ void set_matching_fn(const MultiFunction *fn);
+ void set_matching_fn(const MultiFunction &fn);
+
+ /**
+ * Utility method for creating and assigning a multi-function when it can't have a static
+ * lifetime.
+ */
+ template<typename T, typename... Args> void construct_and_set_matching_fn(Args &&...args);
+
+ bNode &node();
+ bNodeTree &tree();
+
+ ResourceScope &resource_scope();
+};
+
+/**
+ * Gives access to multi-functions for all nodes in a node tree that support them.
+ */
+class NodeMultiFunctions {
+ private:
+ Map<const bNode *, const MultiFunction *> map_;
+
+ public:
+ NodeMultiFunctions(const DerivedNodeTree &tree, ResourceScope &resource_scope);
+
+ const MultiFunction *try_get(const DNode &node) const;
+};
+
+/* --------------------------------------------------------------------
+ * NodeMultiFunctionBuilder inline methods.
+ */
+
+inline NodeMultiFunctionBuilder::NodeMultiFunctionBuilder(ResourceScope &resource_scope,
+ bNode &node,
+ bNodeTree &tree)
+ : resource_scope_(resource_scope), node_(node), tree_(tree)
+{
+}
+
+inline bNode &NodeMultiFunctionBuilder::node()
+{
+ return node_;
+}
+
+inline bNodeTree &NodeMultiFunctionBuilder::tree()
+{
+ return tree_;
+}
+
+inline ResourceScope &NodeMultiFunctionBuilder::resource_scope()
+{
+ return resource_scope_;
+}
+
+inline void NodeMultiFunctionBuilder::set_matching_fn(const MultiFunction *fn)
+{
+ built_fn_ = fn;
+}
+
+inline void NodeMultiFunctionBuilder::set_matching_fn(const MultiFunction &fn)
+{
+ this->set_matching_fn(&fn);
+}
+
+template<typename T, typename... Args>
+inline void NodeMultiFunctionBuilder::construct_and_set_matching_fn(Args &&...args)
+{
+ const T &fn = resource_scope_.construct<T>(__func__, std::forward<Args>(args)...);
+ this->set_matching_fn(&fn);
+}
+
+/* --------------------------------------------------------------------
+ * NodeMultiFunctions inline methods.
+ */
+
+inline const MultiFunction *NodeMultiFunctions::try_get(const DNode &node) const
+{
+ return map_.lookup_default(node->bnode(), nullptr);
+}
+
+} // namespace blender::nodes
diff --git a/source/blender/nodes/NOD_node_tree_multi_function.hh b/source/blender/nodes/NOD_node_tree_multi_function.hh
deleted file mode 100644
index 7eeeaef0b98..00000000000
--- a/source/blender/nodes/NOD_node_tree_multi_function.hh
+++ /dev/null
@@ -1,390 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#pragma once
-
-/** \file
- * \ingroup nodes
- *
- * This file allows you to generate a multi-function network from a user-generated node tree.
- */
-
-#include "FN_multi_function_builder.hh"
-#include "FN_multi_function_network.hh"
-
-#include "NOD_derived_node_tree.hh"
-#include "NOD_type_callbacks.hh"
-
-#include "BLI_multi_value_map.hh"
-#include "BLI_resource_scope.hh"
-
-namespace blender::nodes {
-
-/**
- * A MFNetworkTreeMap maps various components of a node tree to components of a fn::MFNetwork. This
- * is necessary for further processing of a multi-function network that has been generated from a
- * node tree.
- */
-class MFNetworkTreeMap {
- private:
- /**
- * Store by id instead of using a hash table to avoid unnecessary hash table lookups.
- *
- * Input sockets in a node tree can have multiple corresponding sockets in the generated
- * MFNetwork. This is because nodes are allowed to expand into multiple multi-function nodes.
- */
- const DerivedNodeTree &tree_;
- fn::MFNetwork &network_;
- MultiValueMap<DSocket, fn::MFSocket *> sockets_by_dsocket_;
-
- public:
- MFNetworkTreeMap(const DerivedNodeTree &tree, fn::MFNetwork &network)
- : tree_(tree), network_(network)
- {
- }
-
- const DerivedNodeTree &tree() const
- {
- return tree_;
- }
-
- const fn::MFNetwork &network() const
- {
- return network_;
- }
-
- fn::MFNetwork &network()
- {
- return network_;
- }
-
- void add(const DSocket &dsocket, fn::MFSocket &socket)
- {
- BLI_assert(dsocket->is_input() == socket.is_input());
- BLI_assert(dsocket->is_input() || sockets_by_dsocket_.lookup(dsocket).is_empty());
- sockets_by_dsocket_.add(dsocket, &socket);
- }
-
- void add(const DInputSocket &dsocket, fn::MFInputSocket &socket)
- {
- sockets_by_dsocket_.add(dsocket, &socket);
- }
-
- void add(const DOutputSocket &dsocket, fn::MFOutputSocket &socket)
- {
- /* There can be at most one matching output socket. */
- BLI_assert(sockets_by_dsocket_.lookup(dsocket).is_empty());
- sockets_by_dsocket_.add(dsocket, &socket);
- }
-
- void add(const DTreeContext &context,
- Span<const InputSocketRef *> dsockets,
- Span<fn::MFInputSocket *> sockets)
- {
- assert_same_size(dsockets, sockets);
- for (int i : dsockets.index_range()) {
- this->add(DInputSocket(&context, dsockets[i]), *sockets[i]);
- }
- }
-
- void add(const DTreeContext &context,
- Span<const OutputSocketRef *> dsockets,
- Span<fn::MFOutputSocket *> sockets)
- {
- assert_same_size(dsockets, sockets);
- for (int i : dsockets.index_range()) {
- this->add(DOutputSocket(&context, dsockets[i]), *sockets[i]);
- }
- }
-
- void add_try_match(const DNode &dnode, fn::MFNode &node)
- {
- this->add_try_match(*dnode.context(),
- dnode->inputs().cast<const SocketRef *>(),
- node.inputs().cast<fn::MFSocket *>());
- this->add_try_match(*dnode.context(),
- dnode->outputs().cast<const SocketRef *>(),
- node.outputs().cast<fn::MFSocket *>());
- }
-
- void add_try_match(const DTreeContext &context,
- Span<const InputSocketRef *> dsockets,
- Span<fn::MFInputSocket *> sockets)
- {
- this->add_try_match(
- context, dsockets.cast<const SocketRef *>(), sockets.cast<fn::MFSocket *>());
- }
-
- void add_try_match(const DTreeContext &context,
- Span<const OutputSocketRef *> dsockets,
- Span<fn::MFOutputSocket *> sockets)
- {
- this->add_try_match(
- context, dsockets.cast<const SocketRef *>(), sockets.cast<fn::MFSocket *>());
- }
-
- void add_try_match(const DTreeContext &context,
- Span<const SocketRef *> dsockets,
- Span<fn::MFSocket *> sockets)
- {
- int used_sockets = 0;
- for (const SocketRef *dsocket : dsockets) {
- if (!dsocket->is_available()) {
- continue;
- }
- if (!socket_is_mf_data_socket(*dsocket->typeinfo())) {
- continue;
- }
- fn::MFSocket *socket = sockets[used_sockets];
- this->add(DSocket(&context, dsocket), *socket);
- used_sockets++;
- }
- }
-
- fn::MFOutputSocket &lookup(const DOutputSocket &dsocket)
- {
- return sockets_by_dsocket_.lookup(dsocket)[0]->as_output();
- }
-
- Span<fn::MFInputSocket *> lookup(const DInputSocket &dsocket)
- {
- return sockets_by_dsocket_.lookup(dsocket).cast<fn::MFInputSocket *>();
- }
-
- fn::MFInputSocket &lookup_dummy(const DInputSocket &dsocket)
- {
- Span<fn::MFInputSocket *> sockets = this->lookup(dsocket);
- BLI_assert(sockets.size() == 1);
- fn::MFInputSocket &socket = *sockets[0];
- BLI_assert(socket.node().is_dummy());
- return socket;
- }
-
- fn::MFOutputSocket &lookup_dummy(const DOutputSocket &dsocket)
- {
- fn::MFOutputSocket &socket = this->lookup(dsocket);
- BLI_assert(socket.node().is_dummy());
- return socket;
- }
-
- bool is_mapped(const DSocket &dsocket) const
- {
- return !sockets_by_dsocket_.lookup(dsocket).is_empty();
- }
-};
-
-/**
- * This data is necessary throughout the generation of a MFNetwork from a node tree.
- */
-struct CommonMFNetworkBuilderData {
- ResourceScope &scope;
- fn::MFNetwork &network;
- MFNetworkTreeMap &network_map;
- const DerivedNodeTree &tree;
-};
-
-class MFNetworkBuilderBase {
- protected:
- CommonMFNetworkBuilderData &common_;
-
- public:
- MFNetworkBuilderBase(CommonMFNetworkBuilderData &common) : common_(common)
- {
- }
-
- /**
- * Returns the network that is currently being built.
- */
- fn::MFNetwork &network()
- {
- return common_.network;
- }
-
- /**
- * Returns the map between the node tree and the multi-function network that is being built.
- */
- MFNetworkTreeMap &network_map()
- {
- return common_.network_map;
- }
-
- /**
- * Returns a resource collector that will only be destructed after the multi-function network is
- * destructed.
- */
- ResourceScope &resource_scope()
- {
- return common_.scope;
- }
-
- /**
- * Constructs a new function that will live at least as long as the MFNetwork.
- */
- template<typename T, typename... Args> T &construct_fn(Args &&...args)
- {
- BLI_STATIC_ASSERT((std::is_base_of_v<fn::MultiFunction, T>), "");
- void *buffer = common_.scope.linear_allocator().allocate(sizeof(T), alignof(T));
- T *fn = new (buffer) T(std::forward<Args>(args)...);
- common_.scope.add(destruct_ptr<T>(fn), fn->name().c_str());
- return *fn;
- }
-};
-
-/**
- * This class is used by socket implementations to define how an unlinked input socket is handled
- * in a multi-function network.
- */
-class SocketMFNetworkBuilder : public MFNetworkBuilderBase {
- private:
- bNodeSocket *bsocket_;
- fn::MFOutputSocket *built_socket_ = nullptr;
-
- public:
- SocketMFNetworkBuilder(CommonMFNetworkBuilderData &common, const DSocket &dsocket)
- : MFNetworkBuilderBase(common), bsocket_(dsocket->bsocket())
- {
- }
-
- /**
- * Returns the socket that is currently being built.
- */
- bNodeSocket &bsocket()
- {
- return *bsocket_;
- }
-
- /**
- * Utility method that returns bsocket->default_value for the current socket.
- */
- template<typename T> T *socket_default_value()
- {
- return static_cast<T *>(bsocket_->default_value);
- }
-
- /**
- * Builds a function node for that socket that outputs the given constant value.
- */
- template<typename T> void set_constant_value(T value)
- {
- this->construct_generator_fn<fn::CustomMF_Constant<T>>(std::move(value));
- }
- void set_constant_value(const CPPType &type, const void *value)
- {
- /* The value has live as long as the generated mf network. */
- this->construct_generator_fn<fn::CustomMF_GenericConstant>(type, value);
- }
-
- template<typename T, typename... Args> void construct_generator_fn(Args &&...args)
- {
- const fn::MultiFunction &fn = this->construct_fn<T>(std::forward<Args>(args)...);
- this->set_generator_fn(fn);
- }
-
- /**
- * Uses the first output of the given multi-function as value of the socket.
- */
- void set_generator_fn(const fn::MultiFunction &fn)
- {
- fn::MFFunctionNode &node = common_.network.add_function(fn);
- this->set_socket(node.output(0));
- }
-
- /**
- * Define a multi-function socket that outputs the value of the bsocket.
- */
- void set_socket(fn::MFOutputSocket &socket)
- {
- built_socket_ = &socket;
- }
-
- fn::MFOutputSocket *built_socket()
- {
- return built_socket_;
- }
-};
-
-/**
- * This class is used by node implementations to define how a user-level node expands into
- * multi-function nodes internally.
- */
-class NodeMFNetworkBuilder : public MFNetworkBuilderBase {
- private:
- DNode dnode_;
-
- public:
- NodeMFNetworkBuilder(CommonMFNetworkBuilderData &common, DNode dnode)
- : MFNetworkBuilderBase(common), dnode_(dnode)
- {
- }
-
- /**
- * Tells the builder to build a function that corresponds to the node that is being built. It
- * will try to match up sockets.
- */
- template<typename T, typename... Args> T &construct_and_set_matching_fn(Args &&...args)
- {
- T &function = this->construct_fn<T>(std::forward<Args>(args)...);
- this->set_matching_fn(function);
- return function;
- }
-
- const fn::MultiFunction &get_not_implemented_fn()
- {
- return this->get_default_fn("Not Implemented (" + dnode_->name() + ")");
- }
-
- const fn::MultiFunction &get_default_fn(StringRef name);
-
- const void set_not_implemented()
- {
- this->set_matching_fn(this->get_not_implemented_fn());
- }
-
- /**
- * Tells the builder that the given function corresponds to the node that is being built. It will
- * try to match up sockets. For that it skips unavailable and non-data sockets.
- */
- void set_matching_fn(const fn::MultiFunction &function)
- {
- fn::MFFunctionNode &node = common_.network.add_function(function);
- common_.network_map.add_try_match(dnode_, node);
- }
-
- /**
- * Returns the node that is currently being built.
- */
- bNode &bnode()
- {
- return *dnode_->bnode();
- }
-
- /**
- * Returns the node that is currently being built.
- */
- const DNode &dnode() const
- {
- return dnode_;
- }
-};
-
-MFNetworkTreeMap insert_node_tree_into_mf_network(fn::MFNetwork &network,
- const DerivedNodeTree &tree,
- ResourceScope &scope);
-
-using MultiFunctionByNode = Map<DNode, const fn::MultiFunction *>;
-MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree, ResourceScope &scope);
-
-} // namespace blender::nodes
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index a091f28f3a0..4da8648173d 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -61,7 +61,7 @@ DefNode(ShaderNode, SH_NODE_COMBRGB, 0, "COMBRG
DefNode(ShaderNode, SH_NODE_HUE_SAT, 0, "HUE_SAT", HueSaturation, "Hue/Saturation", "" )
DefNode(ShaderNode, SH_NODE_OUTPUT_MATERIAL, def_sh_output, "OUTPUT_MATERIAL", OutputMaterial, "Material Output", "" )
-DefNode(ShaderNode, SH_NODE_EEVEE_SPECULAR, 0, "EEVEE_SPECULAR", EeveeSpecular, "Specular", "" )
+DefNode(ShaderNode, SH_NODE_EEVEE_SPECULAR, 0, "EEVEE_SPECULAR", EeveeSpecular, "Specular BSDF", "" )
DefNode(ShaderNode, SH_NODE_OUTPUT_LIGHT, def_sh_output, "OUTPUT_LIGHT", OutputLight, "Light Output", "" )
DefNode(ShaderNode, SH_NODE_OUTPUT_WORLD, def_sh_output, "OUTPUT_WORLD", OutputWorld, "World Output", "" )
DefNode(ShaderNode, SH_NODE_OUTPUT_LINESTYLE, def_sh_output_linestyle,"OUTPUT_LINESTYLE", OutputLineStyle, "Line Style Output", "" )
@@ -303,6 +303,8 @@ DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR",
DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "")
DefNode(GeometryNode, GEO_NODE_CURVE_REVERSE, 0, "CURVE_REVERSE", CurveReverse, "Curve Reverse", "")
DefNode(GeometryNode, GEO_NODE_CURVE_SET_HANDLES, def_geo_curve_set_handles, "CURVE_SET_HANDLES", CurveSetHandles, "Set Handle Type", "")
+DefNode(GeometryNode, GEO_NODE_CURVE_SELECT_HANDLES, def_geo_curve_select_handles, "CURVE_SELECT_HANDLES", CurveSelectHandles, "Select by Handle Type", "")
+DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "CURVE_SPLINE_TYPE", CurveSplineType, "Set Spline Type", "")
DefNode(GeometryNode, GEO_NODE_CURVE_SUBDIVIDE, def_geo_curve_subdivide, "CURVE_SUBDIVIDE", CurveSubdivide, "Curve Subdivide", "")
DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "")
DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "")
@@ -335,7 +337,7 @@ DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POIN
DefNode(GeometryNode, GEO_NODE_RAYCAST, def_geo_raycast, "RAYCAST", Raycast, "Raycast", "")
DefNode(GeometryNode, GEO_NODE_SELECT_BY_MATERIAL, 0, "SELECT_BY_MATERIAL", SelectByMaterial, "Select by Material", "")
DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "")
-DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, 0, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "")
+DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "")
DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "")
DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "")
DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "")
diff --git a/source/blender/nodes/composite/nodes/node_composite_antialiasing.c b/source/blender/nodes/composite/nodes/node_composite_antialiasing.c
index 81e2408fcf9..a5906c31093 100644
--- a/source/blender/nodes/composite/nodes/node_composite_antialiasing.c
+++ b/source/blender/nodes/composite/nodes/node_composite_antialiasing.c
@@ -1,6 +1,4 @@
/*
- * ***** BEGIN GPL LICENSE BLOCK *****
- *
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
@@ -19,10 +17,6 @@
* All rights reserved.
*
* The Original Code is: all of this file.
- *
- * Contributor(s): IRIE Shinsuke
- *
- * ***** END GPL LICENSE BLOCK *****
*/
/** \file
diff --git a/source/blender/nodes/composite/nodes/node_composite_switchview.c b/source/blender/nodes/composite/nodes/node_composite_switchview.c
index 3ffad8216de..ec5c79cc087 100644
--- a/source/blender/nodes/composite/nodes/node_composite_switchview.c
+++ b/source/blender/nodes/composite/nodes/node_composite_switchview.c
@@ -132,7 +132,7 @@ static void init_switch_view(const bContext *C, PointerRNA *ptr)
for (nr = 0, srv = rd->views.first; srv; srv = srv->next, nr++) {
sock = ntreeCompositSwitchViewAddSocket(ntree, node, srv->name);
- if ((srv->viewflag & SCE_VIEW_DISABLE)) {
+ if (srv->viewflag & SCE_VIEW_DISABLE) {
sock->flag |= SOCK_HIDDEN;
}
}
diff --git a/source/blender/nodes/function/node_function_util.hh b/source/blender/nodes/function/node_function_util.hh
index 9fbd6712827..96a8f29c3e9 100644
--- a/source/blender/nodes/function/node_function_util.hh
+++ b/source/blender/nodes/function/node_function_util.hh
@@ -30,7 +30,7 @@
#include "BLT_translation.h"
#include "NOD_function.h"
-#include "NOD_node_tree_multi_function.hh"
+#include "NOD_multi_function.hh"
#include "node_util.h"
diff --git a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc
index 7a83ff8e016..0ba9080918c 100644
--- a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc
+++ b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc
@@ -58,7 +58,7 @@ static void node_boolean_math_label(bNodeTree *UNUSED(ntree), bNode *node, char
BLI_strncpy(label, IFACE_(name), maxlen);
}
-static const blender::fn::MultiFunction &get_multi_function(bNode &bnode)
+static const blender::fn::MultiFunction *get_multi_function(bNode &bnode)
{
static blender::fn::CustomMF_SI_SI_SO<bool, bool, bool> and_fn{
"And", [](bool a, bool b) { return a && b; }};
@@ -68,20 +68,21 @@ static const blender::fn::MultiFunction &get_multi_function(bNode &bnode)
switch (bnode.custom1) {
case NODE_BOOLEAN_MATH_AND:
- return and_fn;
+ return &and_fn;
case NODE_BOOLEAN_MATH_OR:
- return or_fn;
+ return &or_fn;
case NODE_BOOLEAN_MATH_NOT:
- return not_fn;
+ return &not_fn;
}
- BLI_assert(false);
- return blender::fn::dummy_multi_function;
+ BLI_assert_unreachable();
+ return nullptr;
}
-static void node_boolean_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+static void fn_node_boolean_math_build_multi_function(
+ blender::nodes::NodeMultiFunctionBuilder &builder)
{
- const blender::fn::MultiFunction &fn = get_multi_function(builder.bnode());
+ const blender::fn::MultiFunction *fn = get_multi_function(builder.node());
builder.set_matching_fn(fn);
}
@@ -93,7 +94,7 @@ void register_node_type_fn_boolean_math()
node_type_socket_templates(&ntype, fn_node_boolean_math_in, fn_node_boolean_math_out);
node_type_label(&ntype, node_boolean_math_label);
node_type_update(&ntype, node_boolean_math_update);
- ntype.expand_in_mf_network = node_boolean_expand_in_mf_network;
+ ntype.build_multi_function = fn_node_boolean_math_build_multi_function;
ntype.draw_buttons = fn_node_boolean_math_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/function/nodes/node_fn_float_compare.cc b/source/blender/nodes/function/nodes/node_fn_float_compare.cc
index 6c8df8f2ea0..16ffb761a15 100644
--- a/source/blender/nodes/function/nodes/node_fn_float_compare.cc
+++ b/source/blender/nodes/function/nodes/node_fn_float_compare.cc
@@ -64,7 +64,7 @@ static void node_float_compare_label(bNodeTree *UNUSED(ntree),
BLI_strncpy(label, IFACE_(name), maxlen);
}
-static const blender::fn::MultiFunction &get_multi_function(bNode &node)
+static const blender::fn::MultiFunction *get_multi_function(bNode &node)
{
static blender::fn::CustomMF_SI_SI_SO<float, float, bool> less_than_fn{
"Less Than", [](float a, float b) { return a < b; }};
@@ -81,26 +81,27 @@ static const blender::fn::MultiFunction &get_multi_function(bNode &node)
switch (node.custom1) {
case NODE_FLOAT_COMPARE_LESS_THAN:
- return less_than_fn;
+ return &less_than_fn;
case NODE_FLOAT_COMPARE_LESS_EQUAL:
- return less_equal_fn;
+ return &less_equal_fn;
case NODE_FLOAT_COMPARE_GREATER_THAN:
- return greater_than_fn;
+ return &greater_than_fn;
case NODE_FLOAT_COMPARE_GREATER_EQUAL:
- return greater_equal_fn;
+ return &greater_equal_fn;
case NODE_FLOAT_COMPARE_EQUAL:
- return equal_fn;
+ return &equal_fn;
case NODE_FLOAT_COMPARE_NOT_EQUAL:
- return not_equal_fn;
+ return &not_equal_fn;
}
- BLI_assert(false);
- return blender::fn::dummy_multi_function;
+ BLI_assert_unreachable();
+ return nullptr;
}
-static void node_float_compare_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+static void fn_node_float_compare_build_multi_function(
+ blender::nodes::NodeMultiFunctionBuilder &builder)
{
- const blender::fn::MultiFunction &fn = get_multi_function(builder.bnode());
+ const blender::fn::MultiFunction *fn = get_multi_function(builder.node());
builder.set_matching_fn(fn);
}
@@ -112,7 +113,7 @@ void register_node_type_fn_float_compare()
node_type_socket_templates(&ntype, fn_node_float_compare_in, fn_node_float_compare_out);
node_type_label(&ntype, node_float_compare_label);
node_type_update(&ntype, node_float_compare_update);
- ntype.expand_in_mf_network = node_float_compare_expand_in_mf_network;
+ ntype.build_multi_function = fn_node_float_compare_build_multi_function;
ntype.draw_buttons = geo_node_float_compare_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc
index 26cde576400..52acfefe615 100644
--- a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc
+++ b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc
@@ -50,7 +50,7 @@ static void node_float_to_int_label(bNodeTree *UNUSED(ntree), bNode *node, char
BLI_strncpy(label, IFACE_(name), maxlen);
}
-static const blender::fn::MultiFunction &get_multi_function(bNode &bnode)
+static const blender::fn::MultiFunction *get_multi_function(bNode &bnode)
{
static blender::fn::CustomMF_SI_SO<float, int> round_fn{"Round",
[](float a) { return (int)round(a); }};
@@ -63,22 +63,23 @@ static const blender::fn::MultiFunction &get_multi_function(bNode &bnode)
switch (static_cast<FloatToIntRoundingMode>(bnode.custom1)) {
case FN_NODE_FLOAT_TO_INT_ROUND:
- return round_fn;
+ return &round_fn;
case FN_NODE_FLOAT_TO_INT_FLOOR:
- return floor_fn;
+ return &floor_fn;
case FN_NODE_FLOAT_TO_INT_CEIL:
- return ceil_fn;
+ return &ceil_fn;
case FN_NODE_FLOAT_TO_INT_TRUNCATE:
- return trunc_fn;
+ return &trunc_fn;
}
BLI_assert_unreachable();
- return blender::fn::dummy_multi_function;
+ return nullptr;
}
-static void node_float_to_int_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+static void fn_node_float_to_int_build_multi_function(
+ blender::nodes::NodeMultiFunctionBuilder &builder)
{
- const blender::fn::MultiFunction &fn = get_multi_function(builder.bnode());
+ const blender::fn::MultiFunction *fn = get_multi_function(builder.node());
builder.set_matching_fn(fn);
}
@@ -89,7 +90,7 @@ void register_node_type_fn_float_to_int()
fn_node_type_base(&ntype, FN_NODE_FLOAT_TO_INT, "Float to Integer", NODE_CLASS_CONVERTOR, 0);
node_type_socket_templates(&ntype, fn_node_float_to_int_in, fn_node_float_to_int_out);
node_type_label(&ntype, node_float_to_int_label);
- ntype.expand_in_mf_network = node_float_to_int_expand_in_mf_network;
+ ntype.build_multi_function = fn_node_float_to_int_build_multi_function;
ntype.draw_buttons = fn_node_float_to_int_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/function/nodes/node_fn_input_string.cc b/source/blender/nodes/function/nodes/node_fn_input_string.cc
index f16bdef2f38..560ace57aba 100644
--- a/source/blender/nodes/function/nodes/node_fn_input_string.cc
+++ b/source/blender/nodes/function/nodes/node_fn_input_string.cc
@@ -29,14 +29,14 @@ static void fn_node_input_string_layout(uiLayout *layout, bContext *UNUSED(C), P
uiItemR(layout, ptr, "string", 0, "", ICON_NONE);
}
-static void fn_node_input_string_expand_in_mf_network(
- blender::nodes::NodeMFNetworkBuilder &builder)
+static void fn_node_input_string_build_multi_function(
+ blender::nodes::NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.bnode();
+ bNode &bnode = builder.node();
NodeInputString *node_storage = static_cast<NodeInputString *>(bnode.storage);
std::string string = std::string((node_storage->string) ? node_storage->string : "");
-
- builder.construct_and_set_matching_fn<blender::fn::CustomMF_Constant<std::string>>(string);
+ builder.construct_and_set_matching_fn<blender::fn::CustomMF_Constant<std::string>>(
+ std::move(string));
}
static void fn_node_input_string_init(bNodeTree *UNUSED(ntree), bNode *node)
@@ -78,7 +78,7 @@ void register_node_type_fn_input_string()
node_type_socket_templates(&ntype, nullptr, fn_node_input_string_out);
node_type_init(&ntype, fn_node_input_string_init);
node_type_storage(&ntype, "NodeInputString", fn_node_input_string_free, fn_node_string_copy);
- ntype.expand_in_mf_network = fn_node_input_string_expand_in_mf_network;
+ ntype.build_multi_function = fn_node_input_string_build_multi_function;
ntype.draw_buttons = fn_node_input_string_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/function/nodes/node_fn_input_vector.cc b/source/blender/nodes/function/nodes/node_fn_input_vector.cc
index 2cd4eb1d9df..244c045de9a 100644
--- a/source/blender/nodes/function/nodes/node_fn_input_vector.cc
+++ b/source/blender/nodes/function/nodes/node_fn_input_vector.cc
@@ -32,16 +32,14 @@ static void fn_node_input_vector_layout(uiLayout *layout, bContext *UNUSED(C), P
uiItemR(col, ptr, "vector", UI_ITEM_R_EXPAND, "", ICON_NONE);
}
-static void fn_node_vector_input_expand_in_mf_network(
- blender::nodes::NodeMFNetworkBuilder &builder)
+static void fn_node_vector_input_build_multi_function(
+ blender::nodes::NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.bnode();
+ bNode &bnode = builder.node();
NodeInputVector *node_storage = static_cast<NodeInputVector *>(bnode.storage);
blender::float3 vector(node_storage->vector);
-
builder.construct_and_set_matching_fn<blender::fn::CustomMF_Constant<blender::float3>>(vector);
}
-
static void fn_node_input_vector_init(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeInputVector *data = (NodeInputVector *)MEM_callocN(sizeof(NodeInputVector),
@@ -58,7 +56,7 @@ void register_node_type_fn_input_vector()
node_type_init(&ntype, fn_node_input_vector_init);
node_type_storage(
&ntype, "NodeInputVector", node_free_standard_storage, node_copy_standard_storage);
- ntype.expand_in_mf_network = fn_node_vector_input_expand_in_mf_network;
+ ntype.build_multi_function = fn_node_vector_input_build_multi_function;
ntype.draw_buttons = fn_node_input_vector_layout;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/function/nodes/node_fn_random_float.cc b/source/blender/nodes/function/nodes/node_fn_random_float.cc
index a3c9f44b6a1..47ec9adf6bd 100644
--- a/source/blender/nodes/function/nodes/node_fn_random_float.cc
+++ b/source/blender/nodes/function/nodes/node_fn_random_float.cc
@@ -67,10 +67,11 @@ class RandomFloatFunction : public blender::fn::MultiFunction {
}
};
-static void fn_node_random_float_expand_in_mf_network(
- blender::nodes::NodeMFNetworkBuilder &builder)
+static void fn_node_random_float_build_multi_function(
+ blender::nodes::NodeMultiFunctionBuilder &builder)
{
- builder.construct_and_set_matching_fn<RandomFloatFunction>();
+ static RandomFloatFunction fn;
+ builder.set_matching_fn(fn);
}
void register_node_type_fn_random_float()
@@ -79,6 +80,6 @@ void register_node_type_fn_random_float()
fn_node_type_base(&ntype, FN_NODE_RANDOM_FLOAT, "Random Float", 0, 0);
node_type_socket_templates(&ntype, fn_node_random_float_in, fn_node_random_float_out);
- ntype.expand_in_mf_network = fn_node_random_float_expand_in_mf_network;
+ ntype.build_multi_function = fn_node_random_float_build_multi_function;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc
index e0a3f5ad334..5f02061da97 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc
@@ -30,7 +30,7 @@
static bNodeSocketTemplate geo_node_attribute_sample_texture_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
- {SOCK_TEXTURE, N_("Texture")},
+ {SOCK_TEXTURE, N_("Texture"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, PROP_NONE, SOCK_HIDE_LABEL},
{SOCK_STRING, N_("Mapping")},
{SOCK_STRING, N_("Result")},
{-1, ""},
diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
index 4286db52115..91e08d7777b 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
@@ -150,7 +150,7 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords)
plConvexHullDelete(hull);
- BKE_mesh_calc_normals(result);
+ BKE_mesh_normals_tag_dirty(result);
return result;
}
@@ -304,6 +304,8 @@ static void geo_node_convex_hull_exec(GeoNodeExecParams params)
}
params.set_output("Convex Hull", GeometrySet::create_with_mesh(mesh));
#else
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("Disabled, Blender was compiled without Bullet"));
params.set_output("Convex Hull", geometry_set);
#endif /* WITH_BULLET */
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc
new file mode 100644
index 00000000000..fb21c05b0c0
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc
@@ -0,0 +1,147 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_task.hh"
+
+#include "BKE_spline.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_select_by_handle_type_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_STRING, N_("Selection")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_select_by_handle_type_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void geo_node_curve_select_by_handle_type_layout(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "handle_type", 0, "", ICON_NONE);
+}
+
+static void geo_node_curve_select_by_handle_type_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryCurveSelectHandles *data = (NodeGeometryCurveSelectHandles *)MEM_callocN(
+ sizeof(NodeGeometryCurveSelectHandles), __func__);
+
+ data->handle_type = GEO_NODE_CURVE_HANDLE_AUTO;
+ data->mode = GEO_NODE_CURVE_HANDLE_LEFT | GEO_NODE_CURVE_HANDLE_RIGHT;
+ node->storage = data;
+}
+
+static BezierSpline::HandleType handle_type_from_input_type(const GeometryNodeCurveHandleType type)
+{
+ switch (type) {
+ case GEO_NODE_CURVE_HANDLE_AUTO:
+ return BezierSpline::HandleType::Auto;
+ case GEO_NODE_CURVE_HANDLE_ALIGN:
+ return BezierSpline::HandleType::Align;
+ case GEO_NODE_CURVE_HANDLE_FREE:
+ return BezierSpline::HandleType::Free;
+ case GEO_NODE_CURVE_HANDLE_VECTOR:
+ return BezierSpline::HandleType::Vector;
+ }
+ BLI_assert_unreachable();
+ return BezierSpline::HandleType::Auto;
+}
+
+namespace blender::nodes {
+
+static void select_curve_by_handle_type(const CurveEval &curve,
+ const BezierSpline::HandleType type,
+ const GeometryNodeCurveHandleMode mode,
+ const MutableSpan<bool> r_selection)
+{
+ const Array<int> offsets = curve.control_point_offsets();
+ Span<SplinePtr> splines = curve.splines();
+ threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
+ for (const int i_spline : range) {
+ const Spline &spline = *splines[i_spline];
+ if (spline.type() == Spline::Type::Bezier) {
+ const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(spline);
+ Span<BezierSpline::HandleType> types_left = bezier_spline.handle_types_left();
+ Span<BezierSpline::HandleType> types_right = bezier_spline.handle_types_right();
+ for (const int i_point : IndexRange(bezier_spline.size())) {
+ r_selection[offsets[i_spline] + i_point] = (mode & GEO_NODE_CURVE_HANDLE_LEFT &&
+ types_left[i_point] == type) ||
+ (mode & GEO_NODE_CURVE_HANDLE_RIGHT &&
+ types_right[i_point] == type);
+ }
+ }
+ else {
+ r_selection.slice(offsets[i_spline], offsets[i_spline + 1]).fill(false);
+ }
+ }
+ });
+}
+
+static void geo_node_select_by_handle_type_exec(GeoNodeExecParams params)
+{
+ const NodeGeometryCurveSelectHandles *storage =
+ (const NodeGeometryCurveSelectHandles *)params.node().storage;
+ const BezierSpline::HandleType handle_type = handle_type_from_input_type(
+ (GeometryNodeCurveHandleType)storage->handle_type);
+ const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)storage->mode;
+
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ geometry_set = bke::geometry_set_realize_instances(geometry_set);
+
+ CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>();
+ const CurveEval *curve = curve_component.get_for_read();
+
+ if (curve != nullptr) {
+ const std::string selection_name = params.extract_input<std::string>("Selection");
+ OutputAttribute_Typed<bool> selection =
+ curve_component.attribute_try_get_for_output_only<bool>(selection_name, ATTR_DOMAIN_POINT);
+ if (selection) {
+ select_curve_by_handle_type(*curve, handle_type, mode, selection.as_span());
+ selection.save();
+ }
+ }
+
+ params.set_output("Geometry", std::move(geometry_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_select_by_handle_type()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_CURVE_SELECT_HANDLES, "Select by Handle Type", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(
+ &ntype, geo_node_select_by_handle_type_in, geo_node_select_by_handle_type_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_select_by_handle_type_exec;
+ node_type_init(&ntype, geo_node_curve_select_by_handle_type_init);
+ node_type_storage(&ntype,
+ "NodeGeometryCurveSelectHandles",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ ntype.draw_buttons = geo_node_curve_select_by_handle_type_layout;
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc
new file mode 100644
index 00000000000..fe3f42625ae
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc
@@ -0,0 +1,307 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BKE_spline.hh"
+
+#include "BLI_task.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+static bNodeSocketTemplate geo_node_curve_spline_type_in[] = {
+ {SOCK_GEOMETRY, N_("Curve")},
+ {SOCK_STRING, N_("Selection")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_curve_spline_type_out[] = {
+ {SOCK_GEOMETRY, N_("Curve")},
+ {-1, ""},
+};
+
+static void geo_node_curve_spline_type_layout(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "spline_type", 0, "", ICON_NONE);
+}
+
+namespace blender::nodes {
+
+static void geo_node_curve_spline_type_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryCurveSplineType *data = (NodeGeometryCurveSplineType *)MEM_callocN(
+ sizeof(NodeGeometryCurveSplineType), __func__);
+
+ data->spline_type = GEO_NODE_SPLINE_TYPE_POLY;
+ node->storage = data;
+}
+
+template<class T>
+static void scale_input_assign(const Span<T> input,
+ const int scale,
+ const int offset,
+ const MutableSpan<T> r_output)
+{
+ for (const int i : IndexRange(r_output.size())) {
+ r_output[i] = input[i * scale + offset];
+ }
+}
+
+template<class T>
+static void scale_output_assign(const Span<T> input,
+ const int scale,
+ const int offset,
+ const MutableSpan<T> &r_output)
+{
+ for (const int i : IndexRange(input.size())) {
+ r_output[i * scale + offset] = input[i];
+ }
+}
+
+template<typename CopyFn>
+static void copy_attributes(const Spline &input_spline, Spline &output_spline, CopyFn copy_fn)
+{
+ input_spline.attributes.foreach_attribute(
+ [&](StringRefNull name, const AttributeMetaData &meta_data) {
+ std::optional<GSpan> src = input_spline.attributes.get_for_read(name);
+ BLI_assert(src);
+ if (!output_spline.attributes.create(name, meta_data.data_type)) {
+ BLI_assert_unreachable();
+ return false;
+ }
+ std::optional<GMutableSpan> dst = output_spline.attributes.get_for_write(name);
+ if (!dst) {
+ BLI_assert_unreachable();
+ return false;
+ }
+
+ copy_fn(*src, *dst);
+
+ return true;
+ },
+ ATTR_DOMAIN_POINT);
+}
+
+static SplinePtr convert_to_poly_spline(const Spline &input)
+{
+ std::unique_ptr<PolySpline> output = std::make_unique<PolySpline>();
+ output->resize(input.positions().size());
+ output->positions().copy_from(input.positions());
+ output->radii().copy_from(input.radii());
+ output->tilts().copy_from(input.tilts());
+ Spline::copy_base_settings(input, *output);
+ output->attributes = input.attributes;
+ return output;
+}
+
+static SplinePtr poly_to_nurbs(const Spline &input)
+{
+ std::unique_ptr<NURBSpline> output = std::make_unique<NURBSpline>();
+ output->resize(input.positions().size());
+ output->positions().copy_from(input.positions());
+ output->radii().copy_from(input.radii());
+ output->tilts().copy_from(input.tilts());
+ output->weights().fill(1.0f);
+ output->set_resolution(12);
+ output->set_order(4);
+ Spline::copy_base_settings(input, *output);
+ output->knots_mode = NURBSpline::KnotsMode::Bezier;
+ output->attributes = input.attributes;
+ return output;
+}
+
+static SplinePtr bezier_to_nurbs(const Spline &input)
+{
+ const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(input);
+ std::unique_ptr<NURBSpline> output = std::make_unique<NURBSpline>();
+ output->resize(input.size() * 3);
+
+ scale_output_assign(bezier_spline.handle_positions_left(), 3, 0, output->positions());
+ scale_output_assign(input.radii(), 3, 0, output->radii());
+ scale_output_assign(input.tilts(), 3, 0, output->tilts());
+
+ scale_output_assign(bezier_spline.positions(), 3, 1, output->positions());
+ scale_output_assign(input.radii(), 3, 1, output->radii());
+ scale_output_assign(input.tilts(), 3, 1, output->tilts());
+
+ scale_output_assign(bezier_spline.handle_positions_right(), 3, 2, output->positions());
+ scale_output_assign(input.radii(), 3, 2, output->radii());
+ scale_output_assign(input.tilts(), 3, 2, output->tilts());
+
+ Spline::copy_base_settings(input, *output);
+ output->weights().fill(1.0f);
+ output->set_resolution(12);
+ output->set_order(4);
+ output->set_cyclic(input.is_cyclic());
+ output->knots_mode = NURBSpline::KnotsMode::Bezier;
+ output->attributes.reallocate(output->size());
+ copy_attributes(input, *output, [](GSpan src, GMutableSpan dst) {
+ attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ scale_output_assign<T>(src.typed<T>(), 3, 0, dst.typed<T>());
+ scale_output_assign<T>(src.typed<T>(), 3, 1, dst.typed<T>());
+ scale_output_assign<T>(src.typed<T>(), 3, 2, dst.typed<T>());
+ });
+ });
+ return output;
+}
+
+static SplinePtr poly_to_bezier(const Spline &input)
+{
+ std::unique_ptr<BezierSpline> output = std::make_unique<BezierSpline>();
+ output->resize(input.size());
+ output->positions().copy_from(input.positions());
+ output->radii().copy_from(input.radii());
+ output->tilts().copy_from(input.tilts());
+ output->handle_types_left().fill(BezierSpline::HandleType::Vector);
+ output->handle_types_right().fill(BezierSpline::HandleType::Vector);
+ output->set_resolution(12);
+ Spline::copy_base_settings(input, *output);
+ output->attributes = input.attributes;
+ return output;
+}
+
+static SplinePtr nurbs_to_bezier(const Spline &input)
+{
+ const NURBSpline &nurbs_spline = static_cast<const NURBSpline &>(input);
+ std::unique_ptr<BezierSpline> output = std::make_unique<BezierSpline>();
+ output->resize(input.size() / 3);
+ scale_input_assign<float3>(input.positions(), 3, 1, output->positions());
+ scale_input_assign<float3>(input.positions(), 3, 0, output->handle_positions_left());
+ scale_input_assign<float3>(input.positions(), 3, 2, output->handle_positions_right());
+ scale_input_assign<float>(input.radii(), 3, 2, output->radii());
+ scale_input_assign<float>(input.tilts(), 3, 2, output->tilts());
+ output->handle_types_left().fill(BezierSpline::HandleType::Align);
+ output->handle_types_right().fill(BezierSpline::HandleType::Align);
+ output->set_resolution(nurbs_spline.resolution());
+ Spline::copy_base_settings(input, *output);
+ output->attributes.reallocate(output->size());
+ copy_attributes(input, *output, [](GSpan src, GMutableSpan dst) {
+ attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ scale_input_assign<T>(src.typed<T>(), 3, 1, dst.typed<T>());
+ });
+ });
+ return output;
+}
+
+static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params)
+{
+ switch (input.type()) {
+ case Spline::Type::Bezier:
+ return input.copy();
+ case Spline::Type::Poly:
+ return poly_to_bezier(input);
+ case Spline::Type::NURBS:
+ if (input.size() < 6) {
+ params.error_message_add(
+ NodeWarningType::Info,
+ TIP_("NURBS must have minimum of 6 points for Bezier Conversion"));
+ return input.copy();
+ }
+ else {
+ if (input.size() % 3 != 0) {
+ params.error_message_add(NodeWarningType::Info,
+ TIP_("NURBS must have multiples of 3 points for full Bezier "
+ "conversion, curve truncated"));
+ }
+ return nurbs_to_bezier(input);
+ }
+ }
+ BLI_assert_unreachable();
+ return {};
+}
+
+static SplinePtr convert_to_nurbs(const Spline &input)
+{
+ switch (input.type()) {
+ case Spline::Type::NURBS:
+ return input.copy();
+ case Spline::Type::Bezier:
+ return bezier_to_nurbs(input);
+ case Spline::Type::Poly:
+ return poly_to_nurbs(input);
+ }
+ BLI_assert_unreachable();
+ return {};
+}
+
+static void geo_node_curve_spline_type_exec(GeoNodeExecParams params)
+{
+ const NodeGeometryCurveSplineType *storage =
+ (const NodeGeometryCurveSplineType *)params.node().storage;
+ const GeometryNodeSplineType output_type = (const GeometryNodeSplineType)storage->spline_type;
+
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
+ geometry_set = bke::geometry_set_realize_instances(geometry_set);
+ if (!geometry_set.has_curve()) {
+ params.set_output("Curve", geometry_set);
+ return;
+ }
+
+ const CurveComponent *curve_component = geometry_set.get_component_for_read<CurveComponent>();
+ const CurveEval &curve = *curve_component->get_for_read();
+
+ const std::string selection_name = params.extract_input<std::string>("Selection");
+ GVArray_Typed<bool> selection = curve_component->attribute_get_for_read(
+ selection_name, ATTR_DOMAIN_CURVE, true);
+
+ std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>();
+ for (const int i : curve.splines().index_range()) {
+ if (selection[i]) {
+ switch (output_type) {
+ case GEO_NODE_SPLINE_TYPE_POLY:
+ new_curve->add_spline(convert_to_poly_spline(*curve.splines()[i]));
+ break;
+ case GEO_NODE_SPLINE_TYPE_BEZIER:
+ new_curve->add_spline(convert_to_bezier(*curve.splines()[i], params));
+ break;
+ case GEO_NODE_SPLINE_TYPE_NURBS:
+ new_curve->add_spline(convert_to_nurbs(*curve.splines()[i]));
+ break;
+ }
+ }
+ else {
+ new_curve->add_spline(curve.splines()[i]->copy());
+ }
+ }
+
+ new_curve->attributes = curve.attributes;
+ params.set_output("Curve", GeometrySet::create_with_curve(new_curve.release()));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_curve_spline_type()
+{
+ static bNodeType ntype;
+ geo_node_type_base(
+ &ntype, GEO_NODE_CURVE_SPLINE_TYPE, "Set Spline Type", NODE_CLASS_GEOMETRY, 0);
+ node_type_socket_templates(
+ &ntype, geo_node_curve_spline_type_in, geo_node_curve_spline_type_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_curve_spline_type_exec;
+ node_type_init(&ntype, blender::nodes::geo_node_curve_spline_type_init);
+ node_type_storage(&ntype,
+ "NodeGeometryCurveSplineType",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ ntype.draw_buttons = geo_node_curve_spline_type_layout;
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc
index 667e1c931bd..131f9548b40 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc
@@ -126,11 +126,11 @@ static Mesh *create_circle_mesh(const float radius,
MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
- float angle = 0.0f;
- const float angle_delta = 2.0f * M_PI / static_cast<float>(verts_num);
- for (MVert &vert : verts) {
- copy_v3_v3(vert.co, float3(std::cos(angle) * radius, std::sin(angle) * radius, 0.0f));
- angle += angle_delta;
+ /* Assign vertex coordinates. */
+ const float angle_delta = 2.0f * (M_PI / static_cast<float>(verts_num));
+ for (const int i : IndexRange(verts_num)) {
+ const float angle = i * angle_delta;
+ copy_v3_v3(verts[i].co, float3(std::cos(angle) * radius, std::sin(angle) * radius, 0.0f));
}
if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
copy_v3_v3(verts.last().co, float3(0));
@@ -211,6 +211,7 @@ static void geo_node_mesh_primitive_circle_exec(GeoNodeExecParams params)
const float radius = params.extract_input<float>("Radius");
const int verts_num = params.extract_input<int>("Vertices");
if (verts_num < 3) {
+ params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3"));
params.set_output("Geometry", GeometrySet());
return;
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc
index 925ed0f8da8..b834f5e2fa0 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc
@@ -299,7 +299,7 @@ Mesh *create_cylinder_or_cone_mesh(const float radius_top,
mesh->medge[0].v1 = 0;
mesh->medge[0].v2 = 1;
mesh->medge[0].flag |= ME_LOOSEEDGE;
- BKE_mesh_calc_normals(mesh);
+ BKE_mesh_normals_tag_dirty(mesh);
return mesh;
}
@@ -318,9 +318,9 @@ Mesh *create_cylinder_or_cone_mesh(const float radius_top,
/* Calculate vertex positions. */
const int top_verts_start = 0;
const int bottom_verts_start = top_verts_start + (!top_is_point ? verts_num : 1);
- float angle = 0.0f;
- const float angle_delta = 2.0f * M_PI / static_cast<float>(verts_num);
+ const float angle_delta = 2.0f * (M_PI / static_cast<float>(verts_num));
for (const int i : IndexRange(verts_num)) {
+ const float angle = i * angle_delta;
const float x = std::cos(angle);
const float y = std::sin(angle);
if (!top_is_point) {
@@ -330,7 +330,6 @@ Mesh *create_cylinder_or_cone_mesh(const float radius_top,
copy_v3_v3(verts[bottom_verts_start + i].co,
float3(x * radius_bottom, y * radius_bottom, -height));
}
- angle += angle_delta;
}
if (top_is_point) {
copy_v3_v3(verts[top_verts_start].co, float3(0.0f, 0.0f, height));
@@ -534,12 +533,10 @@ Mesh *create_cylinder_or_cone_mesh(const float radius_top,
}
}
- BKE_mesh_calc_normals(mesh);
+ BKE_mesh_normals_tag_dirty(mesh);
calculate_uvs(mesh, top_is_point, bottom_is_point, verts_num, fill_type);
- BLI_assert(BKE_mesh_is_valid(mesh));
-
return mesh;
}
@@ -553,6 +550,7 @@ static void geo_node_mesh_primitive_cone_exec(GeoNodeExecParams params)
const int verts_num = params.extract_input<int>("Vertices");
if (verts_num < 3) {
+ params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3"));
params.set_output("Geometry", GeometrySet());
return;
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc
index 1767f765da4..b40cb478b03 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc
@@ -70,6 +70,7 @@ static void geo_node_mesh_primitive_cylinder_exec(GeoNodeExecParams params)
const float depth = params.extract_input<float>("Depth");
const int verts_num = params.extract_input<int>("Vertices");
if (verts_num < 3) {
+ params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3"));
params.set_output("Geometry", GeometrySet());
return;
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc
index ac2f5a23a4d..410290c79ee 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc
@@ -79,19 +79,17 @@ static Mesh *create_grid_mesh(const int verts_x,
MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
{
- const float dx = size_x / edges_x;
- const float dy = size_y / edges_y;
- float x = -size_x * 0.5;
+ const float dx = edges_x == 0 ? 0.0f : size_x / edges_x;
+ const float dy = edges_y == 0 ? 0.0f : size_y / edges_y;
+ const float x_shift = edges_x / 2.0f;
+ const float y_shift = edges_y / 2.0f;
for (const int x_index : IndexRange(verts_x)) {
- float y = -size_y * 0.5;
for (const int y_index : IndexRange(verts_y)) {
const int vert_index = x_index * verts_y + y_index;
- verts[vert_index].co[0] = x;
- verts[vert_index].co[1] = y;
+ verts[vert_index].co[0] = (x_index - x_shift) * dx;
+ verts[vert_index].co[1] = (y_index - y_shift) * dy;
verts[vert_index].co[2] = 0.0f;
- y += dy;
}
- x += dx;
}
}
@@ -162,6 +160,12 @@ static void geo_node_mesh_primitive_grid_exec(GeoNodeExecParams params)
const int verts_x = params.extract_input<int>("Vertices X");
const int verts_y = params.extract_input<int>("Vertices Y");
if (verts_x < 2 || verts_y < 2) {
+ if (verts_x < 2) {
+ params.error_message_add(NodeWarningType::Info, TIP_("Vertices X must be at least 2"));
+ }
+ if (verts_y < 2) {
+ params.error_message_add(NodeWarningType::Info, TIP_("Vertices Y must be at least 2"));
+ }
params.set_output("Geometry", GeometrySet());
return;
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc
index a193c05daa1..2e6d8ca34c8 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc
@@ -118,11 +118,9 @@ static Mesh *create_line_mesh(const float3 start, const float3 delta, const int
short normal[3];
normal_float_to_short_v3(normal, delta.normalized());
- float3 co = start;
for (const int i : verts.index_range()) {
- copy_v3_v3(verts[i].co, co);
+ copy_v3_v3(verts[i].co, start + delta * i);
copy_v3_v3_short(verts[i].no, normal);
- co += delta;
}
fill_edge_data(edges);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc
index 599c59e4a2e..affba602234 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc
@@ -69,26 +69,24 @@ static void calculate_sphere_vertex_data(MutableSpan<MVert> verts,
const int rings)
{
const float delta_theta = M_PI / rings;
- const float delta_phi = (2 * M_PI) / segments;
+ const float delta_phi = (2.0f * M_PI) / segments;
copy_v3_v3(verts[0].co, float3(0.0f, 0.0f, radius));
normal_float_to_short_v3(verts[0].no, float3(0.0f, 0.0f, 1.0f));
int vert_index = 1;
- float theta = delta_theta;
- for (const int UNUSED(ring) : IndexRange(rings - 1)) {
- float phi = 0.0f;
- const float z = cosf(theta);
- for (const int UNUSED(segment) : IndexRange(segments)) {
+ for (const int ring : IndexRange(1, rings - 1)) {
+ const float theta = ring * delta_theta;
+ const float z = std::cos(theta);
+ for (const int segment : IndexRange(1, segments)) {
+ const float phi = segment * delta_phi;
const float sin_theta = std::sin(theta);
const float x = sin_theta * std::cos(phi);
const float y = sin_theta * std::sin(phi);
copy_v3_v3(verts[vert_index].co, float3(x, y, z) * radius);
normal_float_to_short_v3(verts[vert_index].no, float3(x, y, z));
- phi += delta_phi;
vert_index++;
}
- theta += delta_theta;
}
copy_v3_v3(verts.last().co, float3(0.0f, 0.0f, -radius));
@@ -291,6 +289,12 @@ static void geo_node_mesh_primitive_uv_sphere_exec(GeoNodeExecParams params)
const int segments_num = params.extract_input<int>("Segments");
const int rings_num = params.extract_input<int>("Rings");
if (segments_num < 3 || rings_num < 2) {
+ if (segments_num < 3) {
+ params.error_message_add(NodeWarningType::Info, TIP_("Segments must be at least 3"));
+ }
+ if (rings_num < 3) {
+ params.error_message_add(NodeWarningType::Info, TIP_("Rings must be at least 3"));
+ }
params.set_output("Geometry", GeometrySet());
return;
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc
index 245d7800621..165da8ec9f2 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc
@@ -88,7 +88,7 @@ static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params)
}
Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh_in);
- BKE_mesh_calc_normals(mesh_out);
+ BKE_mesh_normals_tag_dirty(mesh_out);
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
mesh_component.replace(mesh_out);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc
index 0c1d8645411..88e3bf17d43 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc
@@ -63,20 +63,20 @@ static void geo_node_raycast_init(bNodeTree *UNUSED(tree), bNode *node)
node->storage = data;
}
+namespace blender::nodes {
+
static void geo_node_raycast_update(bNodeTree *UNUSED(ntree), bNode *node)
{
NodeGeometryRaycast *node_storage = (NodeGeometryRaycast *)node->storage;
- blender::nodes::update_attribute_input_socket_availabilities(
+ update_attribute_input_socket_availabilities(
*node,
"Ray Direction",
(GeometryNodeAttributeInputMode)node_storage->input_type_ray_direction);
- blender::nodes::update_attribute_input_socket_availabilities(
+ update_attribute_input_socket_availabilities(
*node, "Ray Length", (GeometryNodeAttributeInputMode)node_storage->input_type_ray_length);
}
-namespace blender::nodes {
-
-static void raycast_to_mesh(const Mesh *mesh,
+static void raycast_to_mesh(const Mesh &mesh,
const VArray<float3> &ray_origins,
const VArray<float3> &ray_directions,
const VArray<float> &ray_lengths,
@@ -95,62 +95,64 @@ static void raycast_to_mesh(const Mesh *mesh,
BLI_assert(ray_origins.size() == r_hit_distances.size() || r_hit_distances.is_empty());
BVHTreeFromMesh tree_data;
- BKE_bvhtree_from_mesh_get(&tree_data, mesh, BVHTREE_FROM_LOOPTRI, 4);
-
- if (tree_data.tree != nullptr) {
- for (const int i : ray_origins.index_range()) {
- const float ray_length = ray_lengths[i];
- const float3 ray_origin = ray_origins[i];
- const float3 ray_direction = ray_directions[i].normalized();
-
- BVHTreeRayHit hit;
- hit.index = -1;
- hit.dist = ray_length;
- if (BLI_bvhtree_ray_cast(tree_data.tree,
- ray_origin,
- ray_direction,
- 0.0f,
- &hit,
- tree_data.raycast_callback,
- &tree_data) != -1) {
- if (!r_hit.is_empty()) {
- r_hit[i] = hit.index >= 0;
- }
- if (!r_hit_indices.is_empty()) {
- /* Index should always be a valid looptri index, use 0 when hit failed. */
- r_hit_indices[i] = max_ii(hit.index, 0);
- }
- if (!r_hit_positions.is_empty()) {
- r_hit_positions[i] = hit.co;
- }
- if (!r_hit_normals.is_empty()) {
- r_hit_normals[i] = hit.no;
- }
- if (!r_hit_distances.is_empty()) {
- r_hit_distances[i] = hit.dist;
- }
+ BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_LOOPTRI, 4);
+ if (tree_data.tree == nullptr) {
+ free_bvhtree_from_mesh(&tree_data);
+ return;
+ }
+
+ for (const int i : ray_origins.index_range()) {
+ const float ray_length = ray_lengths[i];
+ const float3 ray_origin = ray_origins[i];
+ const float3 ray_direction = ray_directions[i].normalized();
+
+ BVHTreeRayHit hit;
+ hit.index = -1;
+ hit.dist = ray_length;
+ if (BLI_bvhtree_ray_cast(tree_data.tree,
+ ray_origin,
+ ray_direction,
+ 0.0f,
+ &hit,
+ tree_data.raycast_callback,
+ &tree_data) != -1) {
+ if (!r_hit.is_empty()) {
+ r_hit[i] = hit.index >= 0;
+ }
+ if (!r_hit_indices.is_empty()) {
+ /* Index should always be a valid looptri index, use 0 when hit failed. */
+ r_hit_indices[i] = max_ii(hit.index, 0);
+ }
+ if (!r_hit_positions.is_empty()) {
+ r_hit_positions[i] = hit.co;
}
- else {
- if (!r_hit.is_empty()) {
- r_hit[i] = false;
- }
- if (!r_hit_indices.is_empty()) {
- r_hit_indices[i] = 0;
- }
- if (!r_hit_positions.is_empty()) {
- r_hit_positions[i] = float3(0.0f, 0.0f, 0.0f);
- }
- if (!r_hit_normals.is_empty()) {
- r_hit_normals[i] = float3(0.0f, 0.0f, 0.0f);
- }
- if (!r_hit_distances.is_empty()) {
- r_hit_distances[i] = ray_length;
- }
+ if (!r_hit_normals.is_empty()) {
+ r_hit_normals[i] = hit.no;
+ }
+ if (!r_hit_distances.is_empty()) {
+ r_hit_distances[i] = hit.dist;
+ }
+ }
+ else {
+ if (!r_hit.is_empty()) {
+ r_hit[i] = false;
+ }
+ if (!r_hit_indices.is_empty()) {
+ r_hit_indices[i] = 0;
+ }
+ if (!r_hit_positions.is_empty()) {
+ r_hit_positions[i] = float3(0.0f, 0.0f, 0.0f);
+ }
+ if (!r_hit_normals.is_empty()) {
+ r_hit_normals[i] = float3(0.0f, 0.0f, 0.0f);
+ }
+ if (!r_hit_distances.is_empty()) {
+ r_hit_distances[i] = ray_length;
}
}
-
- free_bvhtree_from_mesh(&tree_data);
}
+
+ free_bvhtree_from_mesh(&tree_data);
}
static bke::mesh_surface_sample::eAttributeMapMode get_map_mode(
@@ -166,7 +168,7 @@ static bke::mesh_surface_sample::eAttributeMapMode get_map_mode(
}
static void raycast_from_points(const GeoNodeExecParams &params,
- const GeometrySet &src_geometry,
+ const GeometrySet &target_geometry,
GeometryComponent &dst_component,
const StringRef hit_name,
const StringRef hit_position_name,
@@ -177,7 +179,8 @@ static void raycast_from_points(const GeoNodeExecParams &params,
{
BLI_assert(hit_attribute_names.size() == hit_attribute_output_names.size());
- const MeshComponent *src_mesh_component = src_geometry.get_component_for_read<MeshComponent>();
+ const MeshComponent *src_mesh_component =
+ target_geometry.get_component_for_read<MeshComponent>();
if (src_mesh_component == nullptr) {
return;
}
@@ -211,8 +214,7 @@ static void raycast_from_points(const GeoNodeExecParams &params,
dst_component.attribute_try_get_for_output_only<float>(hit_distance_name, result_domain);
/* Positions and looptri indices are always needed for interpolation,
- * so create temporary arrays if no output attribute is given.
- */
+ * so create temporary arrays if no output attribute is given. */
Array<int> hit_indices;
Array<float3> hit_positions_internal;
if (!hit_attribute_names.is_empty()) {
@@ -232,7 +234,7 @@ static void raycast_from_points(const GeoNodeExecParams &params,
hit_distance_attribute.as_span() :
MutableSpan<float>();
- raycast_to_mesh(src_mesh,
+ raycast_to_mesh(*src_mesh,
ray_origins,
ray_directions,
ray_lengths,
@@ -268,34 +270,32 @@ static void raycast_from_points(const GeoNodeExecParams &params,
static void geo_node_raycast_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- GeometrySet cast_geometry_set = params.extract_input<GeometrySet>("Target Geometry");
+ GeometrySet target_geometry_set = params.extract_input<GeometrySet>("Target Geometry");
const std::string hit_name = params.extract_input<std::string>("Is Hit");
const std::string hit_position_name = params.extract_input<std::string>("Hit Position");
const std::string hit_normal_name = params.extract_input<std::string>("Hit Normal");
const std::string hit_distance_name = params.extract_input<std::string>("Hit Distance");
- const Array<std::string> hit_attribute_names = {
- params.extract_input<std::string>("Target Attribute")};
- const Array<std::string> hit_attribute_output_names = {
- params.extract_input<std::string>("Hit Attribute")};
+ const Array<std::string> hit_names = {params.extract_input<std::string>("Target Attribute")};
+ const Array<std::string> hit_output_names = {params.extract_input<std::string>("Hit Attribute")};
geometry_set = bke::geometry_set_realize_instances(geometry_set);
- cast_geometry_set = bke::geometry_set_realize_instances(cast_geometry_set);
+ target_geometry_set = bke::geometry_set_realize_instances(target_geometry_set);
- static const Array<GeometryComponentType> SupportedTypes = {
+ static const Array<GeometryComponentType> types = {
GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE};
- for (GeometryComponentType geo_type : SupportedTypes) {
- if (geometry_set.has(geo_type)) {
+ for (const GeometryComponentType type : types) {
+ if (geometry_set.has(type)) {
raycast_from_points(params,
- cast_geometry_set,
- geometry_set.get_component_for_write(geo_type),
+ target_geometry_set,
+ geometry_set.get_component_for_write(type),
hit_name,
hit_position_name,
hit_normal_name,
hit_distance_name,
- hit_attribute_names,
- hit_attribute_output_names);
+ hit_names,
+ hit_output_names);
}
}
@@ -312,7 +312,7 @@ void register_node_type_geo_raycast()
node_type_socket_templates(&ntype, geo_node_raycast_in, geo_node_raycast_out);
node_type_size_preset(&ntype, NODE_SIZE_LARGE);
node_type_init(&ntype, geo_node_raycast_init);
- node_type_update(&ntype, geo_node_raycast_update);
+ node_type_update(&ntype, blender::nodes::geo_node_raycast_update);
node_type_storage(
&ntype, "NodeGeometryRaycast", node_free_standard_storage, node_copy_standard_storage);
ntype.geometry_node_execute = blender::nodes::geo_node_raycast_exec;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
index f0f032ed8f4..4f70252ae75 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
@@ -18,17 +18,15 @@
#include "BKE_subdiv.h"
#include "BKE_subdiv_mesh.h"
+#include "DNA_modifier_types.h"
#include "UI_interface.h"
#include "UI_resources.h"
-
#include "node_geometry_util.hh"
static bNodeSocketTemplate geo_node_subdivision_surface_in[] = {
{SOCK_GEOMETRY, N_("Geometry")},
{SOCK_INT, N_("Level"), 1, 0, 0, 0, 0, 6},
{SOCK_BOOLEAN, N_("Use Creases")},
- {SOCK_BOOLEAN, N_("Boundary Smooth"), true},
- {SOCK_BOOLEAN, N_("Smooth UVs")},
{-1, ""},
};
@@ -37,6 +35,30 @@ static bNodeSocketTemplate geo_node_subdivision_surface_out[] = {
{-1, ""},
};
+static void geo_node_subdivision_surface_layout(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+#ifndef WITH_OPENSUBDIV
+ UNUSED_VARS(ptr);
+ uiItemL(layout, IFACE_("Disabled, built without OpenSubdiv"), ICON_ERROR);
+#else
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiItemR(layout, ptr, "uv_smooth", 0, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "boundary_smooth", 0, nullptr, ICON_NONE);
+#endif
+}
+
+static void geo_node_subdivision_surface_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometrySubdivisionSurface *data = (NodeGeometrySubdivisionSurface *)MEM_callocN(
+ sizeof(NodeGeometrySubdivisionSurface), __func__);
+ data->uv_smooth = SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES;
+ data->boundary_smooth = SUBSURF_BOUNDARY_SMOOTH_ALL;
+ node->storage = data;
+}
+
namespace blender::nodes {
static void geo_node_subdivision_surface_exec(GeoNodeExecParams params)
{
@@ -53,6 +75,10 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params)
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without OpenSubdiv"));
#else
+ const NodeGeometrySubdivisionSurface &storage =
+ *(const NodeGeometrySubdivisionSurface *)params.node().storage;
+ const int uv_smooth = storage.uv_smooth;
+ const int boundary_smooth = storage.boundary_smooth;
const int subdiv_level = clamp_i(params.extract_input<int>("Level"), 0, 30);
/* Only process subdivision if level is greater than 0. */
@@ -62,8 +88,6 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params)
}
const bool use_crease = params.extract_input<bool>("Use Creases");
- const bool boundary_smooth = params.extract_input<bool>("Boundary Smooth");
- const bool smooth_uvs = params.extract_input<bool>("Smooth UVs");
const Mesh *mesh_in = geometry_set.get_mesh_for_read();
/* Initialize mesh settings. */
@@ -79,9 +103,9 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params)
subdiv_settings.level = subdiv_level;
subdiv_settings.vtx_boundary_interpolation = BKE_subdiv_vtx_boundary_interpolation_from_subsurf(
- !boundary_smooth);
+ boundary_smooth);
subdiv_settings.fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth(
- smooth_uvs);
+ uv_smooth);
/* Apply subdivision to mesh. */
Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, mesh_in);
@@ -93,7 +117,7 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params)
}
Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh_in);
- BKE_mesh_calc_normals(mesh_out);
+ BKE_mesh_normals_tag_dirty(mesh_out);
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
mesh_component.replace(mesh_out);
@@ -117,5 +141,12 @@ void register_node_type_geo_subdivision_surface()
node_type_socket_templates(
&ntype, geo_node_subdivision_surface_in, geo_node_subdivision_surface_out);
ntype.geometry_node_execute = blender::nodes::geo_node_subdivision_surface_exec;
+ ntype.draw_buttons = geo_node_subdivision_surface_layout;
+ node_type_init(&ntype, geo_node_subdivision_surface_init);
+ node_type_size_preset(&ntype, NODE_SIZE_MIDDLE);
+ node_type_storage(&ntype,
+ "NodeGeometrySubdivisionSurface",
+ node_free_standard_storage,
+ node_copy_standard_storage);
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc
index 403f4906d07..4c1151bf6c2 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc
@@ -149,6 +149,9 @@ static void geo_node_volume_to_mesh_exec(GeoNodeExecParams params)
#ifdef WITH_OPENVDB
create_mesh_from_volume(geometry_set_in, geometry_set_out, params);
+#else
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("Disabled, Blender was compiled without OpenVDB"));
#endif
params.set_output("Geometry", geometry_set_out);
diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc
index bfd1ad02d36..a3bbca90731 100644
--- a/source/blender/nodes/intern/node_geometry_exec.cc
+++ b/source/blender/nodes/intern/node_geometry_exec.cc
@@ -19,7 +19,6 @@
#include "DEG_depsgraph_query.h"
#include "NOD_geometry_exec.hh"
-#include "NOD_type_callbacks.hh"
#include "NOD_type_conversions.hh"
#include "node_geometry_util.hh"
@@ -218,7 +217,7 @@ void GeoNodeExecParams::check_input_access(StringRef identifier,
BLI_assert_unreachable();
}
else if (requested_type != nullptr) {
- const CPPType &expected_type = *socket_cpp_type_get(*found_socket->typeinfo);
+ const CPPType &expected_type = *found_socket->typeinfo->get_geometry_nodes_cpp_type();
if (*requested_type != expected_type) {
std::cout << "The requested type '" << requested_type->name() << "' is incorrect. Expected '"
<< expected_type.name() << "'.\n";
@@ -258,7 +257,7 @@ void GeoNodeExecParams::check_output_access(StringRef identifier, const CPPType
BLI_assert_unreachable();
}
else {
- const CPPType &expected_type = *socket_cpp_type_get(*found_socket->typeinfo);
+ const CPPType &expected_type = *found_socket->typeinfo->get_geometry_nodes_cpp_type();
if (value_type != expected_type) {
std::cout << "The value type '" << value_type.name() << "' is incorrect. Expected '"
<< expected_type.name() << "'.\n";
diff --git a/source/blender/nodes/intern/node_multi_function.cc b/source/blender/nodes/intern/node_multi_function.cc
new file mode 100644
index 00000000000..c91899ed8c2
--- /dev/null
+++ b/source/blender/nodes/intern/node_multi_function.cc
@@ -0,0 +1,40 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "NOD_multi_function.hh"
+
+namespace blender::nodes {
+
+NodeMultiFunctions::NodeMultiFunctions(const DerivedNodeTree &tree, ResourceScope &resource_scope)
+{
+ for (const NodeTreeRef *tree_ref : tree.used_node_tree_refs()) {
+ bNodeTree *btree = tree_ref->btree();
+ for (const NodeRef *node : tree_ref->nodes()) {
+ bNode *bnode = node->bnode();
+ if (bnode->typeinfo->build_multi_function == nullptr) {
+ continue;
+ }
+ NodeMultiFunctionBuilder builder{resource_scope, *bnode, *btree};
+ bnode->typeinfo->build_multi_function(builder);
+ const MultiFunction *fn = builder.built_fn_;
+ if (fn != nullptr) {
+ map_.add_new(bnode, fn);
+ }
+ }
+ }
+}
+
+} // namespace blender::nodes
diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc
index 8fdad0bb242..528616eb23a 100644
--- a/source/blender/nodes/intern/node_socket.cc
+++ b/source/blender/nodes/intern/node_socket.cc
@@ -44,7 +44,6 @@
#include "MEM_guardedalloc.h"
-#include "NOD_node_tree_multi_function.hh"
#include "NOD_socket.h"
#include "FN_cpp_type_make.hh"
@@ -607,60 +606,74 @@ static bNodeSocketType *make_socket_type_virtual()
static bNodeSocketType *make_socket_type_bool()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_BOOLEAN, PROP_NONE);
- socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<bool>(); };
- socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get<bool>(); };
+ socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(bool *)r_value = ((bNodeSocketValueBoolean *)socket.default_value)->value;
};
+ socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type;
+ socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value;
return socktype;
}
static bNodeSocketType *make_socket_type_float(PropertySubType subtype)
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_FLOAT, subtype);
- socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<float>(); };
- socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get<float>(); };
+ socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(float *)r_value = ((bNodeSocketValueFloat *)socket.default_value)->value;
};
+ socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type;
+ socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value;
return socktype;
}
static bNodeSocketType *make_socket_type_int(PropertySubType subtype)
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_INT, subtype);
- socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<int>(); };
- socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get<int>(); };
+ socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(int *)r_value = ((bNodeSocketValueInt *)socket.default_value)->value;
};
+ socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type;
+ socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value;
return socktype;
}
static bNodeSocketType *make_socket_type_vector(PropertySubType subtype)
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_VECTOR, subtype);
- socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<blender::float3>(); };
- socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get<blender::float3>(); };
+ socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(blender::float3 *)r_value = ((bNodeSocketValueVector *)socket.default_value)->value;
};
+ socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type;
+ socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value;
return socktype;
}
static bNodeSocketType *make_socket_type_rgba()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_RGBA, PROP_NONE);
- socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<blender::ColorGeometry4f>(); };
- socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ socktype->get_base_cpp_type = []() {
+ return &blender::fn::CPPType::get<blender::ColorGeometry4f>();
+ };
+ socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(blender::ColorGeometry4f *)r_value = ((bNodeSocketValueRGBA *)socket.default_value)->value;
};
+ socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type;
+ socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value;
return socktype;
}
static bNodeSocketType *make_socket_type_string()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_STRING, PROP_NONE);
- socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<std::string>(); };
- socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get<std::string>(); };
+ socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
new (r_value) std::string(((bNodeSocketValueString *)socket.default_value)->value);
};
+ socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type;
+ socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value;
return socktype;
}
@@ -672,50 +685,60 @@ MAKE_CPP_TYPE(Material, Material *, CPPTypeFlags::BasicType)
static bNodeSocketType *make_socket_type_object()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_OBJECT, PROP_NONE);
- socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<Object *>(); };
- socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get<Object *>(); };
+ socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(Object **)r_value = ((bNodeSocketValueObject *)socket.default_value)->value;
};
+ socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type;
+ socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value;
return socktype;
}
static bNodeSocketType *make_socket_type_geometry()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_GEOMETRY, PROP_NONE);
- socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<GeometrySet>(); };
- socktype->get_cpp_value = [](const bNodeSocket &UNUSED(socket), void *r_value) {
+ socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get<GeometrySet>(); };
+ socktype->get_base_cpp_value = [](const bNodeSocket &UNUSED(socket), void *r_value) {
new (r_value) GeometrySet();
};
+ socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type;
+ socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value;
return socktype;
}
static bNodeSocketType *make_socket_type_collection()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_COLLECTION, PROP_NONE);
- socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<Collection *>(); };
- socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get<Collection *>(); };
+ socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(Collection **)r_value = ((bNodeSocketValueCollection *)socket.default_value)->value;
};
+ socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type;
+ socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value;
return socktype;
}
static bNodeSocketType *make_socket_type_texture()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_TEXTURE, PROP_NONE);
- socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<Tex *>(); };
- socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get<Tex *>(); };
+ socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(Tex **)r_value = ((bNodeSocketValueTexture *)socket.default_value)->value;
};
+ socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type;
+ socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value;
return socktype;
}
static bNodeSocketType *make_socket_type_material()
{
bNodeSocketType *socktype = make_standard_socket_type(SOCK_MATERIAL, PROP_NONE);
- socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<Material *>(); };
- socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) {
+ socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get<Material *>(); };
+ socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
*(Material **)r_value = ((bNodeSocketValueMaterial *)socket.default_value)->value;
};
+ socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type;
+ socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value;
return socktype;
}
diff --git a/source/blender/nodes/intern/node_tree_multi_function.cc b/source/blender/nodes/intern/node_tree_multi_function.cc
deleted file mode 100644
index 7ab6495f733..00000000000
--- a/source/blender/nodes/intern/node_tree_multi_function.cc
+++ /dev/null
@@ -1,409 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#include "NOD_node_tree_multi_function.hh"
-#include "NOD_type_conversions.hh"
-
-#include "FN_multi_function_network_evaluation.hh"
-
-#include "BLI_color.hh"
-#include "BLI_float2.hh"
-#include "BLI_float3.hh"
-
-namespace blender::nodes {
-
-const fn::MultiFunction &NodeMFNetworkBuilder::get_default_fn(StringRef name)
-{
- Vector<fn::MFDataType, 10> input_types;
- Vector<fn::MFDataType, 10> output_types;
-
- for (const InputSocketRef *dsocket : dnode_->inputs()) {
- if (dsocket->is_available()) {
- std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->typeinfo());
- if (data_type.has_value()) {
- input_types.append(*data_type);
- }
- }
- }
- for (const OutputSocketRef *dsocket : dnode_->outputs()) {
- if (dsocket->is_available()) {
- std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->typeinfo());
- if (data_type.has_value()) {
- output_types.append(*data_type);
- }
- }
- }
-
- const fn::MultiFunction &fn = this->construct_fn<fn::CustomMF_DefaultOutput>(
- name, input_types, output_types);
- return fn;
-}
-
-static void insert_dummy_node(CommonMFNetworkBuilderData &common, const DNode &dnode)
-{
- constexpr int stack_capacity = 10;
-
- Vector<fn::MFDataType, stack_capacity> input_types;
- Vector<StringRef, stack_capacity> input_names;
- Vector<const InputSocketRef *, stack_capacity> input_dsockets;
-
- for (const InputSocketRef *dsocket : dnode->inputs()) {
- if (dsocket->is_available()) {
- std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->bsocket()->typeinfo);
- if (data_type.has_value()) {
- input_types.append(*data_type);
- input_names.append(dsocket->name());
- input_dsockets.append(dsocket);
- }
- }
- }
-
- Vector<fn::MFDataType, stack_capacity> output_types;
- Vector<StringRef, stack_capacity> output_names;
- Vector<const OutputSocketRef *, stack_capacity> output_dsockets;
-
- for (const OutputSocketRef *dsocket : dnode->outputs()) {
- if (dsocket->is_available()) {
- std::optional<fn::MFDataType> data_type = socket_mf_type_get(*dsocket->bsocket()->typeinfo);
- if (data_type.has_value()) {
- output_types.append(*data_type);
- output_names.append(dsocket->name());
- output_dsockets.append(dsocket);
- }
- }
- }
-
- fn::MFDummyNode &dummy_node = common.network.add_dummy(
- dnode->name(), input_types, output_types, input_names, output_names);
-
- common.network_map.add(*dnode.context(), input_dsockets, dummy_node.inputs());
- common.network_map.add(*dnode.context(), output_dsockets, dummy_node.outputs());
-}
-
-static bool has_data_sockets(const DNode &dnode)
-{
- for (const InputSocketRef *socket : dnode->inputs()) {
- if (socket_is_mf_data_socket(*socket->bsocket()->typeinfo)) {
- return true;
- }
- }
- for (const OutputSocketRef *socket : dnode->outputs()) {
- if (socket_is_mf_data_socket(*socket->bsocket()->typeinfo)) {
- return true;
- }
- }
- return false;
-}
-
-static void foreach_node_to_insert(CommonMFNetworkBuilderData &common,
- FunctionRef<void(DNode)> callback)
-{
- common.tree.foreach_node([&](const DNode dnode) {
- if (dnode->is_group_node()) {
- return;
- }
- /* Don't insert non-root group input/output nodes, because they will be inlined. */
- if (!dnode.context()->is_root()) {
- if (dnode->is_group_input_node() || dnode->is_group_output_node()) {
- return;
- }
- }
- callback(dnode);
- });
-}
-
-/**
- * Expands all function nodes in the multi-function network. Nodes that don't have an expand
- * function, but do have data sockets, will get corresponding dummy nodes.
- */
-static void insert_nodes(CommonMFNetworkBuilderData &common)
-{
- foreach_node_to_insert(common, [&](const DNode dnode) {
- const bNodeType *node_type = dnode->typeinfo();
- if (node_type->expand_in_mf_network != nullptr) {
- NodeMFNetworkBuilder builder{common, dnode};
- node_type->expand_in_mf_network(builder);
- }
- else if (has_data_sockets(dnode)) {
- insert_dummy_node(common, dnode);
- }
- });
-}
-
-static fn::MFOutputSocket &insert_default_value_for_type(CommonMFNetworkBuilderData &common,
- fn::MFDataType type)
-{
- const fn::MultiFunction *default_fn;
- if (type.is_single()) {
- default_fn = &common.scope.construct<fn::CustomMF_GenericConstant>(
- AT, type.single_type(), type.single_type().default_value());
- }
- else {
- default_fn = &common.scope.construct<fn::CustomMF_GenericConstantArray>(
- AT, fn::GSpan(type.vector_base_type()));
- }
-
- fn::MFNode &node = common.network.add_function(*default_fn);
- return node.output(0);
-}
-
-static fn::MFOutputSocket *insert_unlinked_input(CommonMFNetworkBuilderData &common,
- const DInputSocket &dsocket)
-{
- BLI_assert(socket_is_mf_data_socket(*dsocket->typeinfo()));
-
- SocketMFNetworkBuilder builder{common, dsocket};
- socket_expand_in_mf_network(builder);
-
- fn::MFOutputSocket *built_socket = builder.built_socket();
- BLI_assert(built_socket != nullptr);
- return built_socket;
-}
-
-static void insert_links_and_unlinked_inputs(CommonMFNetworkBuilderData &common)
-{
- foreach_node_to_insert(common, [&](const DNode dnode) {
- for (const InputSocketRef *socket_ref : dnode->inputs()) {
- const DInputSocket to_dsocket{dnode.context(), socket_ref};
- if (!to_dsocket->is_available()) {
- continue;
- }
- if (!socket_is_mf_data_socket(*to_dsocket->typeinfo())) {
- continue;
- }
-
- Span<fn::MFInputSocket *> to_sockets = common.network_map.lookup(to_dsocket);
- BLI_assert(to_sockets.size() >= 1);
- const fn::MFDataType to_type = to_sockets[0]->data_type();
-
- Vector<DSocket> from_dsockets;
- to_dsocket.foreach_origin_socket([&](DSocket socket) { from_dsockets.append(socket); });
- if (from_dsockets.size() > 1) {
- fn::MFOutputSocket &from_socket = insert_default_value_for_type(common, to_type);
- for (fn::MFInputSocket *to_socket : to_sockets) {
- common.network.add_link(from_socket, *to_socket);
- }
- continue;
- }
- if (from_dsockets.is_empty()) {
- /* The socket is not linked. Need to use the value of the socket itself. */
- fn::MFOutputSocket *built_socket = insert_unlinked_input(common, to_dsocket);
- for (fn::MFInputSocket *to_socket : to_sockets) {
- common.network.add_link(*built_socket, *to_socket);
- }
- continue;
- }
- if (from_dsockets[0]->is_input()) {
- DInputSocket from_dsocket{from_dsockets[0]};
- fn::MFOutputSocket *built_socket = insert_unlinked_input(common, from_dsocket);
- for (fn::MFInputSocket *to_socket : to_sockets) {
- common.network.add_link(*built_socket, *to_socket);
- }
- continue;
- }
- DOutputSocket from_dsocket{from_dsockets[0]};
- fn::MFOutputSocket *from_socket = &common.network_map.lookup(from_dsocket);
- const fn::MFDataType from_type = from_socket->data_type();
-
- if (from_type != to_type) {
- const fn::MultiFunction *conversion_fn =
- get_implicit_type_conversions().get_conversion_multi_function(from_type, to_type);
- if (conversion_fn != nullptr) {
- fn::MFNode &node = common.network.add_function(*conversion_fn);
- common.network.add_link(*from_socket, node.input(0));
- from_socket = &node.output(0);
- }
- else {
- from_socket = &insert_default_value_for_type(common, to_type);
- }
- }
-
- for (fn::MFInputSocket *to_socket : to_sockets) {
- common.network.add_link(*from_socket, *to_socket);
- }
- }
- });
-}
-
-/**
- * Expands all function nodes contained in the given node tree within the given multi-function
- * network.
- *
- * Returns a mapping between the original node tree and the generated nodes/sockets for further
- * processing.
- */
-MFNetworkTreeMap insert_node_tree_into_mf_network(fn::MFNetwork &network,
- const DerivedNodeTree &tree,
- ResourceScope &scope)
-{
- MFNetworkTreeMap network_map{tree, network};
-
- CommonMFNetworkBuilderData common{scope, network, network_map, tree};
-
- insert_nodes(common);
- insert_links_and_unlinked_inputs(common);
-
- return network_map;
-}
-
-/**
- * A single node is allowed to expand into multiple nodes before evaluation. Depending on what
- * nodes it expands to, it belongs a different type of the ones below.
- */
-enum class NodeExpandType {
- SingleFunctionNode,
- MultipleFunctionNodes,
- HasDummyNodes,
-};
-
-/**
- * Checks how the given node expanded in the multi-function network. If it is only a single
- * function node, the corresponding function is returned as well.
- */
-static NodeExpandType get_node_expand_type(MFNetworkTreeMap &network_map,
- const DNode &dnode,
- const fn::MultiFunction **r_single_function)
-{
- const fn::MFFunctionNode *single_function_node = nullptr;
- bool has_multiple_nodes = false;
- bool has_dummy_nodes = false;
-
- auto check_mf_node = [&](fn::MFNode &mf_node) {
- if (mf_node.is_function()) {
- if (single_function_node == nullptr) {
- single_function_node = &mf_node.as_function();
- }
- if (&mf_node != single_function_node) {
- has_multiple_nodes = true;
- }
- }
- else {
- BLI_assert(mf_node.is_dummy());
- has_dummy_nodes = true;
- }
- };
-
- for (const InputSocketRef *dsocket : dnode->inputs()) {
- if (dsocket->is_available()) {
- for (fn::MFInputSocket *mf_input :
- network_map.lookup(DInputSocket(dnode.context(), dsocket))) {
- check_mf_node(mf_input->node());
- }
- }
- }
- for (const OutputSocketRef *dsocket : dnode->outputs()) {
- if (dsocket->is_available()) {
- fn::MFOutputSocket &mf_output = network_map.lookup(DOutputSocket(dnode.context(), dsocket));
- check_mf_node(mf_output.node());
- }
- }
-
- if (has_dummy_nodes) {
- return NodeExpandType::HasDummyNodes;
- }
- if (has_multiple_nodes) {
- return NodeExpandType::MultipleFunctionNodes;
- }
- *r_single_function = &single_function_node->function();
- return NodeExpandType::SingleFunctionNode;
-}
-
-static const fn::MultiFunction &create_function_for_node_that_expands_into_multiple(
- const DNode &dnode,
- fn::MFNetwork &network,
- MFNetworkTreeMap &network_map,
- ResourceScope &scope)
-{
- Vector<const fn::MFOutputSocket *> dummy_fn_inputs;
- for (const InputSocketRef *dsocket : dnode->inputs()) {
- if (dsocket->is_available()) {
- MFDataType data_type = *socket_mf_type_get(*dsocket->typeinfo());
- fn::MFOutputSocket &fn_input = network.add_input(data_type.to_string(), data_type);
- for (fn::MFInputSocket *mf_input :
- network_map.lookup(DInputSocket(dnode.context(), dsocket))) {
- network.add_link(fn_input, *mf_input);
- dummy_fn_inputs.append(&fn_input);
- }
- }
- }
- Vector<const fn::MFInputSocket *> dummy_fn_outputs;
- for (const OutputSocketRef *dsocket : dnode->outputs()) {
- if (dsocket->is_available()) {
- fn::MFOutputSocket &mf_output = network_map.lookup(DOutputSocket(dnode.context(), dsocket));
- MFDataType data_type = mf_output.data_type();
- fn::MFInputSocket &fn_output = network.add_output(data_type.to_string(), data_type);
- network.add_link(mf_output, fn_output);
- dummy_fn_outputs.append(&fn_output);
- }
- }
-
- fn::MFNetworkEvaluator &fn_evaluator = scope.construct<fn::MFNetworkEvaluator>(
- __func__, std::move(dummy_fn_inputs), std::move(dummy_fn_outputs));
- return fn_evaluator;
-}
-
-/**
- * Returns a single multi-function for every node that supports it. This makes it easier to reuse
- * the multi-function implementation of nodes in different contexts.
- */
-MultiFunctionByNode get_multi_function_per_node(const DerivedNodeTree &tree, ResourceScope &scope)
-{
- /* Build a network that nodes can insert themselves into. However, the individual nodes are not
- * connected. */
- fn::MFNetwork &network = scope.construct<fn::MFNetwork>(__func__);
- MFNetworkTreeMap network_map{tree, network};
- MultiFunctionByNode functions_by_node;
-
- CommonMFNetworkBuilderData common{scope, network, network_map, tree};
-
- tree.foreach_node([&](DNode dnode) {
- const bNodeType *node_type = dnode->typeinfo();
- if (node_type->expand_in_mf_network == nullptr) {
- /* This node does not have a multi-function implementation. */
- return;
- }
-
- NodeMFNetworkBuilder builder{common, dnode};
- node_type->expand_in_mf_network(builder);
-
- const fn::MultiFunction *single_function = nullptr;
- const NodeExpandType expand_type = get_node_expand_type(network_map, dnode, &single_function);
-
- switch (expand_type) {
- case NodeExpandType::HasDummyNodes: {
- /* Dummy nodes cannot be executed, so skip them. */
- break;
- }
- case NodeExpandType::SingleFunctionNode: {
- /* This is the common case. Most nodes just expand to a single function. */
- functions_by_node.add_new(dnode, single_function);
- break;
- }
- case NodeExpandType::MultipleFunctionNodes: {
- /* If a node expanded into multiple functions, a new function has to be created that
- * combines those. */
- const fn::MultiFunction &fn = create_function_for_node_that_expands_into_multiple(
- dnode, network, network_map, scope);
- functions_by_node.add_new(dnode, &fn);
- break;
- }
- }
- });
-
- return functions_by_node;
-}
-
-} // namespace blender::nodes
diff --git a/source/blender/nodes/intern/type_callbacks.cc b/source/blender/nodes/intern/type_callbacks.cc
deleted file mode 100644
index 5160432aea5..00000000000
--- a/source/blender/nodes/intern/type_callbacks.cc
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#include "NOD_node_tree_multi_function.hh"
-#include "NOD_type_callbacks.hh"
-
-namespace blender::nodes {
-
-const CPPType *socket_cpp_type_get(const bNodeSocketType &stype)
-{
- if (stype.get_cpp_type != nullptr) {
- return stype.get_cpp_type();
- }
- return nullptr;
-}
-
-std::optional<MFDataType> socket_mf_type_get(const bNodeSocketType &stype)
-{
- const CPPType *cpp_type = socket_cpp_type_get(stype);
- if (cpp_type != nullptr) {
- return MFDataType::ForSingle(*cpp_type);
- }
- return {};
-}
-
-bool socket_is_mf_data_socket(const bNodeSocketType &stype)
-{
- if (!socket_mf_type_get(stype).has_value()) {
- return false;
- }
- if (stype.expand_in_mf_network == nullptr && stype.get_cpp_value == nullptr) {
- return false;
- }
- return true;
-}
-
-bool socket_cpp_value_get(const bNodeSocket &socket, void *r_value)
-{
- if (socket.typeinfo->get_cpp_value != nullptr) {
- socket.typeinfo->get_cpp_value(socket, r_value);
- return true;
- }
- return false;
-}
-
-void socket_expand_in_mf_network(SocketMFNetworkBuilder &builder)
-{
- bNodeSocket &socket = builder.bsocket();
- if (socket.typeinfo->expand_in_mf_network != nullptr) {
- socket.typeinfo->expand_in_mf_network(builder);
- }
- else if (socket.typeinfo->get_cpp_value != nullptr) {
- const CPPType &type = *socket_cpp_type_get(*socket.typeinfo);
- void *buffer = builder.resource_scope().linear_allocator().allocate(type.size(),
- type.alignment());
- socket.typeinfo->get_cpp_value(socket, buffer);
- builder.set_constant_value(type, buffer);
- }
- else {
- BLI_assert_unreachable();
- }
-}
-
-} // namespace blender::nodes
diff --git a/source/blender/nodes/shader/node_shader_tree.c b/source/blender/nodes/shader/node_shader_tree.c
index 7367f73d171..98edc133a1d 100644
--- a/source/blender/nodes/shader/node_shader_tree.c
+++ b/source/blender/nodes/shader/node_shader_tree.c
@@ -531,6 +531,7 @@ static void ntree_shader_groups_flatten(bNodeTree *localtree)
bNodeTree *ngroup = (bNodeTree *)node->id;
ntreeFreeLocalNode(localtree, node);
ntreeFreeTree(ngroup);
+ BLI_assert(!ngroup->id.py_instance); /* Or call #BKE_libblock_free_data_py. */
MEM_freeN(ngroup);
}
else {
diff --git a/source/blender/nodes/shader/node_shader_util.h b/source/blender/nodes/shader/node_shader_util.h
index dc44f0fa98f..a75354d3381 100644
--- a/source/blender/nodes/shader/node_shader_util.h
+++ b/source/blender/nodes/shader/node_shader_util.h
@@ -72,7 +72,7 @@
#ifdef __cplusplus
# include "FN_multi_function_builder.hh"
-# include "NOD_node_tree_multi_function.hh"
+# include "NOD_multi_function.hh"
# include "BLI_color.hh"
# include "BLI_float3.hh"
diff --git a/source/blender/nodes/shader/nodes/node_shader_clamp.cc b/source/blender/nodes/shader/nodes/node_shader_clamp.cc
index 4f77421cfe0..f105f8bcaf9 100644
--- a/source/blender/nodes/shader/nodes/node_shader_clamp.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_clamp.cc
@@ -51,7 +51,7 @@ static int gpu_shader_clamp(GPUMaterial *mat,
GPU_stack_link(mat, node, "clamp_range", in, out);
}
-static void sh_node_clamp_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+static void sh_node_clamp_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder)
{
static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, float> minmax_fn{
"Clamp (Min Max)",
@@ -65,7 +65,7 @@ static void sh_node_clamp_expand_in_mf_network(blender::nodes::NodeMFNetworkBuil
return clamp_f(value, b, a);
}};
- int clamp_type = builder.bnode().custom1;
+ int clamp_type = builder.node().custom1;
if (clamp_type == NODE_CLAMP_MINMAX) {
builder.set_matching_fn(minmax_fn);
}
@@ -82,7 +82,7 @@ void register_node_type_sh_clamp(void)
node_type_socket_templates(&ntype, sh_node_clamp_in, sh_node_clamp_out);
node_type_init(&ntype, node_shader_init_clamp);
node_type_gpu(&ntype, gpu_shader_clamp);
- ntype.expand_in_mf_network = sh_node_clamp_expand_in_mf_network;
+ ntype.build_multi_function = sh_node_clamp_build_multi_function;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.cc b/source/blender/nodes/shader/nodes/node_shader_curves.cc
index f1d5040a292..df075d6e973 100644
--- a/source/blender/nodes/shader/nodes/node_shader_curves.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc
@@ -143,9 +143,10 @@ class CurveVecFunction : public blender::fn::MultiFunction {
}
};
-static void sh_node_curve_vec_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+static void sh_node_curve_vec_build_multi_function(
+ blender::nodes::NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.bnode();
+ bNode &bnode = builder.node();
CurveMapping *cumap = (CurveMapping *)bnode.storage;
BKE_curvemapping_init(cumap);
builder.construct_and_set_matching_fn<CurveVecFunction>(*cumap);
@@ -162,7 +163,7 @@ void register_node_type_sh_curve_vec(void)
node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves);
node_type_exec(&ntype, node_initexec_curves, nullptr, node_shader_exec_curve_vec);
node_type_gpu(&ntype, gpu_shader_curve_vec);
- ntype.expand_in_mf_network = sh_node_curve_vec_expand_in_mf_network;
+ ntype.build_multi_function = sh_node_curve_vec_build_multi_function;
nodeRegisterType(&ntype);
}
@@ -317,9 +318,10 @@ class CurveRGBFunction : public blender::fn::MultiFunction {
}
};
-static void sh_node_curve_rgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+static void sh_node_curve_rgb_build_multi_function(
+ blender::nodes::NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.bnode();
+ bNode &bnode = builder.node();
CurveMapping *cumap = (CurveMapping *)bnode.storage;
BKE_curvemapping_init(cumap);
builder.construct_and_set_matching_fn<CurveRGBFunction>(*cumap);
@@ -336,7 +338,7 @@ void register_node_type_sh_curve_rgb(void)
node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves);
node_type_exec(&ntype, node_initexec_curves, nullptr, node_shader_exec_curve_rgb);
node_type_gpu(&ntype, gpu_shader_curve_rgb);
- ntype.expand_in_mf_network = sh_node_curve_rgb_expand_in_mf_network;
+ ntype.build_multi_function = sh_node_curve_rgb_build_multi_function;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_eevee_specular.c b/source/blender/nodes/shader/nodes/node_shader_eevee_specular.c
index 56169b9be5e..015af19abb2 100644
--- a/source/blender/nodes/shader/nodes/node_shader_eevee_specular.c
+++ b/source/blender/nodes/shader/nodes/node_shader_eevee_specular.c
@@ -91,7 +91,7 @@ void register_node_type_sh_eevee_specular(void)
{
static bNodeType ntype;
- sh_node_type_base(&ntype, SH_NODE_EEVEE_SPECULAR, "Specular", NODE_CLASS_SHADER, 0);
+ sh_node_type_base(&ntype, SH_NODE_EEVEE_SPECULAR, "Specular BSDF", NODE_CLASS_SHADER, 0);
node_type_socket_templates(&ntype, sh_node_eevee_specular_in, sh_node_eevee_specular_out);
node_type_init(&ntype, NULL);
node_type_storage(&ntype, "", NULL, NULL);
diff --git a/source/blender/nodes/shader/nodes/node_shader_map_range.cc b/source/blender/nodes/shader/nodes/node_shader_map_range.cc
index ad7abd9d491..e4739e2864d 100644
--- a/source/blender/nodes/shader/nodes/node_shader_map_range.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_map_range.cc
@@ -261,9 +261,10 @@ class MapRangeSmootherstepFunction : public blender::fn::MultiFunction {
}
};
-static void sh_node_map_range_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+static void sh_node_map_range_build_multi_function(
+ blender::nodes::NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.bnode();
+ bNode &bnode = builder.node();
bool clamp = bnode.custom1 != 0;
int interpolation_type = bnode.custom2;
@@ -301,7 +302,6 @@ static void sh_node_map_range_expand_in_mf_network(blender::nodes::NodeMFNetwork
break;
}
default:
- builder.set_not_implemented();
break;
}
}
@@ -315,7 +315,7 @@ void register_node_type_sh_map_range(void)
node_type_init(&ntype, node_shader_init_map_range);
node_type_update(&ntype, node_shader_update_map_range);
node_type_gpu(&ntype, gpu_shader_map_range);
- ntype.expand_in_mf_network = sh_node_map_range_expand_in_mf_network;
+ ntype.build_multi_function = sh_node_map_range_build_multi_function;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_math.cc b/source/blender/nodes/shader/nodes/node_shader_math.cc
index 7a846031456..c30f2948ab1 100644
--- a/source/blender/nodes/shader/nodes/node_shader_math.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_math.cc
@@ -69,11 +69,9 @@ static int gpu_shader_math(GPUMaterial *mat,
return 0;
}
-static const blender::fn::MultiFunction &get_base_multi_function(
- blender::nodes::NodeMFNetworkBuilder &builder)
+static const blender::fn::MultiFunction *get_base_multi_function(bNode &node)
{
- const int mode = builder.bnode().custom1;
-
+ const int mode = node.custom1;
const blender::fn::MultiFunction *base_fn = nullptr;
blender::nodes::try_dispatch_float_math_fl_to_fl(
@@ -82,7 +80,7 @@ static const blender::fn::MultiFunction &get_base_multi_function(
base_fn = &fn;
});
if (base_fn != nullptr) {
- return *base_fn;
+ return base_fn;
}
blender::nodes::try_dispatch_float_math_fl_fl_to_fl(
@@ -92,7 +90,7 @@ static const blender::fn::MultiFunction &get_base_multi_function(
base_fn = &fn;
});
if (base_fn != nullptr) {
- return *base_fn;
+ return base_fn;
}
blender::nodes::try_dispatch_float_math_fl_fl_fl_to_fl(
@@ -102,36 +100,51 @@ static const blender::fn::MultiFunction &get_base_multi_function(
base_fn = &fn;
});
if (base_fn != nullptr) {
- return *base_fn;
+ return base_fn;
}
- return builder.get_not_implemented_fn();
+ return nullptr;
}
-static void sh_node_math_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
-{
- const blender::fn::MultiFunction &base_function = get_base_multi_function(builder);
+class ClampWrapperFunction : public blender::fn::MultiFunction {
+ private:
+ const blender::fn::MultiFunction &fn_;
- const blender::nodes::DNode &dnode = builder.dnode();
- blender::fn::MFNetwork &network = builder.network();
- blender::fn::MFFunctionNode &base_node = network.add_function(base_function);
+ public:
+ ClampWrapperFunction(const blender::fn::MultiFunction &fn) : fn_(fn)
+ {
+ this->set_signature(&fn.signature());
+ }
- builder.network_map().add_try_match(*dnode.context(), dnode->inputs(), base_node.inputs());
+ void call(blender::IndexMask mask,
+ blender::fn::MFParams params,
+ blender::fn::MFContext context) const override
+ {
+ fn_.call(mask, params, context);
+
+ /* Assumes the output parameter is the last one. */
+ const int output_param_index = this->param_amount() - 1;
+ /* This has actually been initialized in the call above. */
+ blender::MutableSpan<float> results = params.uninitialized_single_output<float>(
+ output_param_index);
+
+ for (const int i : mask) {
+ float &value = results[i];
+ CLAMP(value, 0.0f, 1.0f);
+ }
+ }
+};
+
+static void sh_node_math_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder)
+{
+ const blender::fn::MultiFunction *base_function = get_base_multi_function(builder.node());
- const bool clamp_output = builder.bnode().custom2 != 0;
+ const bool clamp_output = builder.node().custom2 != 0;
if (clamp_output) {
- static blender::fn::CustomMF_SI_SO<float, float> clamp_fn{"Clamp", [](float value) {
- CLAMP(value, 0.0f, 1.0f);
- return value;
- }};
- blender::fn::MFFunctionNode &clamp_node = network.add_function(clamp_fn);
- network.add_link(base_node.output(0), clamp_node.input(0));
- builder.network_map().add(blender::nodes::DOutputSocket(dnode.context(), &dnode->output(0)),
- clamp_node.output(0));
+ builder.construct_and_set_matching_fn<ClampWrapperFunction>(*base_function);
}
else {
- builder.network_map().add(blender::nodes::DOutputSocket(dnode.context(), &dnode->output(0)),
- base_node.output(0));
+ builder.set_matching_fn(base_function);
}
}
@@ -144,7 +157,7 @@ void register_node_type_sh_math(void)
node_type_label(&ntype, node_math_label);
node_type_gpu(&ntype, gpu_shader_math);
node_type_update(&ntype, node_math_update);
- ntype.expand_in_mf_network = sh_node_math_expand_in_mf_network;
+ ntype.build_multi_function = sh_node_math_build_multi_function;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc
index 6baaa17f956..ade35a40366 100644
--- a/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc
@@ -127,15 +127,71 @@ static int gpu_shader_mix_rgb(GPUMaterial *mat,
return 0;
}
+class MixRGBFunction : public blender::fn::MultiFunction {
+ private:
+ bool clamp_;
+ int type_;
+
+ public:
+ MixRGBFunction(bool clamp, int type) : clamp_(clamp), type_(type)
+ {
+ static blender::fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static blender::fn::MFSignature create_signature()
+ {
+ blender::fn::MFSignatureBuilder signature{"MixRGB"};
+ signature.single_input<float>("Fac");
+ signature.single_input<blender::ColorGeometry4f>("Color1");
+ signature.single_input<blender::ColorGeometry4f>("Color2");
+ signature.single_output<blender::ColorGeometry4f>("Color");
+ return signature.build();
+ }
+
+ void call(blender::IndexMask mask,
+ blender::fn::MFParams params,
+ blender::fn::MFContext UNUSED(context)) const override
+ {
+ const blender::VArray<float> &fac = params.readonly_single_input<float>(0, "Fac");
+ const blender::VArray<blender::ColorGeometry4f> &col1 =
+ params.readonly_single_input<blender::ColorGeometry4f>(1, "Color1");
+ const blender::VArray<blender::ColorGeometry4f> &col2 =
+ params.readonly_single_input<blender::ColorGeometry4f>(2, "Color2");
+ blender::MutableSpan<blender::ColorGeometry4f> results =
+ params.uninitialized_single_output<blender::ColorGeometry4f>(3, "Color");
+
+ for (int64_t i : mask) {
+ results[i] = col1[i];
+ ramp_blend(type_, results[i], clamp_f(fac[i], 0.0f, 1.0f), col2[i]);
+ }
+
+ if (clamp_) {
+ for (int64_t i : mask) {
+ clamp_v3(results[i], 0.0f, 1.0f);
+ }
+ }
+ }
+};
+
+static void sh_node_mix_rgb_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder)
+{
+ bNode &node = builder.node();
+ bool clamp = node.custom2 & SHD_MIXRGB_CLAMP;
+ int mix_type = node.custom1;
+ builder.construct_and_set_matching_fn<MixRGBFunction>(clamp, mix_type);
+}
+
void register_node_type_sh_mix_rgb(void)
{
static bNodeType ntype;
- sh_node_type_base(&ntype, SH_NODE_MIX_RGB, "Mix", NODE_CLASS_OP_COLOR, 0);
+ sh_fn_node_type_base(&ntype, SH_NODE_MIX_RGB, "Mix", NODE_CLASS_OP_COLOR, 0);
node_type_socket_templates(&ntype, sh_node_mix_rgb_in, sh_node_mix_rgb_out);
node_type_label(&ntype, node_blend_label);
node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_mix_rgb);
node_type_gpu(&ntype, gpu_shader_mix_rgb);
+ ntype.build_multi_function = sh_node_mix_rgb_build_multi_function;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc
index a7239154633..2779fc6bf68 100644
--- a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc
@@ -96,7 +96,7 @@ class SeparateRGBFunction : public blender::fn::MultiFunction {
}
};
-static void sh_node_seprgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+static void sh_node_seprgb_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder)
{
static SeparateRGBFunction fn;
builder.set_matching_fn(fn);
@@ -110,7 +110,7 @@ void register_node_type_sh_seprgb(void)
node_type_socket_templates(&ntype, sh_node_seprgb_in, sh_node_seprgb_out);
node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_seprgb);
node_type_gpu(&ntype, gpu_shader_seprgb);
- ntype.expand_in_mf_network = sh_node_seprgb_expand_in_mf_network;
+ ntype.build_multi_function = sh_node_seprgb_build_multi_function;
nodeRegisterType(&ntype);
}
@@ -153,7 +153,7 @@ static int gpu_shader_combrgb(GPUMaterial *mat,
return GPU_stack_link(mat, node, "combine_rgb", in, out);
}
-static void sh_node_combrgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+static void sh_node_combrgb_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder)
{
static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, blender::ColorGeometry4f> fn{
"Combine RGB",
@@ -169,7 +169,7 @@ void register_node_type_sh_combrgb(void)
node_type_socket_templates(&ntype, sh_node_combrgb_in, sh_node_combrgb_out);
node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_combrgb);
node_type_gpu(&ntype, gpu_shader_combrgb);
- ntype.expand_in_mf_network = sh_node_combrgb_expand_in_mf_network;
+ ntype.build_multi_function = sh_node_combrgb_build_multi_function;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc
index efa9581c414..1fd794cdd0a 100644
--- a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc
@@ -81,7 +81,7 @@ class MF_SeparateXYZ : public blender::fn::MultiFunction {
}
};
-static void sh_node_sepxyz_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+static void sh_node_sepxyz_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder)
{
static MF_SeparateXYZ separate_fn;
builder.set_matching_fn(separate_fn);
@@ -94,7 +94,7 @@ void register_node_type_sh_sepxyz(void)
sh_fn_node_type_base(&ntype, SH_NODE_SEPXYZ, "Separate XYZ", NODE_CLASS_CONVERTOR, 0);
node_type_socket_templates(&ntype, sh_node_sepxyz_in, sh_node_sepxyz_out);
node_type_gpu(&ntype, gpu_shader_sepxyz);
- ntype.expand_in_mf_network = sh_node_sepxyz_expand_in_mf_network;
+ ntype.build_multi_function = sh_node_sepxyz_build_multi_function;
nodeRegisterType(&ntype);
}
@@ -120,7 +120,7 @@ static int gpu_shader_combxyz(GPUMaterial *mat,
return GPU_stack_link(mat, node, "combine_xyz", in, out);
}
-static void sh_node_combxyz_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+static void sh_node_combxyz_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder)
{
static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, blender::float3> fn{
"Combine Vector", [](float x, float y, float z) { return blender::float3(x, y, z); }};
@@ -134,7 +134,7 @@ void register_node_type_sh_combxyz(void)
sh_fn_node_type_base(&ntype, SH_NODE_COMBXYZ, "Combine XYZ", NODE_CLASS_CONVERTOR, 0);
node_type_socket_templates(&ntype, sh_node_combxyz_in, sh_node_combxyz_out);
node_type_gpu(&ntype, gpu_shader_combxyz);
- ntype.expand_in_mf_network = sh_node_combxyz_expand_in_mf_network;
+ ntype.build_multi_function = sh_node_combxyz_build_multi_function;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc
index 5b2eb300aac..1bc42ab0cc6 100644
--- a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc
@@ -163,9 +163,10 @@ class ColorBandFunction : public blender::fn::MultiFunction {
}
};
-static void sh_node_valtorgb_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+static void sh_node_valtorgb_build_multi_function(
+ blender::nodes::NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.bnode();
+ bNode &bnode = builder.node();
const ColorBand *color_band = (const ColorBand *)bnode.storage;
builder.construct_and_set_matching_fn<ColorBandFunction>(*color_band);
}
@@ -181,7 +182,7 @@ void register_node_type_sh_valtorgb(void)
node_type_storage(&ntype, "ColorBand", node_free_standard_storage, node_copy_standard_storage);
node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_valtorgb);
node_type_gpu(&ntype, gpu_shader_valtorgb);
- ntype.expand_in_mf_network = sh_node_valtorgb_expand_in_mf_network;
+ ntype.build_multi_function = sh_node_valtorgb_build_multi_function;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_value.cc b/source/blender/nodes/shader/nodes/node_shader_value.cc
index 495c8d12824..602d5a1cf56 100644
--- a/source/blender/nodes/shader/nodes/node_shader_value.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_value.cc
@@ -39,9 +39,9 @@ static int gpu_shader_value(GPUMaterial *mat,
return GPU_stack_link(mat, node, "set_value", in, out, link);
}
-static void sh_node_value_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+static void sh_node_value_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder)
{
- const bNodeSocket *bsocket = builder.dnode()->output(0).bsocket();
+ const bNodeSocket *bsocket = (bNodeSocket *)builder.node().outputs.first;
const bNodeSocketValueFloat *value = (const bNodeSocketValueFloat *)bsocket->default_value;
builder.construct_and_set_matching_fn<blender::fn::CustomMF_Constant<float>>(value->value);
}
@@ -53,7 +53,7 @@ void register_node_type_sh_value(void)
sh_fn_node_type_base(&ntype, SH_NODE_VALUE, "Value", NODE_CLASS_INPUT, 0);
node_type_socket_templates(&ntype, nullptr, sh_node_value_out);
node_type_gpu(&ntype, gpu_shader_value);
- ntype.expand_in_mf_network = sh_node_value_expand_in_mf_network;
+ ntype.build_multi_function = sh_node_value_build_multi_function;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc
index 419a11201aa..4424db6aed1 100644
--- a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc
@@ -183,12 +183,11 @@ static void node_shader_update_vector_math(bNodeTree *UNUSED(ntree), bNode *node
}
}
-static const blender::fn::MultiFunction &get_multi_function(
- blender::nodes::NodeMFNetworkBuilder &builder)
+static const blender::fn::MultiFunction *get_multi_function(bNode &node)
{
using blender::float3;
- NodeVectorMathOperation operation = NodeVectorMathOperation(builder.bnode().custom1);
+ NodeVectorMathOperation operation = NodeVectorMathOperation(node.custom1);
const blender::fn::MultiFunction *multi_fn = nullptr;
@@ -199,7 +198,7 @@ static const blender::fn::MultiFunction &get_multi_function(
multi_fn = &fn;
});
if (multi_fn != nullptr) {
- return *multi_fn;
+ return multi_fn;
}
blender::nodes::try_dispatch_float_math_fl3_fl3_fl3_to_fl3(
@@ -209,7 +208,7 @@ static const blender::fn::MultiFunction &get_multi_function(
multi_fn = &fn;
});
if (multi_fn != nullptr) {
- return *multi_fn;
+ return multi_fn;
}
blender::nodes::try_dispatch_float_math_fl3_fl3_fl_to_fl3(
@@ -219,7 +218,7 @@ static const blender::fn::MultiFunction &get_multi_function(
multi_fn = &fn;
});
if (multi_fn != nullptr) {
- return *multi_fn;
+ return multi_fn;
}
blender::nodes::try_dispatch_float_math_fl3_fl3_to_fl(
@@ -229,7 +228,7 @@ static const blender::fn::MultiFunction &get_multi_function(
multi_fn = &fn;
});
if (multi_fn != nullptr) {
- return *multi_fn;
+ return multi_fn;
}
blender::nodes::try_dispatch_float_math_fl3_fl_to_fl3(
@@ -239,7 +238,7 @@ static const blender::fn::MultiFunction &get_multi_function(
multi_fn = &fn;
});
if (multi_fn != nullptr) {
- return *multi_fn;
+ return multi_fn;
}
blender::nodes::try_dispatch_float_math_fl3_to_fl3(
@@ -248,7 +247,7 @@ static const blender::fn::MultiFunction &get_multi_function(
multi_fn = &fn;
});
if (multi_fn != nullptr) {
- return *multi_fn;
+ return multi_fn;
}
blender::nodes::try_dispatch_float_math_fl3_to_fl(
@@ -257,15 +256,16 @@ static const blender::fn::MultiFunction &get_multi_function(
multi_fn = &fn;
});
if (multi_fn != nullptr) {
- return *multi_fn;
+ return multi_fn;
}
- return builder.get_not_implemented_fn();
+ return nullptr;
}
-static void sh_node_vector_math_expand_in_mf_network(blender::nodes::NodeMFNetworkBuilder &builder)
+static void sh_node_vector_math_build_multi_function(
+ blender::nodes::NodeMultiFunctionBuilder &builder)
{
- const blender::fn::MultiFunction &fn = get_multi_function(builder);
+ const blender::fn::MultiFunction *fn = get_multi_function(builder.node());
builder.set_matching_fn(fn);
}
@@ -278,7 +278,7 @@ void register_node_type_sh_vect_math(void)
node_type_label(&ntype, node_vector_math_label);
node_type_gpu(&ntype, gpu_shader_vector_math);
node_type_update(&ntype, node_shader_update_vector_math);
- ntype.expand_in_mf_network = sh_node_vector_math_expand_in_mf_network;
+ ntype.build_multi_function = sh_node_vector_math_build_multi_function;
nodeRegisterType(&ntype);
}
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 3b2c2fa5a03..bc51b7e29ea 100644
--- a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc
@@ -100,11 +100,10 @@ static float3 sh_node_vector_rotate_euler(const float3 vector,
return result + center;
}
-static const blender::fn::MultiFunction &get_multi_function(
- blender::nodes::NodeMFNetworkBuilder &builder)
+static const blender::fn::MultiFunction *get_multi_function(bNode &node)
{
- bool invert = builder.bnode().custom2;
- const int mode = builder.bnode().custom1;
+ bool invert = node.custom2;
+ const int mode = node.custom1;
switch (mode) {
case NODE_VECTOR_ROTATE_TYPE_AXIS: {
@@ -113,13 +112,13 @@ static const blender::fn::MultiFunction &get_multi_function(
"Rotate Axis", [](float3 in, float3 center, float3 axis, float angle) {
return sh_node_vector_rotate_around_axis(in, center, axis, -angle);
}};
- return fn;
+ return &fn;
}
static blender::fn::CustomMF_SI_SI_SI_SI_SO<float3, float3, float3, float, float3> fn{
"Rotate Axis", [](float3 in, float3 center, float3 axis, float angle) {
return sh_node_vector_rotate_around_axis(in, center, axis, angle);
}};
- return fn;
+ return &fn;
}
case NODE_VECTOR_ROTATE_TYPE_AXIS_X: {
float3 axis = float3(1.0f, 0.0f, 0.0f);
@@ -128,13 +127,13 @@ static const blender::fn::MultiFunction &get_multi_function(
"Rotate X-Axis", [=](float3 in, float3 center, float angle) {
return sh_node_vector_rotate_around_axis(in, center, axis, -angle);
}};
- return fn;
+ return &fn;
}
static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{
"Rotate X-Axis", [=](float3 in, float3 center, float angle) {
return sh_node_vector_rotate_around_axis(in, center, axis, angle);
}};
- return fn;
+ return &fn;
}
case NODE_VECTOR_ROTATE_TYPE_AXIS_Y: {
float3 axis = float3(0.0f, 1.0f, 0.0f);
@@ -143,13 +142,13 @@ static const blender::fn::MultiFunction &get_multi_function(
"Rotate Y-Axis", [=](float3 in, float3 center, float angle) {
return sh_node_vector_rotate_around_axis(in, center, axis, -angle);
}};
- return fn;
+ return &fn;
}
static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{
"Rotate Y-Axis", [=](float3 in, float3 center, float angle) {
return sh_node_vector_rotate_around_axis(in, center, axis, angle);
}};
- return fn;
+ return &fn;
}
case NODE_VECTOR_ROTATE_TYPE_AXIS_Z: {
float3 axis = float3(0.0f, 0.0f, 1.0f);
@@ -158,13 +157,13 @@ static const blender::fn::MultiFunction &get_multi_function(
"Rotate Z-Axis", [=](float3 in, float3 center, float angle) {
return sh_node_vector_rotate_around_axis(in, center, axis, -angle);
}};
- return fn;
+ return &fn;
}
static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{
"Rotate Z-Axis", [=](float3 in, float3 center, float angle) {
return sh_node_vector_rotate_around_axis(in, center, axis, angle);
}};
- return fn;
+ return &fn;
}
case NODE_VECTOR_ROTATE_TYPE_EULER_XYZ: {
if (invert) {
@@ -172,24 +171,24 @@ static const blender::fn::MultiFunction &get_multi_function(
"Rotate Euler", [](float3 in, float3 center, float3 rotation) {
return sh_node_vector_rotate_euler(in, center, rotation, true);
}};
- return fn;
+ return &fn;
}
static blender::fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{
"Rotate Euler", [](float3 in, float3 center, float3 rotation) {
return sh_node_vector_rotate_euler(in, center, rotation, false);
}};
- return fn;
+ return &fn;
}
default:
BLI_assert_unreachable();
- return builder.get_not_implemented_fn();
+ return nullptr;
}
}
-static void sh_node_vector_rotate_expand_in_mf_network(
- blender::nodes::NodeMFNetworkBuilder &builder)
+static void sh_node_vector_rotate_build_multi_function(
+ blender::nodes::NodeMultiFunctionBuilder &builder)
{
- const blender::fn::MultiFunction &fn = get_multi_function(builder);
+ const blender::fn::MultiFunction *fn = get_multi_function(builder.node());
builder.set_matching_fn(fn);
}
@@ -211,7 +210,7 @@ void register_node_type_sh_vector_rotate(void)
node_type_socket_templates(&ntype, sh_node_vector_rotate_in, sh_node_vector_rotate_out);
node_type_gpu(&ntype, gpu_shader_vector_rotate);
node_type_update(&ntype, node_shader_update_vector_rotate);
- ntype.expand_in_mf_network = sh_node_vector_rotate_expand_in_mf_network;
+ ntype.build_multi_function = sh_node_vector_rotate_build_multi_function;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c
index 612446e6d19..e5e601e0eb6 100644
--- a/source/blender/python/bmesh/bmesh_py_types.c
+++ b/source/blender/python/bmesh/bmesh_py_types.c
@@ -3241,9 +3241,11 @@ static PyObject *bpy_bmelemseq_subscript(BPy_BMElemSeq *self, PyObject *key)
const Py_ssize_t len = bpy_bmelemseq_length(self);
if (start < 0) {
start += len;
+ CLAMP_MIN(start, 0);
}
if (stop < 0) {
stop += len;
+ CLAMP_MIN(stop, 0);
}
}
diff --git a/source/blender/python/bmesh/bmesh_py_types_customdata.c b/source/blender/python/bmesh/bmesh_py_types_customdata.c
index ff06cf43026..0aa92158524 100644
--- a/source/blender/python/bmesh/bmesh_py_types_customdata.c
+++ b/source/blender/python/bmesh/bmesh_py_types_customdata.c
@@ -843,9 +843,11 @@ static PyObject *bpy_bmlayercollection_subscript(BPy_BMLayerCollection *self, Py
const Py_ssize_t len = bpy_bmlayercollection_length(self);
if (start < 0) {
start += len;
+ CLAMP_MIN(start, 0);
}
if (stop < 0) {
stop += len;
+ CLAMP_MIN(stop, 0);
}
}
diff --git a/source/blender/python/bmesh/bmesh_py_types_meshdata.c b/source/blender/python/bmesh/bmesh_py_types_meshdata.c
index 8f4e07c30d3..d0c745e6a1d 100644
--- a/source/blender/python/bmesh/bmesh_py_types_meshdata.c
+++ b/source/blender/python/bmesh/bmesh_py_types_meshdata.c
@@ -443,7 +443,7 @@ static int bpy_bmdeformvert_ass_subscript(BPy_BMDeformVert *self, PyObject *key,
}
if (value) {
- /* dvert[group_index] = 0.5 */
+ /* Handle `dvert[group_index] = 0.5`. */
if (i < 0) {
PyErr_SetString(PyExc_KeyError,
"BMDeformVert[key] = x: "
@@ -453,7 +453,7 @@ static int bpy_bmdeformvert_ass_subscript(BPy_BMDeformVert *self, PyObject *key,
MDeformWeight *dw = BKE_defvert_ensure_index(self->data, i);
const float f = PyFloat_AsDouble(value);
- if (f == -1 && PyErr_Occurred()) { // parsed key not a number
+ if (f == -1 && PyErr_Occurred()) { /* Parsed key not a number. */
PyErr_SetString(PyExc_TypeError,
"BMDeformVert[key] = x: "
"assigned value not a number");
@@ -463,7 +463,7 @@ static int bpy_bmdeformvert_ass_subscript(BPy_BMDeformVert *self, PyObject *key,
dw->weight = clamp_f(f, 0.0f, 1.0f);
}
else {
- /* del dvert[group_index] */
+ /* Handle `del dvert[group_index]`. */
MDeformWeight *dw = BKE_defvert_find_index(self->data, i);
if (dw == NULL) {
diff --git a/source/blender/python/bmesh/bmesh_py_types_select.c b/source/blender/python/bmesh/bmesh_py_types_select.c
index 9bb9815f731..b89822a080c 100644
--- a/source/blender/python/bmesh/bmesh_py_types_select.c
+++ b/source/blender/python/bmesh/bmesh_py_types_select.c
@@ -205,7 +205,6 @@ static PyObject *bpy_bmeditselseq_subscript_slice(BPy_BMEditSelSeq *self,
Py_ssize_t stop)
{
int count = 0;
- bool ok;
PyObject *list;
BMEditSelection *ese;
@@ -214,30 +213,22 @@ static PyObject *bpy_bmeditselseq_subscript_slice(BPy_BMEditSelSeq *self,
list = PyList_New(0);
- ese = self->bm->selected.first;
-
- ok = (ese != NULL);
-
- if (UNLIKELY(ok == false)) {
- return list;
- }
-
- /* first loop up-until the start */
- for (ok = true; ok; ok = ((ese = ese->next) != NULL)) {
+ /* First loop up-until the start. */
+ for (ese = self->bm->selected.first; ese; ese = ese->next) {
if (count == start) {
break;
}
count++;
}
- /* add items until stop */
- do {
+ /* Add items until stop. */
+ for (; ese; ese = ese->next) {
PyList_APPEND(list, BPy_BMElem_CreatePyObject(self->bm, &ese->ele->head));
count++;
if (count == stop) {
break;
}
- } while ((ese = ese->next));
+ }
return list;
}
@@ -282,9 +273,11 @@ static PyObject *bpy_bmeditselseq_subscript(BPy_BMEditSelSeq *self, PyObject *ke
const Py_ssize_t len = bpy_bmeditselseq_length(self);
if (start < 0) {
start += len;
+ CLAMP_MIN(start, 0);
}
if (stop < 0) {
stop += len;
+ CLAMP_MIN(stop, 0);
}
}
diff --git a/source/blender/python/generic/blf_py_api.c b/source/blender/python/generic/blf_py_api.c
index 0ec66e22fa9..9e725730d40 100644
--- a/source/blender/python/generic/blf_py_api.c
+++ b/source/blender/python/generic/blf_py_api.c
@@ -493,7 +493,6 @@ PyObject *BPyInit_blf(void)
PyModule_AddIntConstant(submodule, "ROTATION", BLF_ROTATION);
PyModule_AddIntConstant(submodule, "CLIPPING", BLF_CLIPPING);
PyModule_AddIntConstant(submodule, "SHADOW", BLF_SHADOW);
- PyModule_AddIntConstant(submodule, "KERNING_DEFAULT", BLF_KERNING_DEFAULT);
PyModule_AddIntConstant(submodule, "WORD_WRAP", BLF_WORD_WRAP);
PyModule_AddIntConstant(submodule, "MONOCHROME", BLF_MONOCHROME);
diff --git a/source/blender/python/generic/py_capi_utils.h b/source/blender/python/generic/py_capi_utils.h
index 1591413530c..bfe0e66e393 100644
--- a/source/blender/python/generic/py_capi_utils.h
+++ b/source/blender/python/generic/py_capi_utils.h
@@ -42,13 +42,13 @@ void PyC_Err_PrintWithFunc(PyObject *py_func);
void PyC_FileAndNum(const char **r_filename, int *r_lineno);
void PyC_FileAndNum_Safe(const char **r_filename, int *r_lineno); /* checks python is running */
int PyC_AsArray_FAST(void *array,
- const size_t array_elem_size,
+ const size_t array_item_size,
PyObject *value_fast,
const Py_ssize_t length,
const PyTypeObject *type,
const char *error_prefix);
int PyC_AsArray(void *array,
- const size_t array_elem_size,
+ const size_t array_item_size,
PyObject *value,
const Py_ssize_t length,
const PyTypeObject *type,
diff --git a/source/blender/python/gpu/gpu_py_buffer.c b/source/blender/python/gpu/gpu_py_buffer.c
index a1fc89e772e..0fef59d6352 100644
--- a/source/blender/python/gpu/gpu_py_buffer.c
+++ b/source/blender/python/gpu/gpu_py_buffer.c
@@ -37,7 +37,7 @@
#include "gpu_py_buffer.h"
-//#define PYGPU_BUFFER_PROTOCOL
+#define PYGPU_BUFFER_PROTOCOL
#define MAX_DIMENSIONS 64
/* -------------------------------------------------------------------- */
@@ -608,7 +608,7 @@ 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 flags)
+static int pygpu_buffer__bf_getbuffer(BPyGPUBuffer *self, Py_buffer *view, int UNUSED(flags))
{
if (view == NULL) {
PyErr_SetString(PyExc_ValueError, "NULL view in getbuffer");
@@ -620,7 +620,7 @@ static int pygpu_buffer__bf_getbuffer(BPyGPUBuffer *self, Py_buffer *view, int f
view->len = bpygpu_Buffer_size(self);
view->readonly = 0;
view->itemsize = GPU_texture_dataformat_size(self->format);
- view->format = pygpu_buffer_formatstr(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");
diff --git a/source/blender/python/gpu/gpu_py_capabilities.c b/source/blender/python/gpu/gpu_py_capabilities.c
index f3fb93021b2..11e7d48f096 100644
--- a/source/blender/python/gpu/gpu_py_capabilities.c
+++ b/source/blender/python/gpu/gpu_py_capabilities.c
@@ -33,66 +33,166 @@
/** \name Functions
* \{ */
+PyDoc_STRVAR(pygpu_max_texture_size_get_doc,
+ ".. function:: max_texture_size_get()\n"
+ "\n"
+ " Get estimated maximum texture size to be able to handle.\n"
+ "\n"
+ " :return: Texture size.\n"
+ " :rtype: int\n");
static PyObject *pygpu_max_texture_size_get(PyObject *UNUSED(self))
{
return PyLong_FromLong(GPU_max_texture_size());
}
+PyDoc_STRVAR(pygpu_max_texture_layers_get_doc,
+ ".. function:: max_texture_layers_get()\n"
+ "\n"
+ " Get maximum number of layers in texture.\n"
+ "\n"
+ " :return: Number of layers.\n"
+ " :rtype: int\n");
static PyObject *pygpu_max_texture_layers_get(PyObject *UNUSED(self))
{
return PyLong_FromLong(GPU_max_texture_layers());
}
+PyDoc_STRVAR(pygpu_max_textures_get_doc,
+ ".. function:: max_textures_get()\n"
+ "\n"
+ " Get maximum supported texture image units used for\n"
+ " accessing texture maps from the vertex shader and the\n"
+ " fragment processor.\n"
+ "\n"
+ " :return: Texture image units.\n"
+ " :rtype: int\n");
static PyObject *pygpu_max_textures_get(PyObject *UNUSED(self))
{
return PyLong_FromLong(GPU_max_textures());
}
+PyDoc_STRVAR(pygpu_max_textures_vert_get_doc,
+ ".. function:: max_textures_vert_get()\n"
+ "\n"
+ " Get maximum supported texture image units used for\n"
+ " accessing texture maps from the vertex shader.\n"
+ "\n"
+ " :return: Texture image units.\n"
+ " :rtype: int\n");
static PyObject *pygpu_max_textures_vert_get(PyObject *UNUSED(self))
{
return PyLong_FromLong(GPU_max_textures_vert());
}
+PyDoc_STRVAR(pygpu_max_textures_geom_get_doc,
+ ".. function:: max_textures_geom_get()\n"
+ "\n"
+ " Get maximum supported texture image units used for\n"
+ " accessing texture maps from the geometry shader.\n"
+ "\n"
+ " :return: Texture image units.\n"
+ " :rtype: int\n");
static PyObject *pygpu_max_textures_geom_get(PyObject *UNUSED(self))
{
return PyLong_FromLong(GPU_max_textures_geom());
}
+PyDoc_STRVAR(pygpu_max_textures_frag_get_doc,
+ ".. function:: max_textures_frag_get()\n"
+ "\n"
+ " Get maximum supported texture image units used for\n"
+ " accessing texture maps from the fragment shader.\n"
+ "\n"
+ " :return: Texture image units.\n"
+ " :rtype: int\n");
static PyObject *pygpu_max_textures_frag_get(PyObject *UNUSED(self))
{
return PyLong_FromLong(GPU_max_textures_frag());
}
+PyDoc_STRVAR(pygpu_max_uniforms_vert_get_doc,
+ ".. function:: max_uniforms_vert_get()\n"
+ "\n"
+ " Get maximum number of values held in uniform variable\n"
+ " storage for a vertex shader.\n"
+ "\n"
+ " :return: Number of values.\n"
+ " :rtype: int\n");
static PyObject *pygpu_max_uniforms_vert_get(PyObject *UNUSED(self))
{
return PyLong_FromLong(GPU_max_uniforms_vert());
}
+PyDoc_STRVAR(pygpu_max_uniforms_frag_get_doc,
+ ".. function:: max_uniforms_frag_get()\n"
+ "\n"
+ " Get maximum number of values held in uniform variable\n"
+ " storage for a fragment shader.\n"
+ "\n"
+ " :return: Number of values.\n"
+ " :rtype: int\n");
static PyObject *pygpu_max_uniforms_frag_get(PyObject *UNUSED(self))
{
return PyLong_FromLong(GPU_max_uniforms_frag());
}
+PyDoc_STRVAR(pygpu_max_batch_indices_get_doc,
+ ".. function:: max_batch_indices_get()\n"
+ "\n"
+ " Get maximum number of vertex array indices.\n"
+ "\n"
+ " :return: Number of indices.\n"
+ " :rtype: int\n");
static PyObject *pygpu_max_batch_indices_get(PyObject *UNUSED(self))
{
return PyLong_FromLong(GPU_max_batch_indices());
}
+PyDoc_STRVAR(pygpu_max_batch_vertices_get_doc,
+ ".. function:: max_batch_vertices_get()\n"
+ "\n"
+ " Get maximum number of vertex array vertices.\n"
+ "\n"
+ " :return: Number of vertices.\n"
+ " :rtype: int\n");
static PyObject *pygpu_max_batch_vertices_get(PyObject *UNUSED(self))
{
return PyLong_FromLong(GPU_max_batch_vertices());
}
+PyDoc_STRVAR(pygpu_max_vertex_attribs_get_doc,
+ ".. function:: max_vertex_attribs_get()\n"
+ "\n"
+ " Get maximum number of vertex attributes accessible to\n"
+ " a vertex shader.\n"
+ "\n"
+ " :return: Number of attributes.\n"
+ " :rtype: int\n");
static PyObject *pygpu_max_vertex_attribs_get(PyObject *UNUSED(self))
{
return PyLong_FromLong(GPU_max_vertex_attribs());
}
+PyDoc_STRVAR(pygpu_max_varying_floats_get_doc,
+ ".. function:: max_varying_floats_get()\n"
+ "\n"
+ " Get maximum number of varying variables used by\n"
+ " vertex and fragment shaders.\n"
+ "\n"
+ " :return: Number of variables.\n"
+ " :rtype: int\n");
static PyObject *pygpu_max_varying_floats_get(PyObject *UNUSED(self))
{
return PyLong_FromLong(GPU_max_varying_floats());
}
+PyDoc_STRVAR(pygpu_extensions_get_doc,
+ ".. function:: extensions_get()\n"
+ "\n"
+ " Get supported extensions in the current context.\n"
+ "\n"
+ " :return: Extensions.\n"
+ " :rtype: tuple of string\n");
static PyObject *pygpu_extensions_get(PyObject *UNUSED(self))
{
int extensions_len = GPU_extensions_len();
@@ -112,19 +212,55 @@ static PyObject *pygpu_extensions_get(PyObject *UNUSED(self))
* \{ */
static struct PyMethodDef pygpu_capabilities__tp_methods[] = {
- {"max_texture_size_get", (PyCFunction)pygpu_max_texture_size_get, METH_NOARGS, NULL},
- {"max_texture_layers_get", (PyCFunction)pygpu_max_texture_layers_get, METH_NOARGS, NULL},
- {"max_textures_get", (PyCFunction)pygpu_max_textures_get, METH_NOARGS, NULL},
- {"max_textures_vert_get", (PyCFunction)pygpu_max_textures_vert_get, METH_NOARGS, NULL},
- {"max_textures_geom_get", (PyCFunction)pygpu_max_textures_geom_get, METH_NOARGS, NULL},
- {"max_textures_frag_get", (PyCFunction)pygpu_max_textures_frag_get, METH_NOARGS, NULL},
- {"max_uniforms_vert_get", (PyCFunction)pygpu_max_uniforms_vert_get, METH_NOARGS, NULL},
- {"max_uniforms_frag_get", (PyCFunction)pygpu_max_uniforms_frag_get, METH_NOARGS, NULL},
- {"max_batch_indices_get", (PyCFunction)pygpu_max_batch_indices_get, METH_NOARGS, NULL},
- {"max_batch_vertices_get", (PyCFunction)pygpu_max_batch_vertices_get, METH_NOARGS, NULL},
- {"max_vertex_attribs_get", (PyCFunction)pygpu_max_vertex_attribs_get, METH_NOARGS, NULL},
- {"max_varying_floats_get", (PyCFunction)pygpu_max_varying_floats_get, METH_NOARGS, NULL},
- {"extensions_get", (PyCFunction)pygpu_extensions_get, METH_NOARGS, NULL},
+ {"max_texture_size_get",
+ (PyCFunction)pygpu_max_texture_size_get,
+ METH_NOARGS,
+ pygpu_max_texture_size_get_doc},
+ {"max_texture_layers_get",
+ (PyCFunction)pygpu_max_texture_layers_get,
+ METH_NOARGS,
+ pygpu_max_texture_layers_get_doc},
+ {"max_textures_get",
+ (PyCFunction)pygpu_max_textures_get,
+ METH_NOARGS,
+ pygpu_max_textures_get_doc},
+ {"max_textures_vert_get",
+ (PyCFunction)pygpu_max_textures_vert_get,
+ METH_NOARGS,
+ pygpu_max_textures_vert_get_doc},
+ {"max_textures_geom_get",
+ (PyCFunction)pygpu_max_textures_geom_get,
+ METH_NOARGS,
+ pygpu_max_textures_geom_get_doc},
+ {"max_textures_frag_get",
+ (PyCFunction)pygpu_max_textures_frag_get,
+ METH_NOARGS,
+ pygpu_max_textures_frag_get_doc},
+ {"max_uniforms_vert_get",
+ (PyCFunction)pygpu_max_uniforms_vert_get,
+ METH_NOARGS,
+ pygpu_max_uniforms_vert_get_doc},
+ {"max_uniforms_frag_get",
+ (PyCFunction)pygpu_max_uniforms_frag_get,
+ METH_NOARGS,
+ pygpu_max_uniforms_frag_get_doc},
+ {"max_batch_indices_get",
+ (PyCFunction)pygpu_max_batch_indices_get,
+ METH_NOARGS,
+ pygpu_max_batch_indices_get_doc},
+ {"max_batch_vertices_get",
+ (PyCFunction)pygpu_max_batch_vertices_get,
+ METH_NOARGS,
+ pygpu_max_batch_vertices_get_doc},
+ {"max_vertex_attribs_get",
+ (PyCFunction)pygpu_max_vertex_attribs_get,
+ METH_NOARGS,
+ pygpu_max_vertex_attribs_get_doc},
+ {"max_varying_floats_get",
+ (PyCFunction)pygpu_max_varying_floats_get,
+ METH_NOARGS,
+ pygpu_max_varying_floats_get_doc},
+ {"extensions_get", (PyCFunction)pygpu_extensions_get, METH_NOARGS, pygpu_extensions_get_doc},
{NULL, NULL, 0, NULL},
};
diff --git a/source/blender/python/gpu/gpu_py_offscreen.c b/source/blender/python/gpu/gpu_py_offscreen.c
index 457f00b1267..6f23c2213e2 100644
--- a/source/blender/python/gpu/gpu_py_offscreen.c
+++ b/source/blender/python/gpu/gpu_py_offscreen.c
@@ -227,7 +227,7 @@ static PyObject *pygpu_offscreen__tp_new(PyTypeObject *UNUSED(self),
}
if (GPU_context_active_get()) {
- ofs = GPU_offscreen_create(width, height, true, false, err_out);
+ ofs = GPU_offscreen_create(width, height, true, GPU_RGBA8, err_out);
}
else {
STRNCPY(err_out, "No active GPU context found");
@@ -279,7 +279,8 @@ static PyObject *pygpu_offscreen_texture_color_get(BPyGPUOffScreen *self, void *
PyDoc_STRVAR(
pygpu_offscreen_draw_view3d_doc,
- ".. method:: draw_view3d(scene, view_layer, view3d, region, view_matrix, projection_matrix)\n"
+ ".. method:: draw_view3d(scene, view_layer, view3d, region, view_matrix, projection_matrix, "
+ "do_color_management=False)\n"
"\n"
" Draw the 3d viewport in the offscreen object.\n"
"\n"
@@ -294,7 +295,9 @@ PyDoc_STRVAR(
" :arg view_matrix: View Matrix (e.g. ``camera.matrix_world.inverted()``).\n"
" :type view_matrix: :class:`mathutils.Matrix`\n"
" :arg projection_matrix: Projection Matrix (e.g. ``camera.calc_matrix_camera(...)``).\n"
- " :type projection_matrix: :class:`mathutils.Matrix`\n");
+ " :type projection_matrix: :class:`mathutils.Matrix`\n"
+ " :arg do_color_management: Color manage the output.\n"
+ " :type do_color_management: bool\n");
static PyObject *pygpu_offscreen_draw_view3d(BPyGPUOffScreen *self, PyObject *args, PyObject *kwds)
{
MatrixObject *py_mat_view, *py_mat_projection;
@@ -306,12 +309,20 @@ static PyObject *pygpu_offscreen_draw_view3d(BPyGPUOffScreen *self, PyObject *ar
View3D *v3d;
ARegion *region;
+ bool do_color_management = false;
+
BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
- static const char *_keywords[] = {
- "scene", "view_layer", "view3d", "region", "view_matrix", "projection_matrix", NULL};
+ static const char *_keywords[] = {"scene",
+ "view_layer",
+ "view3d",
+ "region",
+ "view_matrix",
+ "projection_matrix",
+ "do_color_management",
+ NULL};
- static _PyArg_Parser _parser = {"OOOOO&O&:draw_view3d", _keywords, 0};
+ static _PyArg_Parser _parser = {"OOOOO&O&|$O&:draw_view3d", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kwds,
&_parser,
@@ -322,7 +333,9 @@ static PyObject *pygpu_offscreen_draw_view3d(BPyGPUOffScreen *self, PyObject *ar
Matrix_Parse4x4,
&py_mat_view,
Matrix_Parse4x4,
- &py_mat_projection) ||
+ &py_mat_projection,
+ PyC_ParseBool,
+ &do_color_management) ||
(!(scene = PyC_RNA_AsPointer(py_scene, "Scene")) ||
!(view_layer = PyC_RNA_AsPointer(py_view_layer, "ViewLayer")) ||
!(v3d = PyC_RNA_AsPointer(py_view3d, "SpaceView3D")) ||
@@ -354,7 +367,7 @@ static PyObject *pygpu_offscreen_draw_view3d(BPyGPUOffScreen *self, PyObject *ar
true,
true,
"",
- false,
+ do_color_management,
true,
self->ofs,
NULL);
diff --git a/source/blender/python/gpu/gpu_py_platform.c b/source/blender/python/gpu/gpu_py_platform.c
index 132052b6f1d..62310a83642 100644
--- a/source/blender/python/gpu/gpu_py_platform.c
+++ b/source/blender/python/gpu/gpu_py_platform.c
@@ -33,16 +33,37 @@
/** \name Functions
* \{ */
+PyDoc_STRVAR(pygpu_platform_vendor_get_doc,
+ ".. function:: vendor_get()\n"
+ "\n"
+ " Get GPU vendor.\n"
+ "\n"
+ " :return: Vendor name.\n"
+ " :rtype: str\n");
static PyObject *pygpu_platform_vendor_get(PyObject *UNUSED(self))
{
return PyUnicode_FromString(GPU_platform_vendor());
}
+PyDoc_STRVAR(pygpu_platform_renderer_get_doc,
+ ".. function:: renderer_get()\n"
+ "\n"
+ " Get GPU to be used for rendering.\n"
+ "\n"
+ " :return: GPU name.\n"
+ " :rtype: str\n");
static PyObject *pygpu_platform_renderer_get(PyObject *UNUSED(self))
{
return PyUnicode_FromString(GPU_platform_renderer());
}
+PyDoc_STRVAR(pygpu_platform_version_get_doc,
+ ".. function:: version_get()\n"
+ "\n"
+ " Get GPU driver version.\n"
+ "\n"
+ " :return: Driver version.\n"
+ " :rtype: str\n");
static PyObject *pygpu_platform_version_get(PyObject *UNUSED(self))
{
return PyUnicode_FromString(GPU_platform_version());
@@ -55,9 +76,18 @@ static PyObject *pygpu_platform_version_get(PyObject *UNUSED(self))
* \{ */
static struct PyMethodDef pygpu_platform__tp_methods[] = {
- {"vendor_get", (PyCFunction)pygpu_platform_vendor_get, METH_NOARGS, NULL},
- {"renderer_get", (PyCFunction)pygpu_platform_renderer_get, METH_NOARGS, NULL},
- {"version_get", (PyCFunction)pygpu_platform_version_get, METH_NOARGS, NULL},
+ {"vendor_get",
+ (PyCFunction)pygpu_platform_vendor_get,
+ METH_NOARGS,
+ pygpu_platform_vendor_get_doc},
+ {"renderer_get",
+ (PyCFunction)pygpu_platform_renderer_get,
+ METH_NOARGS,
+ pygpu_platform_renderer_get_doc},
+ {"version_get",
+ (PyCFunction)pygpu_platform_version_get,
+ METH_NOARGS,
+ pygpu_platform_version_get_doc},
{NULL, NULL, 0, NULL},
};
diff --git a/source/blender/python/gpu/gpu_py_shader.c b/source/blender/python/gpu/gpu_py_shader.c
index b3f1c186716..145586d8ab0 100644
--- a/source/blender/python/gpu/gpu_py_shader.c
+++ b/source/blender/python/gpu/gpu_py_shader.c
@@ -44,14 +44,28 @@
/** \name Enum Conversion.
* \{ */
+#define PYDOC_BUILTIN_SHADER_LIST \
+ " - ``2D_FLAT_COLOR``\n" \
+ " - ``2D_IMAGE``\n" \
+ " - ``2D_SMOOTH_COLOR``\n" \
+ " - ``2D_UNIFORM_COLOR``\n" \
+ " - ``3D_FLAT_COLOR``\n" \
+ " - ``3D_SMOOTH_COLOR``\n" \
+ " - ``3D_UNIFORM_COLOR``\n" \
+ " - ``3D_POLYLINE_FLAT_COLOR``\n" \
+ " - ``3D_POLYLINE_SMOOTH_COLOR``\n" \
+ " - ``3D_POLYLINE_UNIFORM_COLOR``\n"
+
static const struct PyC_StringEnumItems pygpu_shader_builtin_items[] = {
- {GPU_SHADER_2D_UNIFORM_COLOR, "2D_UNIFORM_COLOR"},
{GPU_SHADER_2D_FLAT_COLOR, "2D_FLAT_COLOR"},
- {GPU_SHADER_2D_SMOOTH_COLOR, "2D_SMOOTH_COLOR"},
{GPU_SHADER_2D_IMAGE, "2D_IMAGE"},
- {GPU_SHADER_3D_UNIFORM_COLOR, "3D_UNIFORM_COLOR"},
+ {GPU_SHADER_2D_SMOOTH_COLOR, "2D_SMOOTH_COLOR"},
+ {GPU_SHADER_2D_UNIFORM_COLOR, "2D_UNIFORM_COLOR"},
{GPU_SHADER_3D_FLAT_COLOR, "3D_FLAT_COLOR"},
{GPU_SHADER_3D_SMOOTH_COLOR, "3D_SMOOTH_COLOR"},
+ {GPU_SHADER_3D_UNIFORM_COLOR, "3D_UNIFORM_COLOR"},
+ {GPU_SHADER_3D_POLYLINE_FLAT_COLOR, "3D_POLYLINE_FLAT_COLOR"},
+ {GPU_SHADER_3D_POLYLINE_SMOOTH_COLOR, "3D_POLYLINE_SMOOTH_COLOR"},
{GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR, "3D_POLYLINE_UNIFORM_COLOR"},
{0, NULL},
};
@@ -197,8 +211,9 @@ static bool pygpu_shader_uniform_vector_impl(PyObject *args,
return false;
}
- if (r_pybuffer->len != (*r_length * *r_count * elem_size)) {
- PyErr_SetString(PyExc_BufferError, "GPUShader.uniform_vector_*: buffer size does not match.");
+ if (r_pybuffer->len < (*r_length * *r_count * elem_size)) {
+ PyErr_SetString(PyExc_OverflowError,
+ "GPUShader.uniform_vector_*: buffer size smaller than required.");
return false;
}
@@ -704,14 +719,8 @@ PyDoc_STRVAR(pygpu_shader_from_builtin_doc,
" For more details, you can check the shader code with the\n"
" :func:`gpu.shader.code_from_builtin` function.\n"
"\n"
- " :param pygpu_shader_name: One of these builtin shader names:\n\n"
- " - ``2D_UNIFORM_COLOR``\n"
- " - ``2D_FLAT_COLOR``\n"
- " - ``2D_SMOOTH_COLOR``\n"
- " - ``2D_IMAGE``\n"
- " - ``3D_UNIFORM_COLOR``\n"
- " - ``3D_FLAT_COLOR``\n"
- " - ``3D_SMOOTH_COLOR``\n"
+ " :param pygpu_shader_name: One of these builtin shader names:\n"
+ "\n" PYDOC_BUILTIN_SHADER_LIST
" :type pygpu_shader_name: str\n"
" :return: Shader object corresponding to the given name.\n"
" :rtype: :class:`bpy.types.GPUShader`\n");
@@ -734,14 +743,8 @@ PyDoc_STRVAR(pygpu_shader_code_from_builtin_doc,
"\n"
" Exposes the internal shader code for query.\n"
"\n"
- " :param pygpu_shader_name: One of these builtin shader names:\n\n"
- " - ``2D_UNIFORM_COLOR``\n"
- " - ``2D_FLAT_COLOR``\n"
- " - ``2D_SMOOTH_COLOR``\n"
- " - ``2D_IMAGE``\n"
- " - ``3D_UNIFORM_COLOR``\n"
- " - ``3D_FLAT_COLOR``\n"
- " - ``3D_SMOOTH_COLOR``\n"
+ " :param pygpu_shader_name: One of these builtin shader names:\n"
+ "\n" PYDOC_BUILTIN_SHADER_LIST
" :type pygpu_shader_name: str\n"
" :return: Vertex, fragment and geometry shader codes.\n"
" :rtype: dict\n");
diff --git a/source/blender/python/intern/bpy.c b/source/blender/python/intern/bpy.c
index 30a61067c9e..0e0f431cb19 100644
--- a/source/blender/python/intern/bpy.c
+++ b/source/blender/python/intern/bpy.c
@@ -417,9 +417,6 @@ void BPy_init_modules(struct bContext *C)
PyDict_SetItemString(PyImport_GetModuleDict(), "_bpy", mod);
Py_DECREF(mod);
- /* run first, initializes rna types */
- BPY_rna_init();
-
/* needs to be first so bpy_types can run */
PyModule_AddObject(mod, "types", BPY_rna_types());
diff --git a/source/blender/python/intern/bpy_app_icons.c b/source/blender/python/intern/bpy_app_icons.c
index 7cca3ae4700..acd809fb8d5 100644
--- a/source/blender/python/intern/bpy_app_icons.c
+++ b/source/blender/python/intern/bpy_app_icons.c
@@ -35,7 +35,7 @@
/* We may want to load direct from file. */
PyDoc_STRVAR(
bpy_app_icons_new_triangles_doc,
- ".. function:: new_triangles(range, coords, colors)"
+ ".. function:: new_triangles(range, coords, colors)\n"
"\n"
" Create a new icon from triangle geometry.\n"
"\n"
@@ -91,7 +91,7 @@ static PyObject *bpy_app_icons_new_triangles(PyObject *UNUSED(self), PyObject *a
}
PyDoc_STRVAR(bpy_app_icons_new_triangles_from_file_doc,
- ".. function:: new_triangles_from_file(filename)"
+ ".. function:: new_triangles_from_file(filename)\n"
"\n"
" Create a new icon from triangle geometry.\n"
"\n"
@@ -122,7 +122,7 @@ static PyObject *bpy_app_icons_new_triangles_from_file(PyObject *UNUSED(self),
}
PyDoc_STRVAR(bpy_app_icons_release_doc,
- ".. function:: release(icon_id)"
+ ".. function:: release(icon_id)\n"
"\n"
" Release the icon.\n");
static PyObject *bpy_app_icons_release(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
diff --git a/source/blender/python/intern/bpy_app_translations.c b/source/blender/python/intern/bpy_app_translations.c
index 7437598582f..de70035eb2b 100644
--- a/source/blender/python/intern/bpy_app_translations.c
+++ b/source/blender/python/intern/bpy_app_translations.c
@@ -746,7 +746,7 @@ static PyObject *app_translations_new(PyTypeObject *type,
PyObject *UNUSED(args),
PyObject *UNUSED(kw))
{
- /* printf("%s (%p)\n", __func__, _translations); */
+ // printf("%s (%p)\n", __func__, _translations);
if (!_translations) {
_translations = (BlenderAppTranslations *)type->tp_alloc(type, 0);
diff --git a/source/blender/python/intern/bpy_gizmo_wrap.c b/source/blender/python/intern/bpy_gizmo_wrap.c
index 42e0c7d0003..a05ec6b7000 100644
--- a/source/blender/python/intern/bpy_gizmo_wrap.c
+++ b/source/blender/python/intern/bpy_gizmo_wrap.c
@@ -79,7 +79,7 @@ static bool bpy_gizmotype_target_property_def(wmGizmoType *gzt, PyObject *item)
goto fail;
}
- if ((params.array_length < 1 || params.array_length > RNA_MAX_ARRAY_LENGTH)) {
+ if ((params.array_length < 1) || (params.array_length > RNA_MAX_ARRAY_LENGTH)) {
PyErr_SetString(PyExc_ValueError, "'array_length' out of range");
goto fail;
}
diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c
index f91ba4d362c..1a308414bc3 100644
--- a/source/blender/python/intern/bpy_interface.c
+++ b/source/blender/python/intern/bpy_interface.c
@@ -155,7 +155,7 @@ void bpy_context_clear(bContext *UNUSED(C), const PyGILState_STATE *gilstate)
}
else if (py_call_level == 0) {
/* XXX: Calling classes currently won't store the context :\,
- * can't set NULL because of this. but this is very flakey still. */
+ * can't set NULL because of this. but this is very flaky still. */
#if 0
BPY_context_set(NULL);
#endif
@@ -502,7 +502,10 @@ void BPY_python_start(bContext *C, int argc, const char **argv)
}
#endif
- /* bpy.* and lets us import it */
+ /* Run first, initializes RNA types. */
+ BPY_rna_init();
+
+ /* Defines `bpy.*` and lets us import it. */
BPy_init_modules(C);
pyrna_alloc_types();
@@ -541,6 +544,8 @@ void BPY_python_end(void)
/* free other python data. */
pyrna_free_types();
+ BPY_rna_exit();
+
/* clear all python data from structs */
bpy_intern_string_exit();
@@ -650,7 +655,7 @@ void BPY_modules_load_user(bContext *C)
bpy_context_set(C, &gilstate);
for (text = bmain->texts.first; text; text = text->id.next) {
- if (text->flags & TXT_ISSCRIPT && BLI_path_extension_check(text->id.name + 2, ".py")) {
+ if (text->flags & TXT_ISSCRIPT) {
if (!(G.f & G_FLAG_SCRIPT_AUTOEXEC)) {
if (!(G.f & G_FLAG_SCRIPT_AUTOEXEC_FAIL_QUIET)) {
G.f |= G_FLAG_SCRIPT_AUTOEXEC_FAIL;
diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c
index dff96f74d62..ac1a7f68885 100644
--- a/source/blender/python/intern/bpy_rna.c
+++ b/source/blender/python/intern/bpy_rna.c
@@ -181,23 +181,13 @@ static PyMethodDef id_free_weakref_cb_def = {
/* Adds a reference to the list, remember to decref. */
static GHash *id_weakref_pool_get(ID *id)
{
- GHash *weakinfo_hash = NULL;
-
- if (id_weakref_pool) {
- weakinfo_hash = BLI_ghash_lookup(id_weakref_pool, (void *)id);
- }
- else {
- /* First time, allocate pool. */
- id_weakref_pool = BLI_ghash_ptr_new("rna_global_pool");
- weakinfo_hash = NULL;
- }
-
+ GHash *weakinfo_hash = BLI_ghash_lookup(id_weakref_pool, (void *)id);
if (weakinfo_hash == NULL) {
- /* We use a ghash as a set, we could use libHX's HXMAP_SINGULAR, but would be an extra dep. */
+ /* This could be a set, values are used to keep a reference back to the ID
+ * (all of them are the same). */
weakinfo_hash = BLI_ghash_ptr_new("rna_id");
BLI_ghash_insert(id_weakref_pool, id, weakinfo_hash);
}
-
return weakinfo_hash;
}
@@ -283,14 +273,6 @@ static void id_release_weakref_list(struct ID *id, GHash *weakinfo_hash)
BLI_ghash_remove(id_weakref_pool, (void *)id, NULL, NULL);
BLI_ghash_free(weakinfo_hash, NULL, NULL);
-
- if (BLI_ghash_len(id_weakref_pool) == 0) {
- BLI_ghash_free(id_weakref_pool, NULL, NULL);
- id_weakref_pool = NULL;
-# ifdef DEBUG_RNA_WEAKREF
- printf("id_release_weakref freeing pool\n");
-# endif
- }
}
static void id_release_weakref(struct ID *id)
@@ -310,7 +292,8 @@ void BPY_id_release(struct ID *id)
#endif
#ifdef USE_PYRNA_INVALIDATE_WEAKREF
- if (id_weakref_pool) {
+ /* Check for NULL since this may run before Python has been started. */
+ if (id_weakref_pool != NULL) {
PyGILState_STATE gilstate = PyGILState_Ensure();
id_release_weakref(id);
@@ -2825,9 +2808,11 @@ static PyObject *pyrna_prop_collection_subscript(BPy_PropertyRNA *self, PyObject
const Py_ssize_t len = (Py_ssize_t)RNA_property_collection_length(&self->ptr, self->prop);
if (start < 0) {
start += len;
+ CLAMP_MIN(start, 0);
}
if (stop < 0) {
stop += len;
+ CLAMP_MIN(stop, 0);
}
}
@@ -2955,9 +2940,11 @@ static int pyrna_prop_collection_ass_subscript(BPy_PropertyRNA *self,
Py_ssize_t len = (Py_ssize_t)RNA_property_collection_length(&self->ptr, self->prop);
if (start < 0) {
start += len;
+ CLAMP_MIN(start, 0);
}
if (stop < 0) {
stop += len;
+ CLAMP_MIN(stop, 0);
}
}
@@ -4488,7 +4475,7 @@ static PyObject *pyrna_struct_meta_idprop_getattro(PyObject *cls, PyObject *attr
if ((ret == NULL) /* || BPy_PropDeferred_CheckTypeExact(ret) */ ) {
StructRNA *srna = srna_from_self(cls, "StructRNA.__getattr__");
if (srna) {
- PropertyRNA *prop = RNA_struct_type_find_property(srna, PyUnicode_AsUTF8(attr));
+ PropertyRNA *prop = RNA_struct_type_find_property_no_base(srna, PyUnicode_AsUTF8(attr));
if (prop) {
PointerRNA tptr;
PyErr_Clear(); /* Clear error from tp_getattro. */
@@ -4510,7 +4497,7 @@ static int pyrna_struct_meta_idprop_setattro(PyObject *cls, PyObject *attr, PyOb
const char *attr_str = PyUnicode_AsUTF8(attr);
if (srna && !pyrna_write_check() &&
- (is_deferred_prop || RNA_struct_type_find_property(srna, attr_str))) {
+ (is_deferred_prop || RNA_struct_type_find_property_no_base(srna, attr_str))) {
PyErr_Format(PyExc_AttributeError,
"pyrna_struct_meta_idprop_setattro() "
"can't set in readonly state '%.200s.%S'",
@@ -7518,7 +7505,7 @@ static PyObject *pyrna_srna_Subtype(StructRNA *srna)
/* Newclass will now have 2 ref's, ???,
* probably 1 is internal since #Py_DECREF here segfaults. */
- /* PyC_ObSpit("new class ref", newclass); */
+ // PyC_ObSpit("new class ref", newclass);
if (newclass) {
/* srna owns one, and the other is owned by the caller. */
@@ -7772,6 +7759,32 @@ void BPY_rna_init(void)
return;
}
#endif
+
+#ifdef USE_PYRNA_INVALIDATE_WEAKREF
+ BLI_assert(id_weakref_pool == NULL);
+ id_weakref_pool = BLI_ghash_ptr_new("rna_global_pool");
+#endif
+}
+
+void BPY_rna_exit(void)
+{
+#ifdef USE_PYRNA_INVALIDATE_WEAKREF
+ /* This can help track down which kinds of data were not released.
+ * If they were in fact freed by Blender, printing their names
+ * will crash giving a useful error with address sanitizer. The likely cause
+ * for this list not being empty is a missing call to: #BKE_libblock_free_data_py. */
+ const int id_weakref_pool_len = BLI_ghash_len(id_weakref_pool);
+ if (id_weakref_pool_len != id_weakref_pool_len) {
+ printf("Found %d unreleased ID's\n", id_weakref_pool_len);
+ GHashIterator gh_iter;
+ GHASH_ITER (gh_iter, id_weakref_pool) {
+ ID *id = BLI_ghashIterator_getKey(&gh_iter);
+ printf("ID: %s\n", id->name);
+ }
+ }
+ BLI_ghash_free(id_weakref_pool, NULL, NULL);
+ id_weakref_pool = NULL;
+#endif
}
/* 'bpy.data' from Python. */
@@ -8677,6 +8690,8 @@ static int bpy_class_call(bContext *C, PointerRNA *ptr, FunctionRNA *func, Param
}
#ifdef USE_PEDANTIC_WRITE
+ /* Handle nested draw calls, see: T89253. */
+ const bool rna_disallow_writes_prev = rna_disallow_writes;
rna_disallow_writes = is_readonly ? true : false;
#endif
/* *** Main Caller *** */
@@ -8686,7 +8701,7 @@ static int bpy_class_call(bContext *C, PointerRNA *ptr, FunctionRNA *func, Param
/* *** Done Calling *** */
#ifdef USE_PEDANTIC_WRITE
- rna_disallow_writes = false;
+ rna_disallow_writes = rna_disallow_writes_prev;
#endif
RNA_parameter_list_end(&iter);
diff --git a/source/blender/python/intern/bpy_rna.h b/source/blender/python/intern/bpy_rna.h
index 24dbad53eb3..fd468bed470 100644
--- a/source/blender/python/intern/bpy_rna.h
+++ b/source/blender/python/intern/bpy_rna.h
@@ -181,6 +181,7 @@ StructRNA *srna_from_self(PyObject *self, const char *error_prefix);
StructRNA *pyrna_struct_as_srna(PyObject *self, const bool parent, const char *error_prefix);
void BPY_rna_init(void);
+void BPY_rna_exit(void);
PyObject *BPY_rna_module(void);
void BPY_update_rna_module(void);
// PyObject *BPY_rna_doc(void);
diff --git a/source/blender/python/intern/bpy_rna_array.c b/source/blender/python/intern/bpy_rna_array.c
index abbc332d89d..fcc796d4545 100644
--- a/source/blender/python/intern/bpy_rna_array.c
+++ b/source/blender/python/intern/bpy_rna_array.c
@@ -334,7 +334,7 @@ static int validate_array_length(PyObject *rvalue,
}
if (tot != len) {
- /* BLI_snprintf(error_str, error_str_size, "sequence must have length of %d", len); */
+ // BLI_snprintf(error_str, error_str_size, "sequence must have length of %d", len);
PyErr_Format(PyExc_ValueError,
"%s %.200s.%.200s, sequence must have %d items total, not %d",
error_prefix,
diff --git a/source/blender/python/intern/bpy_rna_id_collection.c b/source/blender/python/intern/bpy_rna_id_collection.c
index 1bb68babc3c..7bdb0e30410 100644
--- a/source/blender/python/intern/bpy_rna_id_collection.c
+++ b/source/blender/python/intern/bpy_rna_id_collection.c
@@ -227,7 +227,7 @@ static PyObject *bpy_user_map(PyObject *UNUSED(self), PyObject *args, PyObject *
}
if (!data_cb.is_subset &&
- /* We do not want to pre-add keys of flitered out types. */
+ /* We do not want to pre-add keys of filtered out types. */
(key_types_bitmap == NULL || id_check_type(id, key_types_bitmap)) &&
/* We do not want to pre-add keys when we have filter on value types,
* but not on key types. */
diff --git a/source/blender/python/intern/bpy_rna_operator.c b/source/blender/python/intern/bpy_rna_operator.c
index 490d9aa5212..d3ec54fc12d 100644
--- a/source/blender/python/intern/bpy_rna_operator.c
+++ b/source/blender/python/intern/bpy_rna_operator.c
@@ -91,7 +91,7 @@ static void pyop_poll_message_free_fn(bContext *UNUSED(C), void *user_data)
}
PyDoc_STRVAR(BPY_rna_operator_poll_message_set_doc,
- ".. method:: poll_message_set(message, ...)\n"
+ ".. method:: poll_message_set(message, *args)\n"
"\n"
" Set the message to show in the tool-tip when poll fails.\n"
"\n"
diff --git a/source/blender/python/intern/bpy_utils_units.c b/source/blender/python/intern/bpy_utils_units.c
index aa8cf8f2a9f..62f5a17c4dd 100644
--- a/source/blender/python/intern/bpy_utils_units.c
+++ b/source/blender/python/intern/bpy_utils_units.c
@@ -114,7 +114,7 @@ static PyObject *py_structseq_from_strings(PyTypeObject *py_type,
BLI_assert(py_struct_seq != NULL);
for (str_iter = str_items; *str_iter; str_iter++) {
- PyStructSequence_SET_ITEM(py_struct_seq, pos++, PyUnicode_FromString((*str_iter)));
+ PyStructSequence_SET_ITEM(py_struct_seq, pos++, PyUnicode_FromString(*str_iter));
}
return py_struct_seq;
diff --git a/source/blender/python/mathutils/mathutils.c b/source/blender/python/mathutils/mathutils.c
index 5beca7bd71a..be7dae6871b 100644
--- a/source/blender/python/mathutils/mathutils.c
+++ b/source/blender/python/mathutils/mathutils.c
@@ -95,7 +95,11 @@ Py_hash_t mathutils_array_hash(const float *array, size_t array_len)
x = 0x345678UL;
i = 0;
while (--len >= 0) {
+#if PY_VERSION_HEX >= 0x30a0000 /* Version: 3.10. */
+ y = _Py_HashDouble(NULL, (double)(array[i++]));
+#else
y = _Py_HashDouble((double)(array[i++]));
+#endif
if (y == -1) {
return -1;
}
diff --git a/source/blender/python/mathutils/mathutils_Vector.c b/source/blender/python/mathutils/mathutils_Vector.c
index c2223b023ad..efcaa9b6a51 100644
--- a/source/blender/python/mathutils/mathutils_Vector.c
+++ b/source/blender/python/mathutils/mathutils_Vector.c
@@ -1926,7 +1926,7 @@ static PyObject *Vector_imatmul(PyObject *v1, PyObject *v2)
return NULL;
}
-/* divid: obj / obj */
+/* divide: obj / obj */
static PyObject *Vector_div(PyObject *v1, PyObject *v2)
{
float *vec = NULL, scalar;
@@ -2939,7 +2939,7 @@ static int row_vector_multiplication(float r_vec[MAX_DIMENSIONS],
memcpy(vec_cpy, vec->vec, vec_size * sizeof(float));
r_vec[3] = 1.0f;
- /* muliplication */
+ /* Multiplication. */
for (col = 0; col < mat->num_col; col++) {
double dot = 0.0;
for (row = 0; row < mat->num_row; row++) {
diff --git a/source/blender/python/mathutils/mathutils_geometry.c b/source/blender/python/mathutils/mathutils_geometry.c
index c73dea79aac..5868c76b28f 100644
--- a/source/blender/python/mathutils/mathutils_geometry.c
+++ b/source/blender/python/mathutils/mathutils_geometry.c
@@ -201,7 +201,7 @@ static PyObject *M_Geometry_intersect_line_line(PyObject *UNUSED(self), PyObject
}
if (result == 0) {
- /* collinear */
+ /* Collinear. */
Py_RETURN_NONE;
}
diff --git a/source/blender/render/RE_engine.h b/source/blender/render/RE_engine.h
index 6b2861bbefd..dfc0d5d0e9f 100644
--- a/source/blender/render/RE_engine.h
+++ b/source/blender/render/RE_engine.h
@@ -66,6 +66,7 @@ extern "C" {
#define RE_USE_GPU_CONTEXT 512
#define RE_USE_CUSTOM_FREESTYLE 1024
#define RE_USE_NO_IMAGE_SAVE 2048
+#define RE_USE_ALEMBIC_PROCEDURAL 4096
/* RenderEngine.flag */
#define RE_ENGINE_ANIMATION 1
@@ -235,6 +236,12 @@ void RE_engines_register(RenderEngineType *render_type);
bool RE_engine_is_opengl(RenderEngineType *render_type);
+/**
+ * Return true if the RenderEngineType has native support for direct loading of Alembic data. For
+ * Cycles, this also checks that the experimental feature set is enabled.
+ */
+bool RE_engine_supports_alembic_procedural(const RenderEngineType *render_type, Scene *scene);
+
RenderEngineType *RE_engines_find(const char *idname);
rcti *RE_engine_get_current_tiles(struct Render *re, int *r_total_tiles, bool *r_needs_free);
diff --git a/source/blender/render/intern/engine.c b/source/blender/render/intern/engine.c
index 657cd1f606b..75b3f2db249 100644
--- a/source/blender/render/intern/engine.c
+++ b/source/blender/render/intern/engine.c
@@ -128,6 +128,19 @@ bool RE_engine_is_opengl(RenderEngineType *render_type)
return (render_type->draw_engine != NULL) && DRW_engine_render_support(render_type->draw_engine);
}
+bool RE_engine_supports_alembic_procedural(const RenderEngineType *render_type, Scene *scene)
+{
+ if ((render_type->flag & RE_USE_ALEMBIC_PROCEDURAL) == 0) {
+ return false;
+ }
+
+ if (BKE_scene_uses_cycles(scene) && !BKE_scene_uses_cycles_experimental_features(scene)) {
+ return false;
+ }
+
+ return true;
+}
+
/* Create, Free */
RenderEngine *RE_engine_create(RenderEngineType *type)
diff --git a/source/blender/render/intern/pipeline.c b/source/blender/render/intern/pipeline.c
index 333ee9ecd33..479ad9209f0 100644
--- a/source/blender/render/intern/pipeline.c
+++ b/source/blender/render/intern/pipeline.c
@@ -2524,7 +2524,7 @@ void RE_RenderAnim(Render *re,
if (G.is_break == true) {
/* remove touched file */
if (is_movie == false && do_write_file) {
- if ((rd.mode & R_TOUCH)) {
+ if (rd.mode & R_TOUCH) {
if (!is_multiview_name) {
if ((BLI_file_size(name) == 0)) {
/* BLI_exists(name) is implicit */
diff --git a/source/blender/render/intern/render_result.c b/source/blender/render/intern/render_result.c
index 693cddbebbe..091f5964291 100644
--- a/source/blender/render/intern/render_result.c
+++ b/source/blender/render/intern/render_result.c
@@ -784,7 +784,7 @@ void render_result_views_new(RenderResult *rr, const RenderData *rd)
render_result_views_free(rr);
/* check renderdata for amount of views */
- if ((rd->scemode & R_MULTIVIEW)) {
+ if (rd->scemode & R_MULTIVIEW) {
for (srv = rd->views.first; srv; srv = srv->next) {
if (BKE_scene_multiview_is_render_view_active(rd, srv) == false) {
continue;
diff --git a/source/blender/render/intern/texture_pointdensity.c b/source/blender/render/intern/texture_pointdensity.c
index 31d5bf67f28..06dd570ce2c 100644
--- a/source/blender/render/intern/texture_pointdensity.c
+++ b/source/blender/render/intern/texture_pointdensity.c
@@ -494,10 +494,7 @@ static void free_pointdensity(PointDensity *pd)
pd->point_tree = NULL;
}
- if (pd->point_data) {
- MEM_freeN(pd->point_data);
- pd->point_data = NULL;
- }
+ MEM_SAFE_FREE(pd->point_data);
pd->totpoints = 0;
}
diff --git a/source/blender/render/intern/zbuf.c b/source/blender/render/intern/zbuf.c
index 242c8a199fb..726124871ee 100644
--- a/source/blender/render/intern/zbuf.c
+++ b/source/blender/render/intern/zbuf.c
@@ -56,13 +56,8 @@ void zbuf_alloc_span(ZSpan *zspan, int rectx, int recty)
void zbuf_free_span(ZSpan *zspan)
{
if (zspan) {
- if (zspan->span1) {
- MEM_freeN(zspan->span1);
- }
- if (zspan->span2) {
- MEM_freeN(zspan->span2);
- }
- zspan->span1 = zspan->span2 = NULL;
+ MEM_SAFE_FREE(zspan->span1);
+ MEM_SAFE_FREE(zspan->span2);
}
}
diff --git a/source/blender/sequencer/SEQ_add.h b/source/blender/sequencer/SEQ_add.h
index 2941eb6f4c0..4025f1a4a04 100644
--- a/source/blender/sequencer/SEQ_add.h
+++ b/source/blender/sequencer/SEQ_add.h
@@ -79,14 +79,16 @@ struct Sequence *SEQ_add_image_strip(struct Main *bmain,
struct Sequence *SEQ_add_sound_strip(struct Main *bmain,
struct Scene *scene,
struct ListBase *seqbase,
- struct SeqLoadData *load_data);
+ struct SeqLoadData *load_data,
+ const double audio_offset);
struct Sequence *SEQ_add_meta_strip(struct Scene *scene,
struct ListBase *seqbase,
struct SeqLoadData *load_data);
struct Sequence *SEQ_add_movie_strip(struct Main *bmain,
struct Scene *scene,
struct ListBase *seqbase,
- struct SeqLoadData *load_data);
+ struct SeqLoadData *load_data,
+ double *r_video_start_offset);
struct Sequence *SEQ_add_scene_strip(struct Scene *scene,
struct ListBase *seqbase,
struct SeqLoadData *load_data);
diff --git a/source/blender/sequencer/intern/effects.c b/source/blender/sequencer/intern/effects.c
index 7757271a2e5..3ca0555d9a5 100644
--- a/source/blender/sequencer/intern/effects.c
+++ b/source/blender/sequencer/intern/effects.c
@@ -2164,11 +2164,7 @@ static int num_inputs_wipe(void)
static void free_wipe_effect(Sequence *seq, const bool UNUSED(do_id_user))
{
- if (seq->effectdata) {
- MEM_freeN(seq->effectdata);
- }
-
- seq->effectdata = NULL;
+ MEM_SAFE_FREE(seq->effectdata);
}
static void copy_wipe_effect(Sequence *dst, Sequence *src, const int UNUSED(flag))
@@ -2382,10 +2378,7 @@ static int num_inputs_transform(void)
static void free_transform_effect(Sequence *seq, const bool UNUSED(do_id_user))
{
- if (seq->effectdata) {
- MEM_freeN(seq->effectdata);
- }
- seq->effectdata = NULL;
+ MEM_SAFE_FREE(seq->effectdata);
}
static void copy_transform_effect(Sequence *dst, Sequence *src, const int UNUSED(flag))
@@ -2723,11 +2716,7 @@ static int num_inputs_glow(void)
static void free_glow_effect(Sequence *seq, const bool UNUSED(do_id_user))
{
- if (seq->effectdata) {
- MEM_freeN(seq->effectdata);
- }
-
- seq->effectdata = NULL;
+ MEM_SAFE_FREE(seq->effectdata);
}
static void copy_glow_effect(Sequence *dst, Sequence *src, const int UNUSED(flag))
@@ -2853,11 +2842,7 @@ static int num_inputs_color(void)
static void free_solid_color(Sequence *seq, const bool UNUSED(do_id_user))
{
- if (seq->effectdata) {
- MEM_freeN(seq->effectdata);
- }
-
- seq->effectdata = NULL;
+ MEM_SAFE_FREE(seq->effectdata);
}
static void copy_solid_color(Sequence *dst, Sequence *src, const int UNUSED(flag))
@@ -3111,10 +3096,7 @@ static void free_speed_effect(Sequence *seq, const bool UNUSED(do_id_user))
if (v->frameMap) {
MEM_freeN(v->frameMap);
}
- if (seq->effectdata) {
- MEM_freeN(seq->effectdata);
- }
- seq->effectdata = NULL;
+ MEM_SAFE_FREE(seq->effectdata);
}
static void copy_speed_effect(Sequence *dst, Sequence *src, const int UNUSED(flag))
@@ -3394,11 +3376,7 @@ static int num_inputs_gaussian_blur(void)
static void free_gaussian_blur_effect(Sequence *seq, const bool UNUSED(do_id_user))
{
- if (seq->effectdata) {
- MEM_freeN(seq->effectdata);
- }
-
- seq->effectdata = NULL;
+ MEM_SAFE_FREE(seq->effectdata);
}
static void copy_gaussian_blur_effect(Sequence *dst, Sequence *src, const int UNUSED(flag))
@@ -4052,11 +4030,7 @@ static void copy_effect_default(Sequence *dst, Sequence *src, const int UNUSED(f
static void free_effect_default(Sequence *seq, const bool UNUSED(do_id_user))
{
- if (seq->effectdata) {
- MEM_freeN(seq->effectdata);
- }
-
- seq->effectdata = NULL;
+ MEM_SAFE_FREE(seq->effectdata);
}
static int early_out_noop(Sequence *UNUSED(seq), float UNUSED(facf0), float UNUSED(facf1))
diff --git a/source/blender/sequencer/intern/image_cache.c b/source/blender/sequencer/intern/image_cache.c
index 604c9900355..86bd840ce31 100644
--- a/source/blender/sequencer/intern/image_cache.c
+++ b/source/blender/sequencer/intern/image_cache.c
@@ -102,7 +102,7 @@
/* <cache type>-<resolution X>x<resolution Y>-<rendersize>%(<view_id>)-<frame no>.dcf */
#define DCACHE_FNAME_FORMAT "%d-%dx%d-%d%%(%d)-%d.dcf"
#define DCACHE_IMAGES_PER_FILE 100
-#define DCACHE_CURRENT_VERSION 1
+#define DCACHE_CURRENT_VERSION 2
#define COLORSPACE_NAME_MAX 64 /* XXX: defined in imb intern */
typedef struct DiskCacheHeaderEntry {
@@ -496,24 +496,34 @@ static size_t deflate_imbuf_to_file(ImBuf *ibuf,
int level,
DiskCacheHeaderEntry *header_entry)
{
- if (ibuf->rect) {
- return BLI_gzip_mem_to_file_at_pos(
- ibuf->rect, header_entry->size_raw, file, header_entry->offset, level);
+ void *data = (ibuf->rect != NULL) ? (void *)ibuf->rect : (void *)ibuf->rect_float;
+
+ /* Apply compression if wanted, otherwise just write directly to the file. */
+ if (level > 0) {
+ return BLI_file_zstd_from_mem_at_pos(
+ data, header_entry->size_raw, file, header_entry->offset, level);
}
- return BLI_gzip_mem_to_file_at_pos(
- ibuf->rect_float, header_entry->size_raw, file, header_entry->offset, level);
+ fseek(file, header_entry->offset, SEEK_SET);
+ return fwrite(data, 1, header_entry->size_raw, file);
}
static size_t inflate_file_to_imbuf(ImBuf *ibuf, FILE *file, DiskCacheHeaderEntry *header_entry)
{
- if (ibuf->rect) {
- return BLI_ungzip_file_to_mem_at_pos(
- ibuf->rect, header_entry->size_raw, file, header_entry->offset);
+ void *data = (ibuf->rect != NULL) ? (void *)ibuf->rect : (void *)ibuf->rect_float;
+ char header[4];
+ fseek(file, header_entry->offset, SEEK_SET);
+ if (fread(header, 1, sizeof(header), file) != sizeof(header)) {
+ return 0;
+ }
+
+ /* Check if the data is compressed or raw. */
+ if (BLI_file_magic_is_zstd(header)) {
+ return BLI_file_unzstd_to_mem_at_pos(data, header_entry->size_raw, file, header_entry->offset);
}
- return BLI_ungzip_file_to_mem_at_pos(
- ibuf->rect_float, header_entry->size_raw, file, header_entry->offset);
+ fseek(file, header_entry->offset, SEEK_SET);
+ return fread(data, 1, header_entry->size_raw, file);
}
static bool seq_disk_cache_read_header(FILE *file, DiskCacheHeader *header)
diff --git a/source/blender/sequencer/intern/sound.c b/source/blender/sequencer/intern/sound.c
index 1054dbeeba6..c53aacddcfe 100644
--- a/source/blender/sequencer/intern/sound.c
+++ b/source/blender/sequencer/intern/sound.c
@@ -111,7 +111,12 @@ void SEQ_sound_update_bounds(Scene *scene, Sequence *seq)
/* We have to take into account start frame of the sequence's scene! */
int startofs = seq->startofs + seq->anim_startofs + seq->scene->r.sfra;
- BKE_sound_move_scene_sound(scene, seq->scene_sound, seq->startdisp, seq->enddisp, startofs);
+ BKE_sound_move_scene_sound(scene,
+ seq->scene_sound,
+ seq->startdisp,
+ seq->enddisp,
+ startofs,
+ seq->sound->offset_time);
}
}
else {
diff --git a/source/blender/sequencer/intern/strip_add.c b/source/blender/sequencer/intern/strip_add.c
index dab5593be37..9081c655d2f 100644
--- a/source/blender/sequencer/intern/strip_add.c
+++ b/source/blender/sequencer/intern/strip_add.c
@@ -99,7 +99,7 @@ void SEQ_add_load_data_init(SeqLoadData *load_data,
static void seq_add_generic_update(Scene *scene, ListBase *seqbase, Sequence *seq)
{
- SEQ_sequence_base_unique_name_recursive(scene, seqbase, seq);
+ SEQ_sequence_base_unique_name_recursive(scene, &scene->ed->seqbase, seq);
SEQ_time_update_sequence_bounds(scene, seq);
SEQ_sort(seqbase);
SEQ_relations_invalidate_cache_composite(scene, seq);
@@ -382,9 +382,14 @@ Sequence *SEQ_add_image_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL
* \return created strip
*/
-Sequence *SEQ_add_sound_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqLoadData *load_data)
+Sequence *SEQ_add_sound_strip(Main *bmain,
+ Scene *scene,
+ ListBase *seqbase,
+ SeqLoadData *load_data,
+ const double audio_offset)
{
bSound *sound = BKE_sound_new_file(bmain, load_data->path); /* Handles relative paths. */
+ sound->offset_time = audio_offset;
SoundInfo info;
bool sound_loaded = BKE_sound_info_get(bmain, sound, &info);
@@ -398,14 +403,35 @@ Sequence *SEQ_add_sound_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL
return NULL;
}
- Sequence *seq = SEQ_sequence_alloc(
- seqbase, load_data->start_frame, load_data->channel, SEQ_TYPE_SOUND_RAM);
+ /* If this sound it part of a video, then the sound might start after the video.
+ * In this case we need to then offset the start frame of the audio so it syncs up
+ * properly with the video.
+ */
+ int start_frame_offset = info.start_offset * FPS;
+ double start_frame_offset_remainer = (info.start_offset * FPS - start_frame_offset) / FPS;
+
+ if (start_frame_offset_remainer > FLT_EPSILON) {
+ /* We can't represent a fraction of a frame, so skip the first frame fraction of sound so we
+ * start on a "whole" frame.
+ */
+ start_frame_offset++;
+ }
+
+ sound->offset_time += start_frame_offset_remainer;
+
+ Sequence *seq = SEQ_sequence_alloc(seqbase,
+ load_data->start_frame + start_frame_offset,
+ load_data->channel,
+ SEQ_TYPE_SOUND_RAM);
seq->sound = sound;
seq->scene_sound = NULL;
- /* We add a very small negative offset here, because
- * ceil(132.0) == 133.0, not nice with videos, see T47135. */
- seq->len = MAX2(1, (int)ceil((double)info.length * FPS - 1e-4));
+ /* We round the frame duration as the audio sample lengths usually does not
+ * line up with the video frames. Therefore we round this number to the
+ * nearest frame as the audio track usually overshoots or undershoots the
+ * end frame of the video by a little bit.
+ * See T47135 for under shoot example. */
+ seq->len = MAX2(1, round((info.length - sound->offset_time) * FPS));
Strip *strip = seq->strip;
/* We only need 1 element to store the filename. */
@@ -436,7 +462,8 @@ Sequence *SEQ_add_sound_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL
Sequence *SEQ_add_sound_strip(Main *UNUSED(bmain),
Scene *UNUSED(scene),
ListBase *UNUSED(seqbase),
- SeqLoadData *UNUSED(load_data))
+ SeqLoadData *UNUSED(load_data),
+ const double UNUSED(audio_offset))
{
return NULL;
}
@@ -477,7 +504,11 @@ Sequence *SEQ_add_meta_strip(Scene *scene, ListBase *seqbase, SeqLoadData *load_
* \param load_data: SeqLoadData with information necessary to create strip
* \return created strip
*/
-Sequence *SEQ_add_movie_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqLoadData *load_data)
+Sequence *SEQ_add_movie_strip(Main *bmain,
+ Scene *scene,
+ ListBase *seqbase,
+ SeqLoadData *load_data,
+ double *r_video_start_offset)
{
char path[sizeof(load_data->path)];
BLI_strncpy(path, load_data->path, sizeof(path));
@@ -552,6 +583,7 @@ Sequence *SEQ_add_movie_strip(Main *bmain, Scene *scene, ListBase *seqbase, SeqL
if (anim_arr[0] != NULL) {
seq->len = IMB_anim_get_duration(anim_arr[0], IMB_TC_RECORD_RUN);
+ *r_video_start_offset = IMD_anim_get_offset(anim_arr[0]);
IMB_anim_load_metadata(anim_arr[0]);
diff --git a/source/blender/sequencer/intern/strip_time.c b/source/blender/sequencer/intern/strip_time.c
index 68128690773..b73ac631693 100644
--- a/source/blender/sequencer/intern/strip_time.c
+++ b/source/blender/sequencer/intern/strip_time.c
@@ -34,6 +34,7 @@
#include "BKE_scene.h"
#include "BKE_sound.h"
+#include "DNA_sound_types.h"
#include "IMB_imbuf.h"
#include "SEQ_iterator.h"
@@ -134,7 +135,8 @@ static void seq_update_sound_bounds_recursive_impl(Scene *scene,
seq->scene_sound,
seq->start + startofs,
seq->start + seq->len - endofs,
- startofs + seq->anim_startofs);
+ startofs + seq->anim_startofs,
+ seq->sound->offset_time);
}
}
}
diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt
index e513c49c11b..4d65726fe2b 100644
--- a/source/blender/windowmanager/CMakeLists.txt
+++ b/source/blender/windowmanager/CMakeLists.txt
@@ -48,10 +48,6 @@ set(INC
${CMAKE_BINARY_DIR}/source/blender/makesdna/intern
)
-set(INC_SYS
- ${ZLIB_INCLUDE_DIRS}
-)
-
set(SRC
intern/wm.c
intern/wm_cursors.c
@@ -203,7 +199,8 @@ if(WITH_XR_OPENXR)
list(APPEND SRC
xr/intern/wm_xr.c
- xr/intern/wm_xr_actions.c
+ xr/intern/wm_xr_action.c
+ xr/intern/wm_xr_actionmap.c
xr/intern/wm_xr_draw.c
xr/intern/wm_xr_session.c
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index c0d408be2e0..3027df41e77 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -34,6 +34,7 @@
#include "BLI_sys_types.h"
#include "DNA_windowmanager_types.h"
#include "WM_keymap.h"
+#include "WM_types.h"
#ifdef __cplusplus
extern "C" {
@@ -73,8 +74,7 @@ struct wmNDOFMotionData;
#endif
#ifdef WITH_XR_OPENXR
-struct wmXrActionState;
-struct wmXrPose;
+struct wmXrRuntimeData;
#endif
typedef struct wmGizmo wmGizmo;
@@ -705,13 +705,13 @@ void WM_event_drag_image(struct wmDrag *, struct ImBuf *, float scale, int sx, i
void WM_drag_free(struct wmDrag *drag);
void WM_drag_data_free(int dragtype, void *poin);
void WM_drag_free_list(struct ListBase *lb);
-
struct wmDropBox *WM_dropbox_add(
ListBase *lb,
const char *idname,
- bool (*poll)(struct bContext *, struct wmDrag *, const struct wmEvent *event, const char **),
+ bool (*poll)(struct bContext *, struct wmDrag *, const struct wmEvent *event),
void (*copy)(struct wmDrag *, struct wmDropBox *),
- void (*cancel)(struct Main *, struct wmDrag *, struct wmDropBox *));
+ void (*cancel)(struct Main *, struct wmDrag *, struct wmDropBox *),
+ WMDropboxTooltipFunc tooltip);
ListBase *WM_dropboxmap_find(const char *idname, int spaceid, int regionid);
/* ID drag and drop */
@@ -727,6 +727,8 @@ void WM_drag_free_imported_drag_ID(struct Main *bmain,
struct wmDrag *drag,
struct wmDropBox *drop);
+const char *WM_drag_get_item_name(struct wmDrag *drag);
+
/* Set OpenGL viewport and scissor */
void wmViewport(const struct rcti *winrct);
void wmPartialViewport(rcti *drawrct, const rcti *winrct, const rcti *partialrct);
@@ -959,12 +961,18 @@ bool WM_xr_session_state_viewer_pose_rotation_get(const wmXrData *xr, float r_ro
bool WM_xr_session_state_viewer_pose_matrix_info_get(const wmXrData *xr,
float r_viewmat[4][4],
float *r_focal_len);
-bool WM_xr_session_state_controller_pose_location_get(const wmXrData *xr,
+bool WM_xr_session_state_controller_grip_location_get(const wmXrData *xr,
unsigned int subaction_idx,
float r_location[3]);
-bool WM_xr_session_state_controller_pose_rotation_get(const wmXrData *xr,
+bool WM_xr_session_state_controller_grip_rotation_get(const wmXrData *xr,
unsigned int subaction_idx,
float r_rotation[4]);
+bool WM_xr_session_state_controller_aim_location_get(const wmXrData *xr,
+ unsigned int subaction_idx,
+ float r_location[3]);
+bool WM_xr_session_state_controller_aim_rotation_get(const wmXrData *xr,
+ unsigned int subaction_idx,
+ float r_rotation[4]);
/* wm_xr_actions.c */
/* XR action functions to be called pre-XR session start.
@@ -977,41 +985,38 @@ bool WM_xr_action_create(wmXrData *xr,
eXrActionType type,
unsigned int count_subaction_paths,
const char **subaction_paths,
- const float *float_threshold,
struct wmOperatorType *ot,
struct IDProperty *op_properties,
- eXrOpFlag op_flag);
+ const char *haptic_name,
+ const int64_t *haptic_duration,
+ const float *haptic_frequency,
+ const float *haptic_amplitude,
+ eXrOpFlag op_flag,
+ eXrActionFlag action_flag,
+ eXrHapticFlag haptic_flag);
void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char *action_name);
-bool WM_xr_action_space_create(wmXrData *xr,
- const char *action_set_name,
- const char *action_name,
- unsigned int count_subaction_paths,
- const char **subaction_paths,
- const struct wmXrPose *poses);
-void WM_xr_action_space_destroy(wmXrData *xr,
- const char *action_set_name,
- const char *action_name,
- unsigned int count_subaction_paths,
- const char **subaction_paths);
bool WM_xr_action_binding_create(wmXrData *xr,
const char *action_set_name,
- const char *profile_path,
const char *action_name,
- unsigned int count_interaction_paths,
- const char **interaction_paths);
+ const char *profile_path,
+ unsigned int count_subaction_paths,
+ const char **subaction_paths,
+ const char **component_paths,
+ const float *float_thresholds,
+ const eXrAxisFlag *axis_flags,
+ const struct wmXrPose *poses);
void WM_xr_action_binding_destroy(wmXrData *xr,
const char *action_set_name,
- const char *profile_path,
const char *action_name,
- unsigned int count_interaction_paths,
- const char **interaction_paths);
+ const char *profile_path);
/* If action_set_name is NULL, then all action sets will be treated as active. */
bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name);
-bool WM_xr_controller_pose_action_set(wmXrData *xr,
- const char *action_set_name,
- const char *action_name);
+bool WM_xr_controller_pose_actions_set(wmXrData *xr,
+ const char *action_set_name,
+ const char *grip_action_name,
+ const char *aim_action_name);
/* XR action functions to be called post-XR session start. */
bool WM_xr_action_state_get(const wmXrData *xr,
@@ -1022,10 +1027,48 @@ bool WM_xr_action_state_get(const wmXrData *xr,
bool WM_xr_haptic_action_apply(wmXrData *xr,
const char *action_set_name,
const char *action_name,
+ const char *subaction_path,
const int64_t *duration,
const float *frequency,
const float *amplitude);
-void WM_xr_haptic_action_stop(wmXrData *xr, const char *action_set_name, const char *action_name);
+void WM_xr_haptic_action_stop(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ const char *subaction_path);
+
+/* wm_xr_actionmap.c */
+XrActionMap *WM_xr_actionmap_new(struct wmXrRuntimeData *runtime,
+ const char *name,
+ bool replace_existing);
+void WM_xr_actionmap_ensure_unique(struct wmXrRuntimeData *runtime, XrActionMap *actionmap);
+XrActionMap *WM_xr_actionmap_add_copy(struct wmXrRuntimeData *runtime, XrActionMap *am_src);
+bool WM_xr_actionmap_remove(struct wmXrRuntimeData *runtime, XrActionMap *actionmap);
+XrActionMap *WM_xr_actionmap_find(struct wmXrRuntimeData *runtime, const char *name);
+void WM_xr_actionmap_clear(XrActionMap *actionmap);
+void WM_xr_actionmaps_clear(struct wmXrRuntimeData *runtime);
+ListBase *WM_xr_actionmaps_get(struct wmXrRuntimeData *runtime);
+short WM_xr_actionmap_active_index_get(const struct wmXrRuntimeData *runtime);
+void WM_xr_actionmap_active_index_set(struct wmXrRuntimeData *runtime, short idx);
+short WM_xr_actionmap_selected_index_get(const struct wmXrRuntimeData *runtime);
+void WM_xr_actionmap_selected_index_set(struct wmXrRuntimeData *runtime, short idx);
+
+XrActionMapItem *WM_xr_actionmap_item_new(XrActionMap *actionmap,
+ const char *name,
+ bool replace_existing);
+void WM_xr_actionmap_item_ensure_unique(XrActionMap *actionmap, XrActionMapItem *ami);
+XrActionMapItem *WM_xr_actionmap_item_add_copy(XrActionMap *actionmap, XrActionMapItem *ami_src);
+bool WM_xr_actionmap_item_remove(XrActionMap *actionmap, XrActionMapItem *ami);
+XrActionMapItem *WM_xr_actionmap_item_find(XrActionMap *actionmap, const char *name);
+void WM_xr_actionmap_item_properties_update_ot(XrActionMapItem *ami);
+
+XrActionMapBinding *WM_xr_actionmap_binding_new(XrActionMapItem *ami,
+ const char *name,
+ bool replace_existing);
+void WM_xr_actionmap_binding_ensure_unique(XrActionMapItem *ami, XrActionMapBinding *amb);
+XrActionMapBinding *WM_xr_actionmap_binding_add_copy(XrActionMapItem *ami,
+ XrActionMapBinding *amb_src);
+bool WM_xr_actionmap_binding_remove(XrActionMapItem *ami, XrActionMapBinding *amb);
+XrActionMapBinding *WM_xr_actionmap_binding_find(XrActionMapItem *ami, const char *name);
#endif /* WITH_XR_OPENXR */
#ifdef __cplusplus
diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h
index 4ead0b2699c..843ceca7700 100644
--- a/source/blender/windowmanager/WM_types.h
+++ b/source/blender/windowmanager/WM_types.h
@@ -114,6 +114,8 @@ struct bContext;
struct wmEvent;
struct wmOperator;
struct wmWindowManager;
+struct wmDrag;
+struct wmDropBox;
#include "BLI_compiler_attrs.h"
#include "DNA_listBase.h"
@@ -326,11 +328,10 @@ typedef struct wmNotifier {
#define ND_LAYOUTDELETE (2 << 16)
#define ND_ANIMPLAY (4 << 16)
#define ND_GPENCIL (5 << 16)
-#define ND_EDITOR_CHANGED (6 << 16) /* Sent to new editors after switching to them. */
-#define ND_LAYOUTSET (7 << 16)
-#define ND_SKETCH (8 << 16)
-#define ND_WORKSPACE_SET (9 << 16)
-#define ND_WORKSPACE_DELETE (10 << 16)
+#define ND_LAYOUTSET (6 << 16)
+#define ND_SKETCH (7 << 16)
+#define ND_WORKSPACE_SET (8 << 16)
+#define ND_WORKSPACE_DELETE (9 << 16)
/* NC_SCENE Scene */
#define ND_SCENEBROWSE (1 << 16)
@@ -934,6 +935,11 @@ typedef struct wmDragAsset {
int import_type; /* eFileAssetImportType */
} wmDragAsset;
+typedef char *(*WMDropboxTooltipFunc)(struct bContext *,
+ struct wmDrag *,
+ const struct wmEvent *event,
+ struct wmDropBox *drop);
+
typedef struct wmDrag {
struct wmDrag *next, *prev;
@@ -949,8 +955,8 @@ typedef struct wmDrag {
float scale;
int sx, sy;
- /** If set, draws operator name. */
- char opname[200];
+ /** If filled, draws operator tooltip/operator name. */
+ char tooltip[200];
unsigned int flags;
/** List of wmDragIDs, all are guaranteed to have the same ID type. */
@@ -964,8 +970,8 @@ typedef struct wmDrag {
typedef struct wmDropBox {
struct wmDropBox *next, *prev;
- /** Test if the dropbox is active, then can print optype name. */
- bool (*poll)(struct bContext *, struct wmDrag *, const wmEvent *, const char **);
+ /** Test if the dropbox is active. */
+ bool (*poll)(struct bContext *, struct wmDrag *, const wmEvent *);
/** Before exec, this copies drag info to #wmDrop properties. */
void (*copy)(struct wmDrag *, struct wmDropBox *);
@@ -976,6 +982,9 @@ typedef struct wmDropBox {
*/
void (*cancel)(struct Main *, struct wmDrag *, struct wmDropBox *);
+ /** Custom tooltip shown during dragging. */
+ WMDropboxTooltipFunc tooltip;
+
/**
* If poll succeeds, operator is called.
* Not saved in file, so can be pointer.
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
index 5ec26a7b208..6f6a2402d89 100644
--- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
@@ -616,11 +616,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos,
const int viewport[4] = {0, 0, region->winx, region->winy};
float co_3d_origin[3];
- /* Avoid multiple calculations. */
- struct GPUMatrixUnproject_Precalc unproj_precalc;
- GPU_matrix_unproject_precalc(&unproj_precalc, rv3d->viewmat, rv3d->winmat, viewport);
-
- GPU_matrix_unproject_3fv_with_precalc(&unproj_precalc, co_screen, co_3d_origin);
+ GPU_matrix_unproject_3fv(co_screen, rv3d->viewinv, rv3d->winmat, viewport, co_3d_origin);
uint *buf_iter = buffer;
int hit_found = -1;
@@ -631,7 +627,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos,
wmGizmo *gz = visible_gizmos[buf_iter[3] >> 8];
float co_3d[3];
co_screen[2] = int_as_float(buf_iter[1]);
- GPU_matrix_unproject_3fv_with_precalc(&unproj_precalc, co_screen, co_3d);
+ GPU_matrix_unproject_3fv(co_screen, rv3d->viewinv, rv3d->winmat, viewport, co_3d);
float select_bias = gz->select_bias;
if ((gz->flag & WM_GIZMO_DRAW_NO_SCALE) == 0) {
select_bias *= gz->scale_final;
diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c
index 9657f8aa03c..e11ef52eb84 100644
--- a/source/blender/windowmanager/intern/wm.c
+++ b/source/blender/windowmanager/intern/wm.c
@@ -628,6 +628,7 @@ void wm_close_and_free_all(bContext *C, ListBase *wmlist)
wm_close_and_free(C, wm);
BLI_remlink(wmlist, wm);
BKE_libblock_free_data(&wm->id, true);
+ BKE_libblock_free_data_py(&wm->id);
MEM_freeN(wm);
}
}
diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c
index db72dd2a819..76bb93b681c 100644
--- a/source/blender/windowmanager/intern/wm_dragdrop.c
+++ b/source/blender/windowmanager/intern/wm_dragdrop.c
@@ -96,14 +96,16 @@ ListBase *WM_dropboxmap_find(const char *idname, int spaceid, int regionid)
wmDropBox *WM_dropbox_add(ListBase *lb,
const char *idname,
- bool (*poll)(bContext *, wmDrag *, const wmEvent *, const char **),
+ bool (*poll)(bContext *, wmDrag *, const wmEvent *),
void (*copy)(wmDrag *, wmDropBox *),
- void (*cancel)(struct Main *, wmDrag *, wmDropBox *))
+ void (*cancel)(struct Main *, wmDrag *, wmDropBox *),
+ WMDropboxTooltipFunc tooltip)
{
wmDropBox *drop = MEM_callocN(sizeof(wmDropBox), "wmDropBox");
drop->poll = poll;
drop->copy = copy;
drop->cancel = cancel;
+ drop->tooltip = tooltip;
drop->ot = WM_operatortype_find(idname, 0);
drop->opcontext = WM_OP_INVOKE_DEFAULT;
@@ -218,22 +220,33 @@ void WM_drag_free_list(struct ListBase *lb)
}
}
-static const char *dropbox_active(bContext *C,
- ListBase *handlers,
- wmDrag *drag,
- const wmEvent *event)
+static char *dropbox_tooltip(bContext *C, wmDrag *drag, const wmEvent *event, wmDropBox *drop)
+{
+ char *tooltip = NULL;
+ if (drop->tooltip) {
+ tooltip = drop->tooltip(C, drag, event, drop);
+ }
+ if (!tooltip) {
+ tooltip = BLI_strdup(WM_operatortype_name(drop->ot, drop->ptr));
+ }
+ /* XXX Doing translation here might not be ideal, but later we have no more
+ * access to ot (and hence op context)... */
+ return tooltip;
+}
+
+static wmDropBox *dropbox_active(bContext *C,
+ ListBase *handlers,
+ wmDrag *drag,
+ const wmEvent *event)
{
LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) {
if (handler_base->type == WM_HANDLER_TYPE_DROPBOX) {
wmEventHandler_Dropbox *handler = (wmEventHandler_Dropbox *)handler_base;
if (handler->dropboxes) {
LISTBASE_FOREACH (wmDropBox *, drop, handler->dropboxes) {
- const char *tooltip = NULL;
- if (drop->poll(C, drag, event, &tooltip) &&
+ if (drop->poll(C, drag, event) &&
WM_operator_poll_context(C, drop->ot, drop->opcontext)) {
- /* XXX Doing translation here might not be ideal, but later we have no more
- * access to ot (and hence op context)... */
- return (tooltip) ? tooltip : WM_operatortype_name(drop->ot, drop->ptr);
+ return drop;
}
}
}
@@ -242,29 +255,22 @@ static const char *dropbox_active(bContext *C,
return NULL;
}
-/* return active operator name when mouse is in box */
-static const char *wm_dropbox_active(bContext *C, wmDrag *drag, const wmEvent *event)
+/* return active operator tooltip/name when mouse is in box */
+static char *wm_dropbox_active(bContext *C, wmDrag *drag, const wmEvent *event)
{
wmWindow *win = CTX_wm_window(C);
- ScrArea *area = CTX_wm_area(C);
- ARegion *region = CTX_wm_region(C);
- const char *name;
-
- name = dropbox_active(C, &win->handlers, drag, event);
- if (name) {
- return name;
+ wmDropBox *drop = dropbox_active(C, &win->handlers, drag, event);
+ if (!drop) {
+ ScrArea *area = CTX_wm_area(C);
+ drop = dropbox_active(C, &area->handlers, drag, event);
}
-
- name = dropbox_active(C, &area->handlers, drag, event);
- if (name) {
- return name;
+ if (!drop) {
+ ARegion *region = CTX_wm_region(C);
+ drop = dropbox_active(C, &region->handlers, drag, event);
}
-
- name = dropbox_active(C, &region->handlers, drag, event);
- if (name) {
- return name;
+ if (drop) {
+ return dropbox_tooltip(C, drag, event, drop);
}
-
return NULL;
}
@@ -279,17 +285,18 @@ static void wm_drop_operator_options(bContext *C, wmDrag *drag, const wmEvent *e
return;
}
- drag->opname[0] = 0;
+ drag->tooltip[0] = 0;
/* check buttons (XXX todo rna and value) */
if (UI_but_active_drop_name(C)) {
- BLI_strncpy(drag->opname, IFACE_("Paste name"), sizeof(drag->opname));
+ BLI_strncpy(drag->tooltip, IFACE_("Paste name"), sizeof(drag->tooltip));
}
else {
- const char *opname = wm_dropbox_active(C, drag, event);
+ char *tooltip = wm_dropbox_active(C, drag, event);
- if (opname) {
- BLI_strncpy(drag->opname, opname, sizeof(drag->opname));
+ if (tooltip) {
+ BLI_strncpy(drag->tooltip, tooltip, sizeof(drag->tooltip));
+ MEM_freeN(tooltip);
// WM_cursor_modal_set(win, WM_CURSOR_COPY);
}
// else
@@ -462,7 +469,7 @@ static void wm_drop_operator_draw(const char *name, int x, int y)
UI_fontstyle_draw_simple_backdrop(fstyle, x, y, name, col_fg, col_bg);
}
-static const char *wm_drag_name(wmDrag *drag)
+const char *WM_drag_get_item_name(wmDrag *drag)
{
switch (drag->type) {
case WM_DRAG_ID: {
@@ -576,15 +583,15 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect)
}
if (rect) {
- int w = UI_fontstyle_string_width(fstyle, wm_drag_name(drag));
+ int w = UI_fontstyle_string_width(fstyle, WM_drag_get_item_name(drag));
drag_rect_minmax(rect, x, y, x + w, y + iconsize);
}
else {
- UI_fontstyle_draw_simple(fstyle, x, y, wm_drag_name(drag), text_col);
+ UI_fontstyle_draw_simple(fstyle, x, y, WM_drag_get_item_name(drag), text_col);
}
/* operator name with roundbox */
- if (drag->opname[0]) {
+ if (drag->tooltip[0]) {
if (drag->imb) {
x = cursorx - drag->sx / 2;
@@ -607,11 +614,11 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect)
}
if (rect) {
- int w = UI_fontstyle_string_width(fstyle, wm_drag_name(drag));
+ int w = UI_fontstyle_string_width(fstyle, WM_drag_get_item_name(drag));
drag_rect_minmax(rect, x, y, x + w, y + iconsize);
}
else {
- wm_drop_operator_draw(drag->opname, x, y);
+ wm_drop_operator_draw(drag->tooltip, x, y);
}
}
}
diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c
index f01e28f8822..328950cf8f9 100644
--- a/source/blender/windowmanager/intern/wm_draw.c
+++ b/source/blender/windowmanager/intern/wm_draw.c
@@ -455,7 +455,7 @@ static void wm_draw_region_buffer_create(ARegion *region, bool stereo, bool use_
* depth or multisample buffers. 3D view creates own buffers with
* the data it needs. */
GPUOffScreen *offscreen = GPU_offscreen_create(
- region->winx, region->winy, false, false, NULL);
+ region->winx, region->winy, false, GPU_RGBA8, NULL);
if (!offscreen) {
WM_report(RPT_ERROR, "Region could not be drawn!");
return;
@@ -691,46 +691,48 @@ static void wm_draw_window_offscreen(bContext *C, wmWindow *win, bool stereo)
/* Then do actual drawing of regions. */
LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
- if (region->visible && region->do_draw) {
- CTX_wm_region_set(C, region);
- bool use_viewport = WM_region_use_viewport(area, region);
-
- GPU_debug_group_begin(use_viewport ? "Viewport" : "ARegion");
-
- if (stereo && wm_draw_region_stereo_set(bmain, area, region, STEREO_LEFT_ID)) {
- wm_draw_region_buffer_create(region, true, use_viewport);
-
- for (int view = 0; view < 2; view++) {
- eStereoViews sview;
- if (view == 0) {
- sview = STEREO_LEFT_ID;
- }
- else {
- sview = STEREO_RIGHT_ID;
- 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 (!region->visible || !region->do_draw) {
+ continue;
+ }
+
+ CTX_wm_region_set(C, region);
+ bool use_viewport = WM_region_use_viewport(area, region);
+
+ GPU_debug_group_begin(use_viewport ? "Viewport" : "ARegion");
+
+ if (stereo && wm_draw_region_stereo_set(bmain, area, region, STEREO_LEFT_ID)) {
+ wm_draw_region_buffer_create(region, true, use_viewport);
+
+ for (int view = 0; view < 2; view++) {
+ eStereoViews sview;
+ if (view == 0) {
+ sview = STEREO_LEFT_ID;
}
- if (use_viewport) {
- GPUViewport *viewport = region->draw_buffer->viewport;
- GPU_viewport_stereo_composite(viewport, win->stereo3d_format);
+ else {
+ sview = STEREO_RIGHT_ID;
+ wm_draw_region_stereo_set(bmain, area, region, sview);
}
- }
- else {
- wm_draw_region_buffer_create(region, false, use_viewport);
- wm_draw_region_bind(region, 0);
+
+ wm_draw_region_bind(region, view);
ED_region_do_draw(C, region);
wm_draw_region_unbind(region);
}
+ if (use_viewport) {
+ GPUViewport *viewport = region->draw_buffer->viewport;
+ GPU_viewport_stereo_composite(viewport, win->stereo3d_format);
+ }
+ }
+ 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);
+ }
- GPU_debug_group_end();
+ GPU_debug_group_end();
- region->do_draw = false;
- CTX_wm_region_set(C, NULL);
- }
+ region->do_draw = false;
+ CTX_wm_region_set(C, NULL);
}
CTX_wm_area_set(C, NULL);
@@ -740,29 +742,30 @@ static void wm_draw_window_offscreen(bContext *C, wmWindow *win, bool stereo)
/* Draw menus into their own framebuffer. */
LISTBASE_FOREACH (ARegion *, region, &screen->regionbase) {
- if (region->visible) {
- CTX_wm_menu_set(C, region);
+ if (!region->visible) {
+ continue;
+ }
+ CTX_wm_menu_set(C, region);
- GPU_debug_group_begin("Menu");
+ GPU_debug_group_begin("Menu");
- if (region->type && region->type->layout) {
- /* UI code reads the OpenGL state, but we have to refresh
- * the UI layout beforehand in case the menu size changes. */
- wmViewport(&region->winrct);
- region->type->layout(C, region);
- }
+ if (region->type && region->type->layout) {
+ /* UI code reads the OpenGL state, but we have to refresh
+ * the UI layout beforehand in case the menu size changes. */
+ wmViewport(&region->winrct);
+ region->type->layout(C, region);
+ }
- 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);
+ 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);
- GPU_debug_group_end();
+ GPU_debug_group_end();
- region->do_draw = false;
- CTX_wm_menu_set(C, NULL);
- }
+ region->do_draw = false;
+ CTX_wm_menu_set(C, NULL);
}
}
@@ -786,8 +789,12 @@ static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view)
/* Blit non-overlapping area regions. */
ED_screen_areas_iter (win, screen, area) {
LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
- if (region->visible && region->overlap == false) {
- /* Blit from offscreen buffer. */
+ if (!region->visible) {
+ continue;
+ }
+
+ if (region->overlap == false) {
+ /* Blit from off-screen buffer. */
wm_draw_region_blit(region, view);
}
}
@@ -796,24 +803,25 @@ static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view)
/* Draw overlays and paint cursors. */
ED_screen_areas_iter (win, screen, area) {
LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
- if (region->visible) {
- const bool do_paint_cursor = (wm->paintcursors.first && region == screen->active_region);
- const bool do_draw_overlay = (region->type && region->type->draw_overlay);
- if (!(do_paint_cursor || do_draw_overlay)) {
- continue;
- }
+ if (!region->visible) {
+ continue;
+ }
+ const bool do_paint_cursor = (wm->paintcursors.first && region == screen->active_region);
+ const bool do_draw_overlay = (region->type && region->type->draw_overlay);
+ if (!(do_paint_cursor || do_draw_overlay)) {
+ continue;
+ }
- CTX_wm_area_set(C, area);
- CTX_wm_region_set(C, region);
- if (do_draw_overlay) {
- wm_region_draw_overlay(C, area, region);
- }
- if (do_paint_cursor) {
- wm_paintcursor_draw(C, area, region);
- }
- CTX_wm_region_set(C, NULL);
- CTX_wm_area_set(C, NULL);
+ CTX_wm_area_set(C, area);
+ CTX_wm_region_set(C, region);
+ if (do_draw_overlay) {
+ wm_region_draw_overlay(C, area, region);
+ }
+ if (do_paint_cursor) {
+ wm_paintcursor_draw(C, area, region);
}
+ CTX_wm_region_set(C, NULL);
+ CTX_wm_area_set(C, NULL);
}
}
wmWindowViewport(win);
@@ -821,7 +829,10 @@ static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view)
/* Blend in overlapping area regions */
ED_screen_areas_iter (win, screen, area) {
LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
- if (region->visible && region->overlap) {
+ if (!region->visible) {
+ continue;
+ }
+ if (region->overlap) {
wm_draw_region_blend(region, 0, true);
}
}
@@ -834,9 +845,10 @@ static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view)
/* Blend in floating regions (menus). */
LISTBASE_FOREACH (ARegion *, region, &screen->regionbase) {
- if (region->visible) {
- wm_draw_region_blend(region, 0, true);
+ if (!region->visible) {
+ continue;
}
+ wm_draw_region_blend(region, 0, true);
}
/* always draw, not only when screen tagged */
@@ -888,7 +900,7 @@ static void wm_draw_window(bContext *C, wmWindow *win)
* stereo methods, but it's less efficient than drawing directly. */
const int width = WM_window_pixels_x(win);
const int height = WM_window_pixels_y(win);
- GPUOffScreen *offscreen = GPU_offscreen_create(width, height, false, false, NULL);
+ GPUOffScreen *offscreen = GPU_offscreen_create(width, height, false, GPU_RGBA8, NULL);
if (offscreen) {
GPUTexture *texture = GPU_offscreen_color_texture(offscreen);
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index 5e29a22304c..b9a3dd0c3fb 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -2851,8 +2851,7 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers
if (event->custom == EVT_DATA_DRAGDROP) {
ListBase *lb = (ListBase *)event->customdata;
LISTBASE_FOREACH (wmDrag *, drag, lb) {
- const char *tooltip = NULL;
- if (drop->poll(C, drag, event, &tooltip)) {
+ if (drop->poll(C, drag, event)) {
/* Optionally copy drag information to operator properties. Don't call it if the
* operator fails anyway, it might do more than just set properties (e.g.
* typically import an asset). */
@@ -3472,8 +3471,8 @@ void wm_event_do_handlers(bContext *C)
}
CTX_wm_area_set(C, NULL);
- /* NOTE: do not escape on WM_HANDLER_BREAK,
- * mousemove needs handled for previous area. */
+ /* NOTE: do not escape on #WM_HANDLER_BREAK,
+ * mouse-move needs handled for previous area. */
}
}
diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c
index 06aaf95f232..f83511e76f0 100644
--- a/source/blender/windowmanager/intern/wm_files.c
+++ b/source/blender/windowmanager/intern/wm_files.c
@@ -28,11 +28,10 @@
* winsock stuff.
*/
#include <errno.h>
+#include <fcntl.h> /* for open flags (O_BINARY, O_RDONLY). */
#include <stddef.h>
#include <string.h>
-#include "zlib.h" /* wm_read_exotic() */
-
#ifdef WIN32
/* Need to include windows.h so _WIN32_IE is defined. */
# include <windows.h>
@@ -51,6 +50,7 @@
#include "BLI_blenlib.h"
#include "BLI_fileops_types.h"
+#include "BLI_filereader.h"
#include "BLI_linklist.h"
#include "BLI_math.h"
#include "BLI_system.h"
@@ -481,53 +481,64 @@ static void wm_init_userdef(Main *bmain)
/* intended to check for non-blender formats but for now it only reads blends */
static int wm_read_exotic(const char *name)
{
- int len;
- gzFile gzfile;
+ /* make sure we're not trying to read a directory.... */
+
+ int namelen = strlen(name);
+ if (namelen > 0 && ELEM(name[namelen - 1], '/', '\\')) {
+ return BKE_READ_EXOTIC_FAIL_PATH;
+ }
+
+ /* open the file. */
+ const int filedes = BLI_open(name, O_BINARY | O_RDONLY, 0);
+ if (filedes == -1) {
+ return BKE_READ_EXOTIC_FAIL_OPEN;
+ }
+
+ FileReader *rawfile = BLI_filereader_new_file(filedes);
+ if (rawfile == NULL) {
+ return BKE_READ_EXOTIC_FAIL_OPEN;
+ }
+
+ /* read the header (7 bytes are enough to identify all known types). */
char header[7];
- int retval;
+ if (rawfile->read(rawfile, header, sizeof(header)) != sizeof(header)) {
+ rawfile->close(rawfile);
+ return BKE_READ_EXOTIC_FAIL_FORMAT;
+ }
+ rawfile->seek(rawfile, 0, SEEK_SET);
- /* make sure we're not trying to read a directory.... */
+ /* check for uncompressed .blend */
+ if (STREQLEN(header, "BLENDER", 7)) {
+ rawfile->close(rawfile);
+ return BKE_READ_EXOTIC_OK_BLEND;
+ }
- len = strlen(name);
- if (len > 0 && ELEM(name[len - 1], '/', '\\')) {
- retval = BKE_READ_EXOTIC_FAIL_PATH;
+ /* check for compressed .blend */
+ FileReader *compressed_file = NULL;
+ if (BLI_file_magic_is_gzip(header)) {
+ /* In earlier versions of Blender (before 3.0), compressed files used Gzip instead of Zstd.
+ * While these files will no longer be written, there still needs to be reading support. */
+ compressed_file = BLI_filereader_new_gzip(rawfile);
+ }
+ else if (BLI_file_magic_is_zstd(header)) {
+ compressed_file = BLI_filereader_new_zstd(rawfile);
}
- else {
- gzfile = BLI_gzopen(name, "rb");
- if (gzfile == NULL) {
- retval = BKE_READ_EXOTIC_FAIL_OPEN;
- }
- else {
- len = gzread(gzfile, header, sizeof(header));
- gzclose(gzfile);
- if (len == sizeof(header) && STREQLEN(header, "BLENDER", 7)) {
- retval = BKE_READ_EXOTIC_OK_BLEND;
- }
- else {
- /* We may want to support loading other file formats
- * from their header bytes or file extension.
- * This used to be supported in the code below and may be added
- * back at some point. */
-#if 0
- WM_cursor_wait(true);
- if (is_foo_format(name)) {
- read_foo(name);
- retval = BKE_READ_EXOTIC_OK_OTHER;
- }
- else
-#endif
- {
- retval = BKE_READ_EXOTIC_FAIL_FORMAT;
- }
-#if 0
- WM_cursor_wait(false);
-#endif
- }
+ /* If a compression signature matches, try decompressing the start and check if it's a .blend */
+ if (compressed_file != NULL) {
+ size_t len = compressed_file->read(compressed_file, header, sizeof(header));
+ compressed_file->close(compressed_file);
+ if (len == sizeof(header) && STREQLEN(header, "BLENDER", 7)) {
+ return BKE_READ_EXOTIC_OK_BLEND;
}
}
+ else {
+ rawfile->close(rawfile);
+ }
- return retval;
+ /* Add check for future file formats here. */
+
+ return BKE_READ_EXOTIC_FAIL_FORMAT;
}
/** \} */
@@ -596,19 +607,33 @@ static void wm_file_read_pre(bContext *C, bool use_data, bool UNUSED(use_userdef
}
/**
+ * Parameters for #wm_file_read_post, also used for deferred initialization.
+ */
+struct wmFileReadPost_Params {
+ uint use_data : 1;
+ uint use_userdef : 1;
+
+ uint is_startup_file : 1;
+ uint is_factory_startup : 1;
+ uint reset_app_template : 1;
+};
+
+/**
* Logic shared between #WM_file_read & #wm_homefile_read,
* updates to make after reading a file.
*/
-static void wm_file_read_post(bContext *C,
- const bool is_startup_file,
- const bool is_factory_startup,
- const bool use_data,
- const bool use_userdef,
- const bool reset_app_template)
+static void wm_file_read_post(bContext *C, const struct wmFileReadPost_Params *params)
{
- bool addons_loaded = false;
wmWindowManager *wm = CTX_wm_manager(C);
+ const bool use_data = params->use_data;
+ const bool use_userdef = params->use_userdef;
+ const bool is_startup_file = params->is_startup_file;
+ const bool is_factory_startup = params->is_factory_startup;
+ const bool reset_app_template = params->reset_app_template;
+
+ bool addons_loaded = false;
+
if (use_data) {
if (!G.background) {
/* remove windows which failed to be added via WM_check */
@@ -797,22 +822,31 @@ static void file_read_reports_finalize(BlendFileReadReport *bf_reports)
bf_reports->count.resynced_lib_overrides,
duration_lib_override_recursive_resync_minutes,
duration_lib_override_recursive_resync_seconds);
+
if (bf_reports->resynced_lib_overrides_libraries_count != 0) {
for (LinkNode *node_lib = bf_reports->resynced_lib_overrides_libraries; node_lib != NULL;
node_lib = node_lib->next) {
Library *library = node_lib->link;
BKE_reportf(
- bf_reports->reports, RPT_INFO, "Library %s needs overrides resync.", library->filepath);
+ bf_reports->reports, RPT_INFO, "Library %s needs overrides resync", library->filepath);
}
}
+
if (bf_reports->count.missing_libraries != 0 || bf_reports->count.missing_linked_id != 0) {
BKE_reportf(bf_reports->reports,
RPT_WARNING,
- "%d libraries and %d linked data-blocks are missing, please check the "
- "Info and Outliner editors for details",
+ "%d libraries and %d linked data-blocks are missing (including %d ObjectData and "
+ "%d Proxies), please check the Info and Outliner editors for details",
bf_reports->count.missing_libraries,
- bf_reports->count.missing_linked_id);
+ bf_reports->count.missing_linked_id,
+ bf_reports->count.missing_obdata,
+ bf_reports->count.missing_obproxies);
}
+ else {
+ BLI_assert(bf_reports->count.missing_obdata == 0);
+ BLI_assert(bf_reports->count.missing_obproxies == 0);
+ }
+
if (bf_reports->resynced_lib_overrides_libraries_count != 0) {
BKE_reportf(bf_reports->reports,
RPT_WARNING,
@@ -901,7 +935,14 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports)
wm_history_file_update();
}
- wm_file_read_post(C, false, false, use_data, use_userdef, false);
+ wm_file_read_post(C,
+ &(const struct wmFileReadPost_Params){
+ .use_data = use_data,
+ .use_userdef = use_userdef,
+ .is_startup_file = false,
+ .is_factory_startup = false,
+ .reset_app_template = false,
+ });
bf_reports.duration.whole = PIL_check_seconds_timer() - bf_reports.duration.whole;
file_read_reports_finalize(&bf_reports);
@@ -984,31 +1025,18 @@ const char *WM_init_state_app_template_get(void)
/**
* Called on startup, (context entirely filled with NULLs)
- * or called for 'New File' both startup.blend and userpref.blend are checked.
- *
- * \param use_factory_settings:
- * Ignore on-disk startup file, use bundled `datatoc_startup_blend` instead.
- * Used for "Restore Factory Settings".
+ * or called for 'New File' both `startup.blend` and `userpref.blend` are checked.
*
- * \param use_userdef: Load factory settings as well as startup file.
- * Disabled for "File New" we don't want to reload preferences.
- *
- * \param filepath_startup_override:
- * Optional path pointing to an alternative blend file (may be NULL).
- *
- * \param app_template_override:
- * Template to use instead of the template defined in user-preferences.
- * When not-null, this is written into the user preferences.
+ * \param r_params_file_read_post: Support postponed initialization,
+ * needed for initial startup when only some sub-systems have been initialized.
+ * When non-null, #wm_file_read_post doesn't run, instead it's arguments are stored
+ * in this return argument.
+ * The caller is responsible for calling #wm_homefile_read_post with this return argument.
*/
-void wm_homefile_read(bContext *C,
- ReportList *reports,
- bool use_factory_settings,
- bool use_empty_data,
- bool use_data,
- bool use_userdef,
- const char *filepath_startup_override,
- const char *app_template_override,
- bool *r_is_factory_startup)
+void wm_homefile_read_ex(bContext *C,
+ const struct wmHomeFileRead_Params *params_homefile,
+ ReportList *reports,
+ struct wmFileReadPost_Params **r_params_file_read_post)
{
#if 0 /* UNUSED, keep as this may be needed later & the comment below isn't self evident. */
/* Context does not always have valid main pointer here. */
@@ -1017,6 +1045,14 @@ void wm_homefile_read(bContext *C,
ListBase wmbase;
bool success = false;
+ /* May be enabled, when the user configuration doesn't exist. */
+ const bool use_data = params_homefile->use_data;
+ const bool use_userdef = params_homefile->use_userdef;
+ bool use_factory_settings = params_homefile->use_factory_settings;
+ const bool use_empty_data = params_homefile->use_empty_data;
+ const char *filepath_startup_override = params_homefile->filepath_startup_override;
+ const char *app_template_override = params_homefile->app_template_override;
+
bool filepath_startup_is_factory = true;
char filepath_startup[FILE_MAX];
char filepath_userdef[FILE_MAX];
@@ -1304,13 +1340,45 @@ void wm_homefile_read(bContext *C,
G.save_over = 0;
}
- wm_file_read_post(C, true, is_factory_startup, use_data, use_userdef, reset_app_template);
+ {
+ const struct wmFileReadPost_Params params_file_read_post = {
+ .use_data = use_data,
+ .use_userdef = use_userdef,
+ .is_startup_file = true,
+ .is_factory_startup = is_factory_startup,
+ .reset_app_template = reset_app_template,
+ };
+ if (r_params_file_read_post == NULL) {
+ wm_file_read_post(C, &params_file_read_post);
+ }
+ else {
+ *r_params_file_read_post = MEM_mallocN(sizeof(struct wmFileReadPost_Params), __func__);
+ **r_params_file_read_post = params_file_read_post;
- if (r_is_factory_startup) {
- *r_is_factory_startup = is_factory_startup;
+ /* Match #wm_file_read_post which leaves the window cleared too. */
+ CTX_wm_window_set(C, NULL);
+ }
}
}
+void wm_homefile_read(bContext *C,
+ const struct wmHomeFileRead_Params *params_homefile,
+ ReportList *reports)
+{
+ wm_homefile_read_ex(C, params_homefile, reports, NULL);
+}
+
+/**
+ * Special case, support deferred execution of #wm_file_read_post,
+ * Needed when loading for the first time to workaround order of initialization bug, see T89046.
+ */
+void wm_homefile_read_post(struct bContext *C,
+ const struct wmFileReadPost_Params *params_file_read_post)
+{
+ wm_file_read_post(C, params_file_read_post);
+ MEM_freeN((void *)params_file_read_post);
+}
+
/* -------------------------------------------------------------------- */
/** \name Blend-File History API
* \{ */
@@ -2078,14 +2146,15 @@ static int wm_userpref_read_exec(bContext *C, wmOperator *op)
UserDef U_backup = U;
wm_homefile_read(C,
- op->reports,
- use_factory_settings,
- false,
- use_data,
- use_userdef,
- NULL,
- WM_init_state_app_template_get(),
- NULL);
+ &(const struct wmHomeFileRead_Params){
+ .use_data = use_data,
+ .use_userdef = use_userdef,
+ .use_factory_settings = use_factory_settings,
+ .use_empty_data = false,
+ .filepath_startup_override = NULL,
+ .app_template_override = WM_init_state_app_template_get(),
+ },
+ op->reports);
wm_userpref_read_exceptions(&U, &U_backup);
SET_FLAG_FROM_TEST(G.f, use_factory_settings, G_FLAG_USERPREF_NO_SAVE_ON_EXIT);
@@ -2224,16 +2293,17 @@ static int wm_homefile_read_exec(bContext *C, wmOperator *op)
app_template = WM_init_state_app_template_get();
}
- bool use_data = true;
wm_homefile_read(C,
- op->reports,
- use_factory_settings,
- use_empty_data,
- use_data,
- use_userdef,
- filepath,
- app_template,
- NULL);
+ &(const struct wmHomeFileRead_Params){
+ .use_data = true,
+ .use_userdef = use_userdef,
+ .use_factory_settings = use_factory_settings,
+ .use_empty_data = use_empty_data,
+ .filepath_startup_override = filepath,
+ .app_template_override = app_template,
+ },
+ op->reports);
+
if (use_splash) {
WM_init_splash(C);
}
@@ -3434,7 +3504,7 @@ static uiBlock *block_create__close_file_dialog(struct bContext *C,
BLI_split_file_part(blendfile_pathpath, filename, sizeof(filename));
}
else {
- STRNCPY(filename, IFACE_("untitled.blend"));
+ STRNCPY(filename, "untitled.blend");
}
uiItemL(layout, filename, ICON_NONE);
diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c
index fec5a516688..606c9252ff9 100644
--- a/source/blender/windowmanager/intern/wm_files_link.c
+++ b/source/blender/windowmanager/intern/wm_files_link.c
@@ -30,6 +30,8 @@
#include <stdio.h>
#include <string.h>
+#include "CLG_log.h"
+
#include "MEM_guardedalloc.h"
#include "DNA_ID.h"
@@ -76,6 +78,8 @@
#include "wm_files.h"
+static CLG_LogRef LOG = {"wm.files_link"};
+
/* -------------------------------------------------------------------- */
/** \name Link/Append Operator
* \{ */
@@ -315,7 +319,7 @@ static bool wm_link_append_item_poll(ReportList *reports,
short idcode;
if (!group || !name) {
- printf("skipping %s\n", path);
+ CLOG_WARN(&LOG, "Skipping %s", path);
return false;
}
@@ -485,11 +489,11 @@ static int wm_link_append_exec(bContext *C, wmOperator *op)
}
/* XXX We'd need re-entrant locking on Main for this to work... */
- /* BKE_main_lock(bmain); */
+ // BKE_main_lock(bmain);
wm_link_do(lapp_data, op->reports, bmain, scene, view_layer, CTX_wm_view3d(C));
- /* BKE_main_unlock(bmain); */
+ // BKE_main_unlock(bmain);
/* mark all library linked objects to be updated */
BKE_main_lib_objects_recalc_all(bmain);
@@ -759,12 +763,12 @@ static void lib_relocate_do_remap(Main *bmain,
BLI_assert(new_id);
}
if (new_id) {
-#ifdef PRINT_DEBUG
- printf("before remap of %s, old_id users: %d, new_id users: %d\n",
- old_id->name,
- old_id->us,
- new_id->us);
-#endif
+ CLOG_INFO(&LOG,
+ 4,
+ "Before remap of %s, old_id users: %d, new_id users: %d",
+ old_id->name,
+ old_id->us,
+ new_id->us);
BKE_libblock_remap_locked(bmain, old_id, new_id, remap_flags);
if (old_id->flag & LIB_FAKEUSER) {
@@ -772,12 +776,12 @@ static void lib_relocate_do_remap(Main *bmain,
id_fake_user_set(new_id);
}
-#ifdef PRINT_DEBUG
- printf("after remap of %s, old_id users: %d, new_id users: %d\n",
- old_id->name,
- old_id->us,
- new_id->us);
-#endif
+ CLOG_INFO(&LOG,
+ 4,
+ "After remap of %s, old_id users: %d, new_id users: %d",
+ old_id->name,
+ old_id->us,
+ new_id->us);
/* In some cases, new_id might become direct link, remove parent of library in this case. */
if (new_id->lib->parent && (new_id->tag & LIB_TAG_INDIRECT) == 0) {
@@ -831,7 +835,7 @@ static void lib_relocate_do_remap(Main *bmain,
}
}
-static void lib_relocate_do(Main *bmain,
+static void lib_relocate_do(bContext *C,
Library *library,
WMLinkAppendData *lapp_data,
ReportList *reports,
@@ -843,6 +847,10 @@ static void lib_relocate_do(Main *bmain,
LinkNode *itemlink;
int item_idx;
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+
/* Remove all IDs to be reloaded from Main. */
lba_idx = set_listbasepointers(bmain, lbarray);
while (lba_idx--) {
@@ -871,9 +879,7 @@ static void lib_relocate_do(Main *bmain,
item = wm_link_append_data_item_add(lapp_data, id->name + 2, idcode, id);
BLI_bitmap_set_all(item->libraries, true, lapp_data->num_libraries);
-#ifdef PRINT_DEBUG
- printf("\tdatablock to seek for: %s\n", id->name);
-#endif
+ CLOG_INFO(&LOG, 4, "Datablock to seek for: %s", id->name);
}
}
}
@@ -990,21 +996,31 @@ static void lib_relocate_do(Main *bmain,
}
}
- /* Update overrides of reloaded linked data-blocks.
- * Note that this will not necessarily fully update the override, it might need to be manually
- * 're-generated' depending on changes in linked data. */
+ /* Update overrides of reloaded linked data-blocks. */
ID *id;
FOREACH_MAIN_ID_BEGIN (bmain, id) {
if (ID_IS_LINKED(id) || !ID_IS_OVERRIDE_LIBRARY_REAL(id) ||
(id->tag & LIB_TAG_PRE_EXISTING) == 0) {
continue;
}
- if (id->override_library->reference->lib == library) {
+ if ((id->override_library->reference->tag & LIB_TAG_PRE_EXISTING) == 0) {
BKE_lib_override_library_update(bmain, id);
}
}
FOREACH_MAIN_ID_END;
+ /* Resync overrides if needed. */
+ if (!USER_EXPERIMENTAL_TEST(&U, no_override_auto_resync)) {
+ BKE_lib_override_library_main_resync(bmain,
+ scene,
+ view_layer,
+ &(struct BlendFileReadReport){
+ .reports = reports,
+ });
+ /* We need to rebuild some of the deleted override rules (for UI feedback purpose). */
+ BKE_lib_override_library_main_operations_create(bmain, true);
+ }
+
BKE_main_collection_sync(bmain);
BKE_main_lib_objects_recalc_all(bmain);
@@ -1039,7 +1055,7 @@ void WM_lib_reload(Library *lib, bContext *C, ReportList *reports)
wm_link_append_data_library_add(lapp_data, lib->filepath_abs);
- lib_relocate_do(CTX_data_main(C), lib, lapp_data, reports, true);
+ lib_relocate_do(C, lib, lapp_data, reports, true);
wm_link_append_data_free(lapp_data);
@@ -1103,9 +1119,7 @@ static int wm_lib_relocate_exec_do(bContext *C, wmOperator *op, bool do_reload)
}
if (BLI_path_cmp(lib->filepath_abs, path) == 0) {
-#ifdef PRINT_DEBUG
- printf("We are supposed to reload '%s' lib (%d)...\n", lib->filepath, lib->id.us);
-#endif
+ CLOG_INFO(&LOG, 4, "We are supposed to reload '%s' lib (%d)", lib->filepath, lib->id.us);
do_reload = true;
@@ -1115,9 +1129,8 @@ static int wm_lib_relocate_exec_do(bContext *C, wmOperator *op, bool do_reload)
else {
int totfiles = 0;
-#ifdef PRINT_DEBUG
- printf("We are supposed to relocate '%s' lib to new '%s' one...\n", lib->filepath, libname);
-#endif
+ CLOG_INFO(
+ &LOG, 4, "We are supposed to relocate '%s' lib to new '%s' one", lib->filepath, libname);
/* Check if something is indicated for relocate. */
prop = RNA_struct_find_property(op->ptr, "files");
@@ -1143,17 +1156,13 @@ static int wm_lib_relocate_exec_do(bContext *C, wmOperator *op, bool do_reload)
continue;
}
-#ifdef PRINT_DEBUG
- printf("\t candidate new lib to reload datablocks from: %s\n", path);
-#endif
+ CLOG_INFO(&LOG, 4, "\tCandidate new lib to reload datablocks from: %s", path);
wm_link_append_data_library_add(lapp_data, path);
}
RNA_END;
}
else {
-#ifdef PRINT_DEBUG
- printf("\t candidate new lib to reload datablocks from: %s\n", path);
-#endif
+ CLOG_INFO(&LOG, 4, "\tCandidate new lib to reload datablocks from: %s", path);
wm_link_append_data_library_add(lapp_data, path);
}
}
@@ -1162,7 +1171,7 @@ static int wm_lib_relocate_exec_do(bContext *C, wmOperator *op, bool do_reload)
lapp_data->flag |= BLO_LIBLINK_USE_PLACEHOLDERS | BLO_LIBLINK_FORCE_INDIRECT;
}
- lib_relocate_do(bmain, lib, lapp_data, op->reports, do_reload);
+ lib_relocate_do(C, lib, lapp_data, op->reports, do_reload);
wm_link_append_data_free(lapp_data);
diff --git a/source/blender/windowmanager/intern/wm_gesture_ops.c b/source/blender/windowmanager/intern/wm_gesture_ops.c
index 92ca0b87527..1c736647084 100644
--- a/source/blender/windowmanager/intern/wm_gesture_ops.c
+++ b/source/blender/windowmanager/intern/wm_gesture_ops.c
@@ -585,7 +585,7 @@ void wm_tweakevent_test(bContext *C, const wmEvent *event, int action)
}
else {
/* no tweaks if event was handled */
- if ((action & WM_HANDLER_BREAK)) {
+ if (action & WM_HANDLER_BREAK) {
WM_gesture_end(win, win->tweak);
}
else {
diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c
index d7ea47fc625..a8d2e000108 100644
--- a/source/blender/windowmanager/intern/wm_init_exit.c
+++ b/source/blender/windowmanager/intern/wm_init_exit.c
@@ -222,7 +222,10 @@ static void sound_jack_sync_callback(Main *bmain, int mode, double time)
}
}
-/* only called once, for startup */
+/**
+ * Initialize Blender and load the startup file & preferences
+ * (only called once).
+ */
void WM_init(bContext *C, int argc, const char **argv)
{
@@ -248,24 +251,22 @@ void WM_init(bContext *C, int argc, const char **argv)
ED_undosys_type_init();
- BKE_library_callback_free_notifier_reference_set(
- WM_main_remove_notifier_reference); /* lib_id.c */
- BKE_region_callback_free_gizmomap_set(wm_gizmomap_remove); /* screen.c */
+ BKE_library_callback_free_notifier_reference_set(WM_main_remove_notifier_reference);
+ BKE_region_callback_free_gizmomap_set(wm_gizmomap_remove);
BKE_region_callback_refresh_tag_gizmomap_set(WM_gizmomap_tag_refresh);
- BKE_library_callback_remap_editor_id_reference_set(
- WM_main_remap_editor_id_reference); /* lib_id.c */
- BKE_spacedata_callback_id_remap_set(ED_spacedata_id_remap); /* screen.c */
+ BKE_library_callback_remap_editor_id_reference_set(WM_main_remap_editor_id_reference);
+ BKE_spacedata_callback_id_remap_set(ED_spacedata_id_remap);
DEG_editors_set_update_cb(ED_render_id_flush_update, ED_render_scene_update);
- ED_spacetypes_init(); /* editors/space_api/spacetype.c */
+ ED_spacetypes_init();
ED_node_init_butfuncs();
BLF_init();
BLT_lang_init();
- /* Must call first before doing any '.blend' file reading,
- * since versioning code may create new IDs... See T57066. */
+ /* Must call first before doing any `.blend` file reading,
+ * since versioning code may create new IDs. See T57066. */
BLT_lang_set(NULL);
/* Init icons before reading .blend files for preview icons, which can
@@ -273,37 +274,57 @@ void WM_init(bContext *C, int argc, const char **argv)
* for scripts that do background processing with preview icons. */
BKE_icons_init(BIFICONID_LAST);
- /* reports can't be initialized before the wm,
+ /* Reports can't be initialized before the window-manager,
* but keep before file reading, since that may report errors */
wm_init_reports(C);
WM_msgbus_types_init();
- /* get the default database, plus a wm */
- bool is_factory_startup = true;
- const bool use_data = true;
- const bool use_userdef = true;
-
/* Studio-lights needs to be init before we read the home-file,
* otherwise the versioning cannot find the default studio-light. */
BKE_studiolight_init();
BLI_assert((G.fileflags & G_FILE_NO_UI) == 0);
- wm_homefile_read(C,
- NULL,
- G.factory_startup,
- false,
- use_data,
- use_userdef,
- NULL,
- WM_init_state_app_template_get(),
- &is_factory_startup);
-
- /* Call again to set from userpreferences... */
+ /**
+ * NOTE(@campbellbarton): Startup file and order of initialization.
+ *
+ * Loading #BLENDER_STARTUP_FILE, #BLENDER_USERPREF_FILE, starting Python and other sub-systems,
+ * have inter-dependencies, for example.
+ *
+ * - Some sub-systems depend on the preferences (initializing icons depend on the theme).
+ * - Add-ons depends on the preferences to know what has been enabled.
+ * - Add-ons depends on the window-manger to register their key-maps.
+ * - Evaluating the startup file depends on Python for animation-drivers (see T89046).
+ * - Starting Python depends on the startup file so key-maps can be added in the window-manger.
+ *
+ * Loading preferences early, then application subsystems and finally the startup data would
+ * simplify things if it weren't for key-maps being part of the window-manager
+ * which is blend file data.
+ * Creating a dummy window-manager early, or moving the key-maps into the preferences
+ * would resolve this and may be worth looking into long-term, see: D12184 for details.
+ */
+ struct wmFileReadPost_Params *params_file_read_post = NULL;
+ wm_homefile_read_ex(C,
+ &(const struct wmHomeFileRead_Params){
+ .use_data = true,
+ .use_userdef = true,
+ .use_factory_settings = G.factory_startup,
+ .use_empty_data = false,
+ .filepath_startup_override = NULL,
+ .app_template_override = WM_init_state_app_template_get(),
+ },
+ NULL,
+ &params_file_read_post);
+
+ /* NOTE: leave `G_MAIN->name` set to an empty string since this
+ * matches behavior after loading a new file. */
+ BLI_assert(G_MAIN->name[0] == '\0');
+
+ /* Call again to set from preferences. */
BLT_lang_set(NULL);
- /* For fsMenu. Called here so can include user preference paths if needed. */
+ /* For file-system. Called here so can include user preference paths if needed. */
ED_file_init();
/* That one is generated on demand, we need to be sure it's clear on init. */
@@ -312,12 +333,13 @@ void WM_init(bContext *C, int argc, const char **argv)
if (!G.background) {
#ifdef WITH_INPUT_NDOF
- /* sets 3D mouse deadzone */
+ /* Sets 3D mouse dead-zone. */
WM_ndof_deadzone_set(U.ndof_deadzone);
#endif
WM_init_opengl();
if (!WM_platform_support_perform_checks()) {
+ /* No attempt to avoid memory leaks here. */
exit(-1);
}
@@ -328,20 +350,11 @@ void WM_init(bContext *C, int argc, const char **argv)
ED_spacemacros_init();
- /* NOTE(campbell): there is a bug where python needs initializing before loading the
- * startup.blend because it may contain PyDrivers. It also needs to be after
- * initializing space types and other internal data.
- *
- * However can't redo this at the moment. Solution is to load python
- * before wm_homefile_read() or make py-drivers check if python is running.
- * Will try fix when the crash can be repeated. */
-
#ifdef WITH_PYTHON
BPY_python_start(C, argc, argv);
BPY_python_reset(C);
#else
- (void)argc; /* unused */
- (void)argv; /* unused */
+ UNUSED_VARS(argc, argv);
#endif
if (!G.background) {
@@ -358,14 +371,6 @@ void WM_init(bContext *C, int argc, const char **argv)
wm_history_file_read();
- /* allow a path of "", this is what happens when making a new file */
-#if 0
- if (BKE_main_blendfile_path_from_global()[0] == '\0') {
- BLI_join_dirfile(
- G_MAIN->name, sizeof(G_MAIN->name), BKE_appdir_folder_default(), "untitled.blend");
- }
-#endif
-
BLI_strncpy(G.lib, BKE_main_blendfile_path_from_global(), sizeof(G.lib));
#ifdef WITH_COMPOSITOR
@@ -375,30 +380,7 @@ void WM_init(bContext *C, int argc, const char **argv)
}
#endif
- {
- Main *bmain = CTX_data_main(C);
- /* NOTE: logic here is from wm_file_read_post,
- * call functions that depend on Python being initialized. */
-
- /* normally 'wm_homefile_read' will do this,
- * however python is not initialized when called from this function.
- *
- * unlikely any handlers are set but its possible,
- * note that recovering the last session does its own callbacks. */
- CTX_wm_window_set(C, CTX_wm_manager(C)->windows.first);
-
- BKE_callback_exec_null(bmain, BKE_CB_EVT_VERSION_UPDATE);
- BKE_callback_exec_null(bmain, BKE_CB_EVT_LOAD_POST);
- if (is_factory_startup) {
- BKE_callback_exec_null(bmain, BKE_CB_EVT_LOAD_FACTORY_STARTUP_POST);
- }
-
- wm_file_read_report(C, bmain);
-
- if (!G.background) {
- CTX_wm_window_set(C, NULL);
- }
- }
+ wm_homefile_read_post(C, params_file_read_post);
}
void WM_init_splash(bContext *C)
diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c
index 8a8ea18cc43..b5a038757c2 100644
--- a/source/blender/windowmanager/intern/wm_operators.c
+++ b/source/blender/windowmanager/intern/wm_operators.c
@@ -2766,10 +2766,7 @@ static void radial_control_cancel(bContext *C, wmOperator *op)
wmWindowManager *wm = CTX_wm_manager(C);
ScrArea *area = CTX_wm_area(C);
- if (rc->dial) {
- MEM_freeN(rc->dial);
- rc->dial = NULL;
- }
+ MEM_SAFE_FREE(rc->dial);
ED_area_status_text(area, NULL);
@@ -2959,10 +2956,7 @@ static int radial_control_modal(bContext *C, wmOperator *op, const wmEvent *even
if (event->val == KM_RELEASE) {
rc->slow_mode = false;
handled = true;
- if (rc->dial) {
- MEM_freeN(rc->dial);
- rc->dial = NULL;
- }
+ MEM_SAFE_FREE(rc->dial);
}
break;
}
@@ -3184,10 +3178,11 @@ static void redraw_timer_step(bContext *C,
LISTBASE_FOREACH (ScrArea *, area_iter, &screen->areabase) {
CTX_wm_area_set(C, area_iter);
LISTBASE_FOREACH (ARegion *, region_iter, &area_iter->regionbase) {
- if (region_iter->visible) {
- CTX_wm_region_set(C, region_iter);
- wm_draw_region_test(C, area_iter, region_iter);
+ if (!region_iter->visible) {
+ continue;
}
+ CTX_wm_region_set(C, region_iter);
+ wm_draw_region_test(C, area_iter, region_iter);
}
}
diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c
index 1b08b8dad92..93417213a65 100644
--- a/source/blender/windowmanager/intern/wm_window.c
+++ b/source/blender/windowmanager/intern/wm_window.c
@@ -1058,7 +1058,7 @@ void wm_window_make_drawable(wmWindowManager *wm, wmWindow *win)
BLI_assert(GPU_framebuffer_active_get() == GPU_framebuffer_back_get());
if (win != wm->windrawable && win->ghostwin) {
- // win->lmbut = 0; /* keeps hanging when mousepressed while other window opened */
+ // win->lmbut = 0; /* Keeps hanging when mouse-pressed while other window opened. */
wm_window_clear_drawable(wm);
if (G.debug & G_DEBUG_EVENTS) {
@@ -1416,7 +1416,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr
wm_event_add(win, &event);
- /* printf("Drop detected\n"); */
+ // printf("Drop detected\n");
/* add drag data to wm for paths: */
diff --git a/source/blender/windowmanager/wm_files.h b/source/blender/windowmanager/wm_files.h
index c7fe07cad7f..2fa5a68829e 100644
--- a/source/blender/windowmanager/wm_files.h
+++ b/source/blender/windowmanager/wm_files.h
@@ -24,6 +24,7 @@
#pragma once
struct Main;
+struct wmFileReadPost_Params;
struct wmGenericCallback;
struct wmOperatorType;
@@ -33,15 +34,45 @@ extern "C" {
/* wm_files.c */
void wm_history_file_read(void);
+
+struct wmHomeFileRead_Params {
+ /** Load data, disable when only loading user preferences. */
+ unsigned int use_data : 1;
+ /** Load factory settings as well as startup file (disabled for "File New"). */
+ unsigned int use_userdef : 1;
+
+ /**
+ * Ignore on-disk startup file, use bundled `datatoc_startup_blend` instead.
+ * Used for "Restore Factory Settings".
+ */
+ unsigned int use_factory_settings : 1;
+ /**
+ * Load the startup file without any data-blocks.
+ * Useful for automated content generation, so the file starts without data.
+ */
+ unsigned int use_empty_data : 1;
+ /**
+ * Optional path pointing to an alternative blend file (may be NULL).
+ */
+ const char *filepath_startup_override;
+ /**
+ * Template to use instead of the template defined in user-preferences.
+ * When not-null, this is written into the user preferences.
+ */
+ const char *app_template_override;
+};
+
+void wm_homefile_read_ex(struct bContext *C,
+ const struct wmHomeFileRead_Params *params_homefile,
+ struct ReportList *reports,
+ struct wmFileReadPost_Params **r_params_file_read_post);
void wm_homefile_read(struct bContext *C,
- struct ReportList *reports,
- bool use_factory_settings,
- bool use_empty_data,
- bool use_data,
- bool use_userdef,
- const char *filepath_startup_override,
- const char *app_template_override,
- bool *r_is_factory_startup);
+ const struct wmHomeFileRead_Params *params_homefile,
+ struct ReportList *reports);
+
+void wm_homefile_read_post(struct bContext *C,
+ const struct wmFileReadPost_Params *params_file_read_post);
+
void wm_file_read_report(bContext *C, struct Main *bmain);
void wm_close_file_dialog(bContext *C, struct wmGenericCallback *post_action);
diff --git a/source/blender/windowmanager/xr/intern/wm_xr.c b/source/blender/windowmanager/xr/intern/wm_xr.c
index 2a67c2bee9f..3091a3a19f1 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr.c
@@ -115,6 +115,7 @@ bool wm_xr_init(wmWindowManager *wm)
void wm_xr_exit(wmWindowManager *wm)
{
if (wm->xr.runtime != NULL) {
+ WM_xr_actionmaps_clear(wm->xr.runtime);
wm_xr_runtime_data_free(&wm->xr.runtime);
}
if (wm->xr.session_settings.shading.prop) {
@@ -148,6 +149,7 @@ bool wm_xr_events_handle(wmWindowManager *wm)
wmXrRuntimeData *wm_xr_runtime_data_create(void)
{
wmXrRuntimeData *runtime = MEM_callocN(sizeof(*runtime), __func__);
+ runtime->actactionmap = runtime->selactionmap = -1;
return runtime;
}
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_actions.c b/source/blender/windowmanager/xr/intern/wm_xr_action.c
index 7eabd29baa0..ba347c537ec 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_actions.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr_action.c
@@ -23,6 +23,7 @@
* All functions are designed to be usable by RNA / the Python API.
*/
+#include "BLI_listbase.h"
#include "BLI_math.h"
#include "GHOST_C-api.h"
@@ -56,6 +57,9 @@ static void action_set_destroy(void *val)
MEM_SAFE_FREE(action_set->name);
+ BLI_freelistN(&action_set->active_modal_actions);
+ BLI_freelistN(&action_set->active_haptic_actions);
+
MEM_freeN(action_set);
}
@@ -68,10 +72,15 @@ static wmXrAction *action_create(const char *action_name,
eXrActionType type,
unsigned int count_subaction_paths,
const char **subaction_paths,
- const float *float_threshold,
wmOperatorType *ot,
IDProperty *op_properties,
- eXrOpFlag op_flag)
+ const char *haptic_name,
+ const int64_t *haptic_duration,
+ const float *haptic_frequency,
+ const float *haptic_amplitude,
+ eXrOpFlag op_flag,
+ eXrActionFlag action_flag,
+ eXrHapticFlag haptic_flag)
{
wmXrAction *action = MEM_callocN(sizeof(*action), __func__);
action->name = MEM_mallocN(strlen(action_name) + 1, "XrAction_Name");
@@ -109,15 +118,32 @@ static wmXrAction *action_create(const char *action_name,
action->states = MEM_calloc_arrayN(count, size, "XrAction_States");
action->states_prev = MEM_calloc_arrayN(count, size, "XrAction_StatesPrev");
- if (float_threshold) {
- BLI_assert(type == XR_FLOAT_INPUT || type == XR_VECTOR2F_INPUT);
- action->float_threshold = *float_threshold;
- CLAMP(action->float_threshold, 0.0f, 1.0f);
+ const bool is_float_action = (type == XR_FLOAT_INPUT || type == XR_VECTOR2F_INPUT);
+ const bool is_button_action = (is_float_action || type == XR_BOOLEAN_INPUT);
+ if (is_float_action) {
+ action->float_thresholds = MEM_calloc_arrayN(
+ count, sizeof(*action->float_thresholds), "XrAction_FloatThresholds");
+ }
+ if (is_button_action) {
+ action->axis_flags = MEM_calloc_arrayN(
+ count, sizeof(*action->axis_flags), "XrAction_AxisFlags");
}
action->ot = ot;
action->op_properties = op_properties;
+
+ if (haptic_name) {
+ BLI_assert(is_button_action);
+ action->haptic_name = MEM_mallocN(strlen(haptic_name) + 1, "XrAction_HapticName");
+ strcpy(action->haptic_name, haptic_name);
+ action->haptic_duration = *haptic_duration;
+ action->haptic_frequency = *haptic_frequency;
+ action->haptic_amplitude = *haptic_amplitude;
+ }
+
action->op_flag = op_flag;
+ action->action_flag = action_flag;
+ action->haptic_flag = haptic_flag;
return action;
}
@@ -140,6 +166,11 @@ static void action_destroy(void *val)
MEM_SAFE_FREE(action->states);
MEM_SAFE_FREE(action->states_prev);
+ MEM_SAFE_FREE(action->float_thresholds);
+ MEM_SAFE_FREE(action->axis_flags);
+
+ MEM_SAFE_FREE(action->haptic_name);
+
MEM_freeN(action);
}
@@ -179,13 +210,14 @@ void WM_xr_action_set_destroy(wmXrData *xr, const char *action_set_name)
wmXrSessionState *session_state = &xr->runtime->session_state;
if (action_set == session_state->active_action_set) {
- if (action_set->controller_pose_action) {
+ if (action_set->controller_grip_action || action_set->controller_aim_action) {
wm_xr_session_controller_data_clear(session_state);
- action_set->controller_pose_action = NULL;
- }
- if (action_set->active_modal_action) {
- action_set->active_modal_action = NULL;
+ action_set->controller_grip_action = action_set->controller_aim_action = NULL;
}
+
+ BLI_freelistN(&action_set->active_modal_actions);
+ BLI_freelistN(&action_set->active_haptic_actions);
+
session_state->active_action_set = NULL;
}
@@ -198,10 +230,15 @@ bool WM_xr_action_create(wmXrData *xr,
eXrActionType type,
unsigned int count_subaction_paths,
const char **subaction_paths,
- const float *float_threshold,
wmOperatorType *ot,
IDProperty *op_properties,
- eXrOpFlag op_flag)
+ const char *haptic_name,
+ const int64_t *haptic_duration,
+ const float *haptic_frequency,
+ const float *haptic_amplitude,
+ eXrOpFlag op_flag,
+ eXrActionFlag action_flag,
+ eXrHapticFlag haptic_flag)
{
if (action_find(xr, action_set_name, action_name)) {
return false;
@@ -211,16 +248,23 @@ bool WM_xr_action_create(wmXrData *xr,
type,
count_subaction_paths,
subaction_paths,
- float_threshold,
ot,
op_properties,
- op_flag);
+ haptic_name,
+ haptic_duration,
+ haptic_frequency,
+ haptic_amplitude,
+ op_flag,
+ action_flag,
+ haptic_flag);
GHOST_XrActionInfo info = {
.name = action_name,
.count_subaction_paths = count_subaction_paths,
.subaction_paths = subaction_paths,
.states = action->states,
+ .float_thresholds = action->float_thresholds,
+ .axis_flags = (int16_t *)action->axis_flags,
.customdata_free_fn = action_destroy,
.customdata = action,
};
@@ -257,110 +301,88 @@ void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char
return;
}
- if (action_set->controller_pose_action &&
- STREQ(action_set->controller_pose_action->name, action_name)) {
- if (action_set == xr->runtime->session_state.active_action_set) {
- wm_xr_session_controller_data_clear(&xr->runtime->session_state);
- }
- action_set->controller_pose_action = NULL;
- }
- if (action_set->active_modal_action &&
- STREQ(action_set->active_modal_action->name, action_name)) {
- action_set->active_modal_action = NULL;
- }
-
wmXrAction *action = action_find(xr, action_set_name, action_name);
if (!action) {
return;
}
-}
-
-bool WM_xr_action_space_create(wmXrData *xr,
- const char *action_set_name,
- const char *action_name,
- unsigned int count_subaction_paths,
- const char **subaction_paths,
- const wmXrPose *poses)
-{
- GHOST_XrActionSpaceInfo info = {
- .action_name = action_name,
- .count_subaction_paths = count_subaction_paths,
- .subaction_paths = subaction_paths,
- };
- GHOST_XrPose *ghost_poses = MEM_malloc_arrayN(
- count_subaction_paths, sizeof(*ghost_poses), __func__);
- for (unsigned int i = 0; i < count_subaction_paths; ++i) {
- const wmXrPose *pose = &poses[i];
- GHOST_XrPose *ghost_pose = &ghost_poses[i];
- copy_v3_v3(ghost_pose->position, pose->position);
- copy_qt_qt(ghost_pose->orientation_quat, pose->orientation_quat);
+ if ((action_set->controller_grip_action &&
+ STREQ(action_set->controller_grip_action->name, action_name)) ||
+ (action_set->controller_aim_action &&
+ STREQ(action_set->controller_aim_action->name, action_name))) {
+ if (action_set == xr->runtime->session_state.active_action_set) {
+ wm_xr_session_controller_data_clear(&xr->runtime->session_state);
+ }
+ action_set->controller_grip_action = action_set->controller_aim_action = NULL;
}
- info.poses = ghost_poses;
- bool ret = GHOST_XrCreateActionSpaces(xr->runtime->context, action_set_name, 1, &info) ? true :
- false;
- MEM_freeN(ghost_poses);
- return ret;
-}
+ LISTBASE_FOREACH (LinkData *, ld, &action_set->active_modal_actions) {
+ wmXrAction *active_modal_action = ld->data;
+ if (STREQ(active_modal_action->name, action_name)) {
+ BLI_freelinkN(&action_set->active_modal_actions, ld);
+ break;
+ }
+ }
-void WM_xr_action_space_destroy(wmXrData *xr,
- const char *action_set_name,
- const char *action_name,
- unsigned int count_subaction_paths,
- const char **subaction_paths)
-{
- GHOST_XrActionSpaceInfo info = {
- .action_name = action_name,
- .count_subaction_paths = count_subaction_paths,
- .subaction_paths = subaction_paths,
- };
+ LISTBASE_FOREACH_MUTABLE (wmXrHapticAction *, ha, &action_set->active_haptic_actions) {
+ if (STREQ(ha->action->name, action_name)) {
+ BLI_freelinkN(&action_set->active_haptic_actions, ha);
+ }
+ }
- GHOST_XrDestroyActionSpaces(xr->runtime->context, action_set_name, 1, &info);
+ GHOST_XrDestroyActions(xr->runtime->context, action_set_name, 1, &action_name);
}
bool WM_xr_action_binding_create(wmXrData *xr,
const char *action_set_name,
- const char *profile_path,
const char *action_name,
- unsigned int count_interaction_paths,
- const char **interaction_paths)
+ const char *profile_path,
+ unsigned int count_subaction_paths,
+ const char **subaction_paths,
+ const char **component_paths,
+ const float *float_thresholds,
+ const eXrAxisFlag *axis_flags,
+ const struct wmXrPose *poses)
{
- GHOST_XrActionBindingInfo binding_info = {
- .action_name = action_name,
- .count_interaction_paths = count_interaction_paths,
- .interaction_paths = interaction_paths,
- };
+ GHOST_XrActionBindingInfo *binding_infos = MEM_calloc_arrayN(
+ count_subaction_paths, sizeof(*binding_infos), __func__);
+
+ for (unsigned int i = 0; i < count_subaction_paths; ++i) {
+ GHOST_XrActionBindingInfo *binding_info = &binding_infos[i];
+ binding_info->component_path = component_paths[i];
+ if (float_thresholds) {
+ binding_info->float_threshold = float_thresholds[i];
+ }
+ if (axis_flags) {
+ binding_info->axis_flag = axis_flags[i];
+ }
+ if (poses) {
+ copy_v3_v3(binding_info->pose.position, poses[i].position);
+ copy_qt_qt(binding_info->pose.orientation_quat, poses[i].orientation_quat);
+ }
+ }
GHOST_XrActionProfileInfo profile_info = {
+ .action_name = action_name,
.profile_path = profile_path,
- .count_bindings = 1,
- .bindings = &binding_info,
+ .count_subaction_paths = count_subaction_paths,
+ .subaction_paths = subaction_paths,
+ .bindings = binding_infos,
};
- return GHOST_XrCreateActionBindings(xr->runtime->context, action_set_name, 1, &profile_info);
+ bool ret = GHOST_XrCreateActionBindings(xr->runtime->context, action_set_name, 1, &profile_info);
+
+ MEM_freeN(binding_infos);
+ return ret;
}
void WM_xr_action_binding_destroy(wmXrData *xr,
const char *action_set_name,
- const char *profile_path,
const char *action_name,
- unsigned int count_interaction_paths,
- const char **interaction_paths)
+ const char *profile_path)
{
- GHOST_XrActionBindingInfo binding_info = {
- .action_name = action_name,
- .count_interaction_paths = count_interaction_paths,
- .interaction_paths = interaction_paths,
- };
-
- GHOST_XrActionProfileInfo profile_info = {
- .profile_path = profile_path,
- .count_bindings = 1,
- .bindings = &binding_info,
- };
-
- GHOST_XrDestroyActionBindings(xr->runtime->context, action_set_name, 1, &profile_info);
+ GHOST_XrDestroyActionBindings(
+ xr->runtime->context, action_set_name, 1, &action_name, &profile_path);
}
bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name)
@@ -371,46 +393,64 @@ bool WM_xr_active_action_set_set(wmXrData *xr, const char *action_set_name)
}
{
- /* Unset active modal action (if any). */
+ /* Clear any active modal/haptic actions. */
wmXrActionSet *active_action_set = xr->runtime->session_state.active_action_set;
if (active_action_set) {
- wmXrAction *active_modal_action = active_action_set->active_modal_action;
- if (active_modal_action) {
- if (active_modal_action->active_modal_path) {
- active_modal_action->active_modal_path = NULL;
- }
- active_action_set->active_modal_action = NULL;
- }
+ BLI_freelistN(&active_action_set->active_modal_actions);
+ BLI_freelistN(&active_action_set->active_haptic_actions);
}
}
xr->runtime->session_state.active_action_set = action_set;
- if (action_set->controller_pose_action) {
- wm_xr_session_controller_data_populate(action_set->controller_pose_action, xr);
+ if (action_set->controller_grip_action && action_set->controller_aim_action) {
+ wm_xr_session_controller_data_populate(
+ action_set->controller_grip_action, action_set->controller_aim_action, xr);
+ }
+ else {
+ wm_xr_session_controller_data_clear(&xr->runtime->session_state);
}
return true;
}
-bool WM_xr_controller_pose_action_set(wmXrData *xr,
- const char *action_set_name,
- const char *action_name)
+bool WM_xr_controller_pose_actions_set(wmXrData *xr,
+ const char *action_set_name,
+ const char *grip_action_name,
+ const char *aim_action_name)
{
wmXrActionSet *action_set = action_set_find(xr, action_set_name);
if (!action_set) {
return false;
}
- wmXrAction *action = action_find(xr, action_set_name, action_name);
- if (!action) {
+ wmXrAction *grip_action = action_find(xr, action_set_name, grip_action_name);
+ if (!grip_action) {
return false;
}
- action_set->controller_pose_action = action;
+ wmXrAction *aim_action = action_find(xr, action_set_name, aim_action_name);
+ if (!aim_action) {
+ return false;
+ }
+
+ /* Ensure consistent subaction paths. */
+ const unsigned int count = grip_action->count_subaction_paths;
+ if (count != aim_action->count_subaction_paths) {
+ return false;
+ }
+
+ for (unsigned int i = 0; i < count; ++i) {
+ if (!STREQ(grip_action->subaction_paths[i], aim_action->subaction_paths[i])) {
+ return false;
+ }
+ }
+
+ action_set->controller_grip_action = grip_action;
+ action_set->controller_aim_action = aim_action;
if (action_set == xr->runtime->session_state.active_action_set) {
- wm_xr_session_controller_data_populate(action, xr);
+ wm_xr_session_controller_data_populate(grip_action, aim_action, xr);
}
return true;
@@ -427,12 +467,12 @@ bool WM_xr_action_state_get(const wmXrData *xr,
return false;
}
- BLI_assert(action->type == (eXrActionType)r_state->type);
+ r_state->type = (int)action->type;
/* Find the action state corresponding to the subaction path. */
for (unsigned int i = 0; i < action->count_subaction_paths; ++i) {
if (STREQ(subaction_path, action->subaction_paths[i])) {
- switch ((eXrActionType)r_state->type) {
+ switch (action->type) {
case XR_BOOLEAN_INPUT:
r_state->state_boolean = ((bool *)action->states)[i];
break;
@@ -462,19 +502,28 @@ bool WM_xr_action_state_get(const wmXrData *xr,
bool WM_xr_haptic_action_apply(wmXrData *xr,
const char *action_set_name,
const char *action_name,
+ const char *subaction_path,
const int64_t *duration,
const float *frequency,
const float *amplitude)
{
- return GHOST_XrApplyHapticAction(
- xr->runtime->context, action_set_name, action_name, duration, frequency, amplitude) ?
+ return GHOST_XrApplyHapticAction(xr->runtime->context,
+ action_set_name,
+ action_name,
+ subaction_path,
+ duration,
+ frequency,
+ amplitude) ?
true :
false;
}
-void WM_xr_haptic_action_stop(wmXrData *xr, const char *action_set_name, const char *action_name)
+void WM_xr_haptic_action_stop(wmXrData *xr,
+ const char *action_set_name,
+ const char *action_name,
+ const char *subaction_path)
{
- GHOST_XrStopHapticAction(xr->runtime->context, action_set_name, action_name);
+ GHOST_XrStopHapticAction(xr->runtime->context, action_set_name, action_name, subaction_path);
}
/** \} */ /* XR-Action API */
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c b/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c
new file mode 100644
index 00000000000..f9ad34b5a9b
--- /dev/null
+++ b/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c
@@ -0,0 +1,567 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup wm
+ *
+ * \name Window-Manager XR Action Maps
+ *
+ * XR actionmap API, similar to WM keymap API.
+ */
+
+#include <math.h>
+#include <string.h>
+
+#include "BKE_context.h"
+#include "BKE_idprop.h"
+
+#include "BLI_listbase.h"
+#include "BLI_string.h"
+
+#include "GHOST_Types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "wm_xr_intern.h"
+
+#define WM_XR_ACTIONMAP_STR_DEFAULT "actionmap"
+#define WM_XR_ACTIONMAP_ITEM_STR_DEFAULT "action"
+#define WM_XR_ACTIONMAP_BINDING_STR_DEFAULT "binding"
+
+/* -------------------------------------------------------------------- */
+/** \name Action Map Binding
+ *
+ * Binding in an XR action map item, that maps an action to an XR input.
+ * \{ */
+
+XrActionMapBinding *WM_xr_actionmap_binding_new(XrActionMapItem *ami,
+ const char *name,
+ bool replace_existing)
+{
+ XrActionMapBinding *amb_prev = WM_xr_actionmap_binding_find(ami, name);
+ if (amb_prev && replace_existing) {
+ return amb_prev;
+ }
+
+ XrActionMapBinding *amb = MEM_callocN(sizeof(XrActionMapBinding), __func__);
+ BLI_strncpy(amb->name, name, MAX_NAME);
+ if (amb_prev) {
+ WM_xr_actionmap_binding_ensure_unique(ami, amb);
+ }
+
+ BLI_addtail(&ami->bindings, amb);
+
+ /* Set non-zero threshold by default. */
+ amb->float_threshold = 0.3f;
+
+ return amb;
+}
+
+static XrActionMapBinding *wm_xr_actionmap_binding_find_except(XrActionMapItem *ami,
+ const char *name,
+ XrActionMapBinding *ambexcept)
+{
+ LISTBASE_FOREACH (XrActionMapBinding *, amb, &ami->bindings) {
+ if (STREQLEN(name, amb->name, MAX_NAME) && (amb != ambexcept)) {
+ return amb;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Ensure unique name among all action map bindings.
+ */
+void WM_xr_actionmap_binding_ensure_unique(XrActionMapItem *ami, XrActionMapBinding *amb)
+{
+ char name[MAX_NAME];
+ char *suffix;
+ size_t baselen;
+ size_t idx = 0;
+
+ BLI_strncpy(name, amb->name, MAX_NAME);
+ baselen = BLI_strnlen(name, MAX_NAME);
+ suffix = &name[baselen];
+
+ while (wm_xr_actionmap_binding_find_except(ami, name, amb)) {
+ if ((baselen + 1) + (log10(++idx) + 1) > MAX_NAME) {
+ /* Use default base name. */
+ BLI_strncpy(name, WM_XR_ACTIONMAP_BINDING_STR_DEFAULT, MAX_NAME);
+ baselen = BLI_strnlen(name, MAX_NAME);
+ suffix = &name[baselen];
+ idx = 0;
+ }
+ else {
+ BLI_snprintf(suffix, MAX_NAME, "%zu", idx);
+ }
+ }
+
+ BLI_strncpy(amb->name, name, MAX_NAME);
+}
+
+static XrActionMapBinding *wm_xr_actionmap_binding_copy(XrActionMapBinding *amb_src)
+{
+ XrActionMapBinding *amb_dst = MEM_dupallocN(amb_src);
+
+ amb_dst->prev = amb_dst->next = NULL;
+
+ return amb_dst;
+}
+
+XrActionMapBinding *WM_xr_actionmap_binding_add_copy(XrActionMapItem *ami,
+ XrActionMapBinding *amb_src)
+{
+ XrActionMapBinding *amb_dst = wm_xr_actionmap_binding_copy(amb_src);
+
+ WM_xr_actionmap_binding_ensure_unique(ami, amb_dst);
+
+ BLI_addtail(&ami->bindings, amb_dst);
+
+ return amb_dst;
+}
+
+bool WM_xr_actionmap_binding_remove(XrActionMapItem *ami, XrActionMapBinding *amb)
+{
+ int idx = BLI_findindex(&ami->bindings, amb);
+
+ if (idx != -1) {
+ BLI_freelinkN(&ami->bindings, amb);
+
+ if (BLI_listbase_is_empty(&ami->bindings)) {
+ ami->selbinding = -1;
+ }
+ else {
+ if (idx <= ami->selbinding) {
+ if (--ami->selbinding < 0) {
+ ami->selbinding = 0;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+XrActionMapBinding *WM_xr_actionmap_binding_find(XrActionMapItem *ami, const char *name)
+{
+ LISTBASE_FOREACH (XrActionMapBinding *, amb, &ami->bindings) {
+ if (STREQLEN(name, amb->name, MAX_NAME)) {
+ return amb;
+ }
+ }
+ return NULL;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Action Map Item
+ *
+ * Item in an XR action map, that maps an XR event to an operator, pose, or haptic output.
+ * \{ */
+
+static void wm_xr_actionmap_item_properties_set(XrActionMapItem *ami)
+{
+ WM_operator_properties_alloc(&(ami->op_properties_ptr), &(ami->op_properties), ami->op);
+ WM_operator_properties_sanitize(ami->op_properties_ptr, 1);
+}
+
+static void wm_xr_actionmap_item_properties_free(XrActionMapItem *ami)
+{
+ if (ami->op_properties_ptr) {
+ WM_operator_properties_free(ami->op_properties_ptr);
+ MEM_freeN(ami->op_properties_ptr);
+ ami->op_properties_ptr = NULL;
+ ami->op_properties = NULL;
+ }
+ else {
+ BLI_assert(ami->op_properties == NULL);
+ }
+}
+
+/**
+ * Similar to #wm_xr_actionmap_item_properties_set()
+ * but checks for the #eXrActionType and #wmOperatorType having changed.
+ */
+void WM_xr_actionmap_item_properties_update_ot(XrActionMapItem *ami)
+{
+ switch (ami->type) {
+ case XR_BOOLEAN_INPUT:
+ case XR_FLOAT_INPUT:
+ case XR_VECTOR2F_INPUT:
+ break;
+ case XR_POSE_INPUT:
+ case XR_VIBRATION_OUTPUT:
+ wm_xr_actionmap_item_properties_free(ami);
+ memset(ami->op, 0, sizeof(ami->op));
+ return;
+ }
+
+ if (ami->op[0] == 0) {
+ wm_xr_actionmap_item_properties_free(ami);
+ return;
+ }
+
+ if (ami->op_properties_ptr == NULL) {
+ wm_xr_actionmap_item_properties_set(ami);
+ }
+ else {
+ wmOperatorType *ot = WM_operatortype_find(ami->op, 0);
+ if (ot) {
+ if (ot->srna != ami->op_properties_ptr->type) {
+ /* Matches wm_xr_actionmap_item_properties_set() but doesn't alloc new ptr. */
+ WM_operator_properties_create_ptr(ami->op_properties_ptr, ot);
+ if (ami->op_properties) {
+ ami->op_properties_ptr->data = ami->op_properties;
+ }
+ WM_operator_properties_sanitize(ami->op_properties_ptr, 1);
+ }
+ }
+ else {
+ wm_xr_actionmap_item_properties_free(ami);
+ }
+ }
+}
+
+XrActionMapItem *WM_xr_actionmap_item_new(XrActionMap *actionmap,
+ const char *name,
+ bool replace_existing)
+{
+ XrActionMapItem *ami_prev = WM_xr_actionmap_item_find(actionmap, name);
+ if (ami_prev && replace_existing) {
+ wm_xr_actionmap_item_properties_free(ami_prev);
+ return ami_prev;
+ }
+
+ XrActionMapItem *ami = MEM_callocN(sizeof(XrActionMapItem), __func__);
+ BLI_strncpy(ami->name, name, MAX_NAME);
+ if (ami_prev) {
+ WM_xr_actionmap_item_ensure_unique(actionmap, ami);
+ }
+ ami->selbinding = -1;
+
+ BLI_addtail(&actionmap->items, ami);
+
+ /* Set type to float (button) input by default. */
+ ami->type = XR_FLOAT_INPUT;
+
+ return ami;
+}
+
+static XrActionMapItem *wm_xr_actionmap_item_find_except(XrActionMap *actionmap,
+ const char *name,
+ const XrActionMapItem *amiexcept)
+{
+ LISTBASE_FOREACH (XrActionMapItem *, ami, &actionmap->items) {
+ if (STREQLEN(name, ami->name, MAX_NAME) && (ami != amiexcept)) {
+ return ami;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Ensure unique name among all action map items.
+ */
+void WM_xr_actionmap_item_ensure_unique(XrActionMap *actionmap, XrActionMapItem *ami)
+{
+ char name[MAX_NAME];
+ char *suffix;
+ size_t baselen;
+ size_t idx = 0;
+
+ BLI_strncpy(name, ami->name, MAX_NAME);
+ baselen = BLI_strnlen(name, MAX_NAME);
+ suffix = &name[baselen];
+
+ while (wm_xr_actionmap_item_find_except(actionmap, name, ami)) {
+ if ((baselen + 1) + (log10(++idx) + 1) > MAX_NAME) {
+ /* Use default base name. */
+ BLI_strncpy(name, WM_XR_ACTIONMAP_ITEM_STR_DEFAULT, MAX_NAME);
+ baselen = BLI_strnlen(name, MAX_NAME);
+ suffix = &name[baselen];
+ idx = 0;
+ }
+ else {
+ BLI_snprintf(suffix, MAX_NAME, "%zu", idx);
+ }
+ }
+
+ BLI_strncpy(ami->name, name, MAX_NAME);
+}
+
+static XrActionMapItem *wm_xr_actionmap_item_copy(XrActionMapItem *ami)
+{
+ XrActionMapItem *amin = MEM_dupallocN(ami);
+
+ amin->prev = amin->next = NULL;
+
+ if (amin->op_properties) {
+ amin->op_properties_ptr = MEM_callocN(sizeof(PointerRNA), "wmOpItemPtr");
+ WM_operator_properties_create(amin->op_properties_ptr, amin->op);
+
+ amin->op_properties = IDP_CopyProperty(amin->op_properties);
+ amin->op_properties_ptr->data = amin->op_properties;
+ }
+ else {
+ amin->op_properties = NULL;
+ amin->op_properties_ptr = NULL;
+ }
+
+ return amin;
+}
+
+XrActionMapItem *WM_xr_actionmap_item_add_copy(XrActionMap *actionmap, XrActionMapItem *ami_src)
+{
+ XrActionMapItem *ami_dst = wm_xr_actionmap_item_copy(ami_src);
+
+ WM_xr_actionmap_item_ensure_unique(actionmap, ami_dst);
+
+ BLI_addtail(&actionmap->items, ami_dst);
+
+ return ami_dst;
+}
+
+bool WM_xr_actionmap_item_remove(XrActionMap *actionmap, XrActionMapItem *ami)
+{
+ int idx = BLI_findindex(&actionmap->items, ami);
+
+ if (idx != -1) {
+ if (ami->op_properties_ptr) {
+ WM_operator_properties_free(ami->op_properties_ptr);
+ MEM_freeN(ami->op_properties_ptr);
+ }
+ BLI_freelinkN(&actionmap->items, ami);
+
+ if (BLI_listbase_is_empty(&actionmap->items)) {
+ actionmap->selitem = -1;
+ }
+ else {
+ if (idx <= actionmap->selitem) {
+ if (--actionmap->selitem < 0) {
+ actionmap->selitem = 0;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+XrActionMapItem *WM_xr_actionmap_item_find(XrActionMap *actionmap, const char *name)
+{
+ LISTBASE_FOREACH (XrActionMapItem *, ami, &actionmap->items) {
+ if (STREQLEN(name, ami->name, MAX_NAME)) {
+ return ami;
+ }
+ }
+ return NULL;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Action Map
+ *
+ * List of XR action map items.
+ * \{ */
+
+XrActionMap *WM_xr_actionmap_new(wmXrRuntimeData *runtime, const char *name, bool replace_existing)
+{
+ XrActionMap *am_prev = WM_xr_actionmap_find(runtime, name);
+ if (am_prev && replace_existing) {
+ WM_xr_actionmap_clear(am_prev);
+ return am_prev;
+ }
+
+ XrActionMap *am = MEM_callocN(sizeof(struct XrActionMap), __func__);
+ BLI_strncpy(am->name, name, MAX_NAME);
+ if (am_prev) {
+ WM_xr_actionmap_ensure_unique(runtime, am);
+ }
+ am->selitem = -1;
+
+ BLI_addtail(&runtime->actionmaps, am);
+
+ return am;
+}
+
+static XrActionMap *wm_xr_actionmap_find_except(wmXrRuntimeData *runtime,
+ const char *name,
+ const XrActionMap *am_except)
+{
+ LISTBASE_FOREACH (XrActionMap *, am, &runtime->actionmaps) {
+ if (STREQLEN(name, am->name, MAX_NAME) && (am != am_except)) {
+ return am;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * Ensure unique name among all action maps.
+ */
+void WM_xr_actionmap_ensure_unique(wmXrRuntimeData *runtime, XrActionMap *actionmap)
+{
+ char name[MAX_NAME];
+ char *suffix;
+ size_t baselen;
+ size_t idx = 0;
+
+ BLI_strncpy(name, actionmap->name, MAX_NAME);
+ baselen = BLI_strnlen(name, MAX_NAME);
+ suffix = &name[baselen];
+
+ while (wm_xr_actionmap_find_except(runtime, name, actionmap)) {
+ if ((baselen + 1) + (log10(++idx) + 1) > MAX_NAME) {
+ /* Use default base name. */
+ BLI_strncpy(name, WM_XR_ACTIONMAP_STR_DEFAULT, MAX_NAME);
+ baselen = BLI_strnlen(name, MAX_NAME);
+ suffix = &name[baselen];
+ idx = 0;
+ }
+ else {
+ BLI_snprintf(suffix, MAX_NAME, "%zu", idx);
+ }
+ }
+
+ BLI_strncpy(actionmap->name, name, MAX_NAME);
+}
+
+static XrActionMap *wm_xr_actionmap_copy(XrActionMap *am_src)
+{
+ XrActionMap *am_dst = MEM_dupallocN(am_src);
+
+ am_dst->prev = am_dst->next = NULL;
+ BLI_listbase_clear(&am_dst->items);
+
+ LISTBASE_FOREACH (XrActionMapItem *, ami, &am_src->items) {
+ XrActionMapItem *ami_new = wm_xr_actionmap_item_copy(ami);
+ BLI_addtail(&am_dst->items, ami_new);
+ }
+
+ return am_dst;
+}
+
+XrActionMap *WM_xr_actionmap_add_copy(wmXrRuntimeData *runtime, XrActionMap *am_src)
+{
+ XrActionMap *am_dst = wm_xr_actionmap_copy(am_src);
+
+ WM_xr_actionmap_ensure_unique(runtime, am_dst);
+
+ BLI_addtail(&runtime->actionmaps, am_dst);
+
+ return am_dst;
+}
+
+bool WM_xr_actionmap_remove(wmXrRuntimeData *runtime, XrActionMap *actionmap)
+{
+ int idx = BLI_findindex(&runtime->actionmaps, actionmap);
+
+ if (idx != -1) {
+ WM_xr_actionmap_clear(actionmap);
+ BLI_freelinkN(&runtime->actionmaps, actionmap);
+
+ if (BLI_listbase_is_empty(&runtime->actionmaps)) {
+ runtime->actactionmap = runtime->selactionmap = -1;
+ }
+ else {
+ if (idx <= runtime->actactionmap) {
+ if (--runtime->actactionmap < 0) {
+ runtime->actactionmap = 0;
+ }
+ }
+ if (idx <= runtime->selactionmap) {
+ if (--runtime->selactionmap < 0) {
+ runtime->selactionmap = 0;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+XrActionMap *WM_xr_actionmap_find(wmXrRuntimeData *runtime, const char *name)
+{
+ LISTBASE_FOREACH (XrActionMap *, am, &runtime->actionmaps) {
+ if (STREQLEN(name, am->name, MAX_NAME)) {
+ return am;
+ }
+ }
+ return NULL;
+}
+
+void WM_xr_actionmap_clear(XrActionMap *actionmap)
+{
+ LISTBASE_FOREACH (XrActionMapItem *, ami, &actionmap->items) {
+ wm_xr_actionmap_item_properties_free(ami);
+ }
+
+ BLI_freelistN(&actionmap->items);
+
+ actionmap->selitem = -1;
+}
+
+void WM_xr_actionmaps_clear(wmXrRuntimeData *runtime)
+{
+ LISTBASE_FOREACH (XrActionMap *, am, &runtime->actionmaps) {
+ WM_xr_actionmap_clear(am);
+ }
+
+ BLI_freelistN(&runtime->actionmaps);
+
+ runtime->actactionmap = runtime->selactionmap = -1;
+}
+
+ListBase *WM_xr_actionmaps_get(wmXrRuntimeData *runtime)
+{
+ return &runtime->actionmaps;
+}
+
+short WM_xr_actionmap_active_index_get(const wmXrRuntimeData *runtime)
+{
+ return runtime->actactionmap;
+}
+
+void WM_xr_actionmap_active_index_set(wmXrRuntimeData *runtime, short idx)
+{
+ BLI_assert(idx < BLI_listbase_count(&runtime->actionmaps));
+ runtime->actactionmap = idx;
+}
+
+short WM_xr_actionmap_selected_index_get(const wmXrRuntimeData *runtime)
+{
+ return runtime->selactionmap;
+}
+
+void WM_xr_actionmap_selected_index_set(wmXrRuntimeData *runtime, short idx)
+{
+ BLI_assert(idx < BLI_listbase_count(&runtime->actionmaps));
+ runtime->selactionmap = idx;
+}
+
+/** \} */
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_draw.c b/source/blender/windowmanager/xr/intern/wm_xr_draw.c
index 4ac05e339b9..bbb73fc2007 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_draw.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr_draw.c
@@ -38,18 +38,18 @@
#include "wm_surface.h"
#include "wm_xr_intern.h"
-void wm_xr_pose_to_viewmat(const GHOST_XrPose *pose, float r_viewmat[4][4])
+void wm_xr_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4])
{
- float iquat[4];
- invert_qt_qt_normalized(iquat, pose->orientation_quat);
- quat_to_mat4(r_viewmat, iquat);
- translate_m4(r_viewmat, -pose->position[0], -pose->position[1], -pose->position[2]);
+ quat_to_mat4(r_mat, pose->orientation_quat);
+ copy_v3_v3(r_mat[3], pose->position);
}
-void wm_xr_controller_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4])
+void wm_xr_pose_to_imat(const GHOST_XrPose *pose, float r_imat[4][4])
{
- quat_to_mat4(r_mat, pose->orientation_quat);
- copy_v3_v3(r_mat[3], pose->position);
+ float iquat[4];
+ invert_qt_qt_normalized(iquat, pose->orientation_quat);
+ quat_to_mat4(r_imat, iquat);
+ translate_m4(r_imat, -pose->position[0], -pose->position[1], -pose->position[2]);
}
static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data,
@@ -59,6 +59,7 @@ static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data,
float r_proj_mat[4][4])
{
GHOST_XrPose eye_pose;
+ float eye_inv[4][4], base_inv[4][4];
copy_qt_qt(eye_pose.orientation_quat, draw_view->eye_pose.orientation_quat);
copy_v3_v3(eye_pose.position, draw_view->eye_pose.position);
@@ -69,6 +70,12 @@ static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data,
sub_v3_v3(eye_pose.position, draw_data->eye_position_ofs);
}
+ wm_xr_pose_to_imat(&eye_pose, eye_inv);
+ /* Calculate the base pose matrix (in world space!). */
+ wm_xr_pose_to_imat(&draw_data->base_pose, base_inv);
+
+ mul_m4_m4m4(r_view_mat, eye_inv, base_inv);
+
perspective_m4_fov(r_proj_mat,
draw_view->fov.angle_left,
draw_view->fov.angle_right,
@@ -76,15 +83,6 @@ static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data,
draw_view->fov.angle_down,
session_settings->clip_start,
session_settings->clip_end);
-
- float eye_mat[4][4];
- float base_mat[4][4];
-
- wm_xr_pose_to_viewmat(&eye_pose, eye_mat);
- /* Calculate the base pose matrix (in world space!). */
- wm_xr_pose_to_viewmat(&draw_data->base_pose, base_mat);
-
- mul_m4_m4m4(r_view_mat, eye_mat, base_mat);
}
static void wm_xr_draw_viewport_buffers_to_active_framebuffer(
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_intern.h b/source/blender/windowmanager/xr/intern/wm_xr_intern.h
index 6415f96e322..4d4df43f796 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_intern.h
+++ b/source/blender/windowmanager/xr/intern/wm_xr_intern.h
@@ -26,19 +26,6 @@
struct wmXrActionSet;
-typedef struct wmXrControllerData {
- /** OpenXR path identifier. Length is dependent on OpenXR's XR_MAX_PATH_LENGTH (256).
- This subaction path will later be combined with a component path, and that combined path should
- also have a max of XR_MAX_PATH_LENGTH (e.g. subaction_path = /user/hand/left, component_path =
- /input/trigger/value, interaction_path = /user/hand/left/input/trigger/value).
- */
- char subaction_path[64];
- /** Last known controller pose (in world space) stored for queries. */
- GHOST_XrPose pose;
- /** The last known controller matrix, calculated from above's controller pose. */
- float mat[4][4];
-} wmXrControllerData;
-
typedef struct wmXrSessionState {
bool is_started;
@@ -65,7 +52,7 @@ typedef struct wmXrSessionState {
bool is_view_data_set;
/** Last known controller data. */
- wmXrControllerData controllers[2];
+ ListBase controllers; /* wmXrController */
/** The currently active action set that will be updated on calls to
* wm_xr_session_actions_update(). If NULL, all action sets will be treated as active and
@@ -83,6 +70,10 @@ typedef struct wmXrRuntimeData {
/** Although this struct is internal, RNA gets a handle to this for state information queries. */
wmXrSessionState session_state;
wmXrSessionExitFn exit_fn;
+
+ ListBase actionmaps; /* XrActionMap */
+ short actactionmap;
+ short selactionmap;
} wmXrRuntimeData;
typedef struct wmXrViewportPair {
@@ -112,6 +103,22 @@ typedef struct wmXrDrawData {
float eye_position_ofs[3]; /* Local/view space. */
} wmXrDrawData;
+typedef struct wmXrController {
+ struct wmXrController *next, *prev;
+ /** OpenXR path identifier. Length is dependent on OpenXR's XR_MAX_PATH_LENGTH (256).
+ This subaction path will later be combined with a component path, and that combined path should
+ also have a max of XR_MAX_PATH_LENGTH (e.g. subaction_path = /user/hand/left, component_path =
+ /input/trigger/value, interaction_path = /user/hand/left/input/trigger/value).
+ */
+ char subaction_path[64];
+ /* Pose (in world space) that represents the user's hand when holding the controller.*/
+ GHOST_XrPose grip_pose;
+ float grip_mat[4][4];
+ /* Pose (in world space) that represents the controller's aiming source. */
+ GHOST_XrPose aim_pose;
+ float aim_mat[4][4];
+} wmXrController;
+
typedef struct wmXrAction {
char *name;
eXrActionType type;
@@ -122,28 +129,47 @@ typedef struct wmXrAction {
/** Previous states, stored to determine XR events. */
void *states_prev;
- /** Input threshold for float/vector2f actions. */
- float float_threshold;
+ /** Input thresholds/regions for each subaction path. */
+ float *float_thresholds;
+ eXrAxisFlag *axis_flags;
/** The currently active subaction path (if any) for modal actions. */
- char **active_modal_path;
+ const char *active_modal_path;
/** Operator to be called on XR events. */
struct wmOperatorType *ot;
IDProperty *op_properties;
+
+ /** Haptics. */
+ char *haptic_name;
+ int64_t haptic_duration;
+ float haptic_frequency;
+ float haptic_amplitude;
+
+ /** Flags. */
eXrOpFlag op_flag;
+ eXrActionFlag action_flag;
+ eXrHapticFlag haptic_flag;
} wmXrAction;
+typedef struct wmXrHapticAction {
+ struct wmXrHapticAction *next, *prev;
+ wmXrAction *action;
+ const char *subaction_path;
+ int64_t time_start;
+} wmXrHapticAction;
+
typedef struct wmXrActionSet {
char *name;
- /** The XR pose action that determines the controller
- * transforms. This is usually identified by the OpenXR path "/grip/pose" or "/aim/pose",
- * although it could differ depending on the specification and hardware. */
- wmXrAction *controller_pose_action;
+ /** XR pose actions that determine the controller grip/aim transforms. */
+ wmXrAction *controller_grip_action;
+ wmXrAction *controller_aim_action;
- /** The currently active modal action (if any). */
- wmXrAction *active_modal_action;
+ /** Currently active modal actions. */
+ ListBase active_modal_actions;
+ /** Currently active haptic actions. */
+ ListBase active_haptic_actions;
} wmXrActionSet;
wmXrRuntimeData *wm_xr_runtime_data_create(void);
@@ -164,10 +190,11 @@ void wm_xr_session_gpu_binding_context_destroy(GHOST_ContextHandle context);
void wm_xr_session_actions_init(wmXrData *xr);
void wm_xr_session_actions_update(wmXrData *xr);
-void wm_xr_session_controller_data_populate(const wmXrAction *controller_pose_action,
+void wm_xr_session_controller_data_populate(const wmXrAction *grip_action,
+ const wmXrAction *aim_action,
wmXrData *xr);
void wm_xr_session_controller_data_clear(wmXrSessionState *state);
-void wm_xr_pose_to_viewmat(const GHOST_XrPose *pose, float r_viewmat[4][4]);
-void wm_xr_controller_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4]);
+void wm_xr_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4]);
+void wm_xr_pose_to_imat(const GHOST_XrPose *pose, float r_imat[4][4]);
void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata);
diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c
index 252f358c798..ba30b0dd864 100644
--- a/source/blender/windowmanager/xr/intern/wm_xr_session.c
+++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c
@@ -63,6 +63,16 @@ static void wm_xr_session_create_cb(void)
wm_xr_session_actions_init(xr_data);
}
+static void wm_xr_session_controller_data_free(wmXrSessionState *state)
+{
+ BLI_freelistN(&state->controllers);
+}
+
+static void wm_xr_session_data_free(wmXrSessionState *state)
+{
+ wm_xr_session_controller_data_free(state);
+}
+
static void wm_xr_session_exit_cb(void *customdata)
{
wmXrData *xr_data = customdata;
@@ -74,6 +84,7 @@ static void wm_xr_session_exit_cb(void *customdata)
}
/* Free the entire runtime data (including session state and context), to play safe. */
+ wm_xr_session_data_free(&xr_data->runtime->session_state);
wm_xr_runtime_data_free(&xr_data->runtime);
}
@@ -338,7 +349,7 @@ void wm_xr_session_state_update(const XrSessionSettings *settings,
copy_v3_v3(state->viewer_pose.position, viewer_pose.position);
copy_qt_qt(state->viewer_pose.orientation_quat, viewer_pose.orientation_quat);
- wm_xr_pose_to_viewmat(&viewer_pose, state->viewer_viewmat);
+ wm_xr_pose_to_imat(&viewer_pose, state->viewer_viewmat);
/* No idea why, but multiplying by two seems to make it match the VR view more. */
state->focal_len = 2.0f *
fov_to_focallength(draw_view->fov.angle_right - draw_view->fov.angle_left,
@@ -398,32 +409,71 @@ bool WM_xr_session_state_viewer_pose_matrix_info_get(const wmXrData *xr,
return true;
}
-bool WM_xr_session_state_controller_pose_location_get(const wmXrData *xr,
+bool WM_xr_session_state_controller_grip_location_get(const wmXrData *xr,
unsigned int subaction_idx,
float r_location[3])
{
if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set ||
- subaction_idx >= ARRAY_SIZE(xr->runtime->session_state.controllers)) {
+ (subaction_idx >= BLI_listbase_count(&xr->runtime->session_state.controllers))) {
zero_v3(r_location);
return false;
}
- copy_v3_v3(r_location, xr->runtime->session_state.controllers[subaction_idx].pose.position);
+ const wmXrController *controller = BLI_findlink(&xr->runtime->session_state.controllers,
+ subaction_idx);
+ BLI_assert(controller);
+ copy_v3_v3(r_location, controller->grip_pose.position);
return true;
}
-bool WM_xr_session_state_controller_pose_rotation_get(const wmXrData *xr,
+bool WM_xr_session_state_controller_grip_rotation_get(const wmXrData *xr,
unsigned int subaction_idx,
float r_rotation[4])
{
if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set ||
- subaction_idx >= ARRAY_SIZE(xr->runtime->session_state.controllers)) {
+ (subaction_idx >= BLI_listbase_count(&xr->runtime->session_state.controllers))) {
unit_qt(r_rotation);
return false;
}
- copy_v4_v4(r_rotation,
- xr->runtime->session_state.controllers[subaction_idx].pose.orientation_quat);
+ const wmXrController *controller = BLI_findlink(&xr->runtime->session_state.controllers,
+ subaction_idx);
+ BLI_assert(controller);
+ copy_qt_qt(r_rotation, controller->grip_pose.orientation_quat);
+ return true;
+}
+
+bool WM_xr_session_state_controller_aim_location_get(const wmXrData *xr,
+ unsigned int subaction_idx,
+ float r_location[3])
+{
+ if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set ||
+ (subaction_idx >= BLI_listbase_count(&xr->runtime->session_state.controllers))) {
+ zero_v3(r_location);
+ return false;
+ }
+
+ const wmXrController *controller = BLI_findlink(&xr->runtime->session_state.controllers,
+ subaction_idx);
+ BLI_assert(controller);
+ copy_v3_v3(r_location, controller->aim_pose.position);
+ return true;
+}
+
+bool WM_xr_session_state_controller_aim_rotation_get(const wmXrData *xr,
+ unsigned int subaction_idx,
+ float r_rotation[4])
+{
+ if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set ||
+ (subaction_idx >= BLI_listbase_count(&xr->runtime->session_state.controllers))) {
+ unit_qt(r_rotation);
+ return false;
+ }
+
+ const wmXrController *controller = BLI_findlink(&xr->runtime->session_state.controllers,
+ subaction_idx);
+ BLI_assert(controller);
+ copy_qt_qt(r_rotation, controller->aim_pose.orientation_quat);
return true;
}
@@ -443,16 +493,34 @@ void wm_xr_session_actions_init(wmXrData *xr)
GHOST_XrAttachActionSets(xr->runtime->context);
}
-static void wm_xr_session_controller_mats_update(const XrSessionSettings *settings,
- const wmXrAction *controller_pose_action,
+static void wm_xr_session_controller_pose_calc(const GHOST_XrPose *raw_pose,
+ const float view_ofs[3],
+ const float base_mat[4][4],
+ GHOST_XrPose *r_pose,
+ float r_mat[4][4])
+{
+ float m[4][4];
+ /* Calculate controller matrix in world space. */
+ wm_xr_pose_to_mat(raw_pose, m);
+
+ /* Apply eye position and base pose offsets. */
+ sub_v3_v3(m[3], view_ofs);
+ mul_m4_m4m4(r_mat, base_mat, m);
+
+ /* Save final pose. */
+ mat4_to_loc_quat(r_pose->position, r_pose->orientation_quat, r_mat);
+}
+
+static void wm_xr_session_controller_data_update(const XrSessionSettings *settings,
+ const wmXrAction *grip_action,
+ const wmXrAction *aim_action,
wmXrSessionState *state)
{
- const unsigned int count = (unsigned int)min_ii(
- (int)controller_pose_action->count_subaction_paths, (int)ARRAY_SIZE(state->controllers));
+ BLI_assert(grip_action->count_subaction_paths == aim_action->count_subaction_paths);
+ BLI_assert(grip_action->count_subaction_paths == BLI_listbase_count(&state->controllers));
- float view_ofs[3];
- float base_inv[4][4];
- float tmp[4][4];
+ unsigned int subaction_idx = 0;
+ float view_ofs[3], base_mat[4][4];
if ((settings->flag & XR_SESSION_USE_POSITION_TRACKING) == 0) {
copy_v3_v3(view_ofs, state->prev_local_pose.position);
@@ -464,22 +532,19 @@ static void wm_xr_session_controller_mats_update(const XrSessionSettings *settin
add_v3_v3(view_ofs, state->prev_eye_position_ofs);
}
- wm_xr_pose_to_viewmat(&state->prev_base_pose, base_inv);
- invert_m4(base_inv);
-
- for (unsigned int i = 0; i < count; ++i) {
- wmXrControllerData *controller = &state->controllers[i];
-
- /* Calculate controller matrix in world space. */
- wm_xr_controller_pose_to_mat(&((GHOST_XrPose *)controller_pose_action->states)[i], tmp);
-
- /* Apply eye position and base pose offsets. */
- sub_v3_v3(tmp[3], view_ofs);
- mul_m4_m4m4(controller->mat, base_inv, tmp);
+ wm_xr_pose_to_mat(&state->prev_base_pose, base_mat);
- /* Save final pose. */
- mat4_to_loc_quat(
- controller->pose.position, controller->pose.orientation_quat, controller->mat);
+ LISTBASE_FOREACH_INDEX (wmXrController *, controller, &state->controllers, subaction_idx) {
+ wm_xr_session_controller_pose_calc(&((GHOST_XrPose *)grip_action->states)[subaction_idx],
+ view_ofs,
+ base_mat,
+ &controller->grip_pose,
+ controller->grip_mat);
+ wm_xr_session_controller_pose_calc(&((GHOST_XrPose *)aim_action->states)[subaction_idx],
+ view_ofs,
+ base_mat,
+ &controller->aim_pose,
+ controller->aim_mat);
}
}
@@ -498,33 +563,44 @@ void wm_xr_session_actions_update(wmXrData *xr)
return;
}
- /* Only update controller mats for active action set. */
+ /* Only update controller data for active action set. */
if (active_action_set) {
- if (active_action_set->controller_pose_action) {
- wm_xr_session_controller_mats_update(
- &xr->session_settings, active_action_set->controller_pose_action, state);
+ if (active_action_set->controller_grip_action && active_action_set->controller_aim_action) {
+ wm_xr_session_controller_data_update(&xr->session_settings,
+ active_action_set->controller_grip_action,
+ active_action_set->controller_aim_action,
+ state);
}
}
}
-void wm_xr_session_controller_data_populate(const wmXrAction *controller_pose_action, wmXrData *xr)
+void wm_xr_session_controller_data_populate(const wmXrAction *grip_action,
+ const wmXrAction *aim_action,
+ wmXrData *xr)
{
+ UNUSED_VARS(aim_action); /* Only used for asserts. */
+
wmXrSessionState *state = &xr->runtime->session_state;
+ ListBase *controllers = &state->controllers;
- const unsigned int count = (unsigned int)min_ii(
- (int)ARRAY_SIZE(state->controllers), (int)controller_pose_action->count_subaction_paths);
+ BLI_assert(grip_action->count_subaction_paths == aim_action->count_subaction_paths);
+ const unsigned int count = grip_action->count_subaction_paths;
+
+ wm_xr_session_controller_data_free(state);
for (unsigned int i = 0; i < count; ++i) {
- wmXrControllerData *c = &state->controllers[i];
- strcpy(c->subaction_path, controller_pose_action->subaction_paths[i]);
- memset(&c->pose, 0, sizeof(c->pose));
- zero_m4(c->mat);
+ wmXrController *controller = MEM_callocN(sizeof(*controller), __func__);
+
+ BLI_assert(STREQ(grip_action->subaction_paths[i], aim_action->subaction_paths[i]));
+ strcpy(controller->subaction_path, grip_action->subaction_paths[i]);
+
+ BLI_addtail(controllers, controller);
}
}
void wm_xr_session_controller_data_clear(wmXrSessionState *state)
{
- memset(state->controllers, 0, sizeof(state->controllers));
+ wm_xr_session_controller_data_free(state);
}
/** \} */ /* XR-Session Actions */
@@ -581,9 +657,6 @@ bool wm_xr_session_surface_offscreen_ensure(wmXrSurfaceData *surface_data,
GPUViewport *viewport = vp->viewport;
const bool size_changed = offscreen && (GPU_offscreen_width(offscreen) != draw_view->width) &&
(GPU_offscreen_height(offscreen) != draw_view->height);
- char err_out[256] = "unknown";
- bool failure = false;
-
if (offscreen) {
BLI_assert(viewport);
@@ -594,8 +667,29 @@ bool wm_xr_session_surface_offscreen_ensure(wmXrSurfaceData *surface_data,
GPU_offscreen_free(offscreen);
}
+ char err_out[256] = "unknown";
+ bool failure = false;
+ eGPUTextureFormat format =
+ GPU_R8; /* Initialize with some unsupported format to check following switch statement. */
+
+ switch (draw_view->swapchain_format) {
+ case GHOST_kXrSwapchainFormatRGBA8:
+ format = GPU_RGBA8;
+ break;
+ case GHOST_kXrSwapchainFormatRGBA16:
+ format = GPU_RGBA16;
+ break;
+ case GHOST_kXrSwapchainFormatRGBA16F:
+ format = GPU_RGBA16F;
+ break;
+ case GHOST_kXrSwapchainFormatRGB10_A2:
+ format = GPU_RGB10_A2;
+ break;
+ }
+ BLI_assert(format != GPU_R8);
+
offscreen = vp->offscreen = GPU_offscreen_create(
- draw_view->width, draw_view->height, true, false, err_out);
+ draw_view->width, draw_view->height, true, format, err_out);
if (offscreen) {
viewport = vp->viewport = GPU_viewport_create();
if (!viewport) {