diff options
author | Jeroen Bakker <j.bakker@atmind.nl> | 2022-03-02 18:03:01 +0300 |
---|---|---|
committer | Jeroen Bakker <j.bakker@atmind.nl> | 2022-03-02 18:03:01 +0300 |
commit | a41c2a513761e8884e92526b069ff6eed8168676 (patch) | |
tree | e624093127815a09d2807dccddaabea35510e154 /source/blender | |
parent | a23b4429915ca8597510b57353c4df331487c620 (diff) | |
parent | c23ec04b4e30f300a670f1cb1dc882e0608d09ad (diff) |
Merge branch 'master' into temp-image-buffer-rasterizertemp-image-buffer-rasterizer
Diffstat (limited to 'source/blender')
587 files changed, 12114 insertions, 6301 deletions
diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index 0d63bb88de1..500f386dcc0 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -227,7 +227,7 @@ struct WriteAttributeLookup { * - An output attribute can live side by side with an existing attribute with a different domain * or data type. The old attribute will only be overwritten when the #save function is called. * - * \note The lifetime of an output attribute should not be longer than the the lifetime of the + * \note The lifetime of an output attribute should not be longer than the lifetime of the * geometry component it comes from, since it can keep a reference to the component for use in * the #save method. */ diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 21bbb4ce9ad..a8a851bb228 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -25,13 +25,13 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 3 +#define BLENDER_FILE_SUBVERSION 5 /* 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 * was written with too new a version. */ #define BLENDER_FILE_MIN_VERSION 300 -#define BLENDER_FILE_MIN_SUBVERSION 42 +#define BLENDER_FILE_MIN_SUBVERSION 43 /** User readable version string. */ const char *BKE_blender_version_string(void); diff --git a/source/blender/blenkernel/BKE_brush.h b/source/blender/blenkernel/BKE_brush.h index c3a2aba18b5..4b84c0cfe23 100644 --- a/source/blender/blenkernel/BKE_brush.h +++ b/source/blender/blenkernel/BKE_brush.h @@ -8,13 +8,13 @@ * General operations for brushes. */ +#include "DNA_color_types.h" #include "DNA_object_enums.h" #ifdef __cplusplus extern "C" { #endif -enum eCurveMappingPreset; struct Brush; struct ImBuf; struct ImagePool; diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h index 52de39f3ed9..584568b4a5e 100644 --- a/source/blender/blenkernel/BKE_context.h +++ b/source/blender/blenkernel/BKE_context.h @@ -106,6 +106,7 @@ typedef enum eContextObjectMode { CTX_MODE_EDIT_ARMATURE, CTX_MODE_EDIT_METABALL, CTX_MODE_EDIT_LATTICE, + CTX_MODE_EDIT_CURVES, CTX_MODE_POSE, CTX_MODE_SCULPT, CTX_MODE_PAINT_WEIGHT, diff --git a/source/blender/blenkernel/BKE_curves.h b/source/blender/blenkernel/BKE_curves.h index 2cce15fbfd6..88bb1c67fd1 100644 --- a/source/blender/blenkernel/BKE_curves.h +++ b/source/blender/blenkernel/BKE_curves.h @@ -2,9 +2,11 @@ #pragma once +#include "DNA_curves_types.h" + /** \file * \ingroup bke - * \brief Low-level operations for curves. + * \brief Low-level operations for curves that cannot be defined in the C++ header yet. */ #ifdef __cplusplus @@ -23,14 +25,10 @@ void *BKE_curves_add(struct Main *bmain, const char *name); struct BoundBox *BKE_curves_boundbox_get(struct Object *ob); -void BKE_curves_update_customdata_pointers(struct Curves *curves); bool BKE_curves_customdata_required(struct Curves *curves, struct CustomDataLayer *layer); /* Depsgraph */ -struct Curves *BKE_curves_new_for_eval(const struct Curves *curves_src, - int totpoint, - int totcurve); struct Curves *BKE_curves_copy_for_eval(struct Curves *curves_src, bool reference); void BKE_curves_data_update(struct Depsgraph *depsgraph, diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh new file mode 100644 index 00000000000..f3d9090d16b --- /dev/null +++ b/source/blender/blenkernel/BKE_curves.hh @@ -0,0 +1,166 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BKE_curves.h" + +/** \file + * \ingroup bke + * \brief Low-level operations for curves. + */ + +#include <mutex> + +#include "BLI_float4x4.hh" +#include "BLI_index_mask.hh" +#include "BLI_math_vec_types.hh" +#include "BLI_span.hh" +#include "BLI_task.hh" +#include "BLI_vector.hh" +#include "BLI_virtual_array.hh" + +#include "BKE_attribute_access.hh" + +#include "FN_generic_virtual_array.hh" + +namespace blender::bke { + +/** + * Contains derived data, caches, and other information not saved in files, besides a few pointers + * to arrays that are kept in the non-runtime struct to avoid dereferencing this whenever they are + * accessed. + */ +class CurvesGeometryRuntime { + public: + /** Cache of evaluated positions. */ + mutable Vector<float3> evaluated_position_cache; + mutable std::mutex position_cache_mutex; + mutable bool position_cache_dirty = true; + + /** Direction of the spline at each evaluated point. */ + mutable Vector<float3> evaluated_tangents_cache; + mutable std::mutex tangent_cache_mutex; + mutable bool tangent_cache_dirty = true; + + /** Normal direction vectors for each evaluated point. */ + mutable Vector<float3> evaluated_normals_cache; + mutable std::mutex normal_cache_mutex; + mutable bool normal_cache_dirty = true; +}; + +/** + * A C++ class that wraps the DNA struct for better encapsulation and ease of use. It inherits + * directly from the struct rather than storing a pointer to avoid more complicated ownership + * handling. + */ +class CurvesGeometry : public ::CurvesGeometry { + public: + CurvesGeometry(); + /** + * Create curves with the given size. Only the position attribute is created, along with the + * offsets. + */ + CurvesGeometry(int point_size, int curve_size); + CurvesGeometry(const CurvesGeometry &other); + CurvesGeometry &operator=(const CurvesGeometry &other); + ~CurvesGeometry(); + + static CurvesGeometry &wrap(::CurvesGeometry &dna_struct) + { + CurvesGeometry *geometry = reinterpret_cast<CurvesGeometry *>(&dna_struct); + return *geometry; + } + static const CurvesGeometry &wrap(const ::CurvesGeometry &dna_struct) + { + const CurvesGeometry *geometry = reinterpret_cast<const CurvesGeometry *>(&dna_struct); + return *geometry; + } + + /* -------------------------------------------------------------------- + * Accessors. + */ + + int points_size() const; + int curves_size() const; + IndexRange points_range() const; + IndexRange curves_range() const; + + /** + * The total number of points in the evaluated poly curve. + * This can depend on the resolution attribute if it exists. + */ + int evaluated_points_size() const; + + /** + * Access a range of indices of point data for a specific curve. + */ + IndexRange range_for_curve(int index) const; + + /** The type (#CurveType) of each curve, or potentially a single if all are the same type. */ + VArray<int8_t> curve_types() const; + /** Mutable access to curve types. Call #tag_topology_changed after changing any type. */ + MutableSpan<int8_t> curve_types(); + + MutableSpan<float3> positions(); + Span<float3> positions() const; + + /** + * Calculate the largest and smallest position values, only including control points + * (rather than evaluated points). The existing values of `min` and `max` are taken into account. + * + * \return Whether there are any points. If the curve is empty, the inputs will be unaffected. + */ + bool bounds_min_max(float3 &min, float3 &max) const; + + /** + * The index of the first point in every curve. The size of this span is one larger than the + * number of curves. Consider using #range_for_curve rather than using the offsets directly. + */ + Span<int> offsets() const; + MutableSpan<int> offsets(); + + VArray<bool> cyclic() const; + MutableSpan<bool> cyclic(); + + /* -------------------------------------------------------------------- + * Operations. + */ + + /** + * Change the number of elements. New values for existing attributes should be properly + * initialized afterwards. + */ + void resize(int point_size, int curve_size); + + /** Call after deforming the position attribute. */ + void tag_positions_changed(); + /** + * Call after any operation that changes the topology + * (number of points, evaluated points, or the total count). + */ + void tag_topology_changed(); + /** Call after changing the "tilt" or "up" attributes. */ + void tag_normals_changed(); + + void translate(const float3 &translation); + void transform(const float4x4 &matrix); + + void update_customdata_pointers(); + + /* -------------------------------------------------------------------- + * Attributes. + */ + + fn::GVArray adapt_domain(const fn::GVArray &varray, + AttributeDomain from, + AttributeDomain to) const; +}; + +Curves *curves_new_nomain(int point_size, int curves_size); + +/** + * Create a new curves data-block containing a single curve with the given length and type. + */ +Curves *curves_new_nomain_single(int point_size, CurveType type); + +} // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 7c55c53ed66..f7767cc2a60 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -24,6 +24,7 @@ #include "FN_field.hh" +struct Curves; struct Collection; struct Curve; struct CurveEval; @@ -412,10 +413,10 @@ struct GeometrySet { static GeometrySet create_with_pointcloud( PointCloud *pointcloud, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); /** - * Create a new geometry set that only contains the given curve. + * Create a new geometry set that only contains the given curves. */ - static GeometrySet create_with_curve( - CurveEval *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); + static GeometrySet create_with_curves( + Curves *curves, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); /* Utility methods for access. */ /** @@ -435,9 +436,9 @@ struct GeometrySet { */ bool has_volume() const; /** - * Returns true when the geometry set has a curve component that has a curve. + * Returns true when the geometry set has a curves component that has a curves data-block. */ - bool has_curve() const; + bool has_curves() const; /** * Returns true when the geometry set has any data that is not an instance. */ @@ -460,9 +461,9 @@ struct GeometrySet { */ const Volume *get_volume_for_read() const; /** - * Returns a read-only curve or null. + * Returns a read-only curves data-block or null. */ - const CurveEval *get_curve_for_read() const; + const Curves *get_curves_for_read() const; /** * Returns a mutable mesh or null. No ownership is transferred. @@ -477,9 +478,9 @@ struct GeometrySet { */ Volume *get_volume_for_write(); /** - * Returns a mutable curve or null. No ownership is transferred. + * Returns a mutable curves data-block or null. No ownership is transferred. */ - CurveEval *get_curve_for_write(); + Curves *get_curves_for_write(); /* Utility methods for replacement. */ /** @@ -497,9 +498,9 @@ struct GeometrySet { void replace_volume(Volume *volume, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); /** - * Clear the existing curve and replace it with the given one. + * Clear the existing curves data-block and replace it with the given one. */ - void replace_curve(CurveEval *curve, + void replace_curve(Curves *curves, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); private: @@ -632,17 +633,59 @@ class PointCloudComponent : public GeometryComponent { }; /** - * A geometry component that stores curve data, in other words, a group of splines. - * Curves are stored differently than other geometry components, because the data structure used - * here does not correspond exactly to the #Curve DNA data structure. A #CurveEval is stored here - * instead, though the component does give access to a #Curve for interfacing with render engines - * and other areas of Blender that expect to use a data-block with an #ID. + * Legacy runtime-only curves type. + * These curves are stored differently than other geometry components, because the data structure + * used here does not correspond exactly to the #Curve DNA data structure. A #CurveEval is stored + * here instead, though the component does give access to a #Curve for interfacing with render + * engines and other areas of Blender that expect to use a data-block with an #ID. */ -class CurveComponent : public GeometryComponent { +class CurveComponentLegacy : public GeometryComponent { private: CurveEval *curve_ = nullptr; GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned; + public: + CurveComponentLegacy(); + ~CurveComponentLegacy(); + GeometryComponent *copy() const override; + + void clear(); + bool has_curve() const; + /** + * Clear the component and replace it with the new curve. + */ + void replace(CurveEval *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); + CurveEval *release(); + + const CurveEval *get_for_read() const; + CurveEval *get_for_write(); + + int attribute_domain_size(AttributeDomain domain) const final; + + bool is_empty() const final; + + bool owns_direct_data() const override; + void ensure_owns_direct_data() override; + + static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_CURVE; + + private: + const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final; + + blender::fn::GVArray attribute_try_adapt_domain_impl(const blender::fn::GVArray &varray, + AttributeDomain from_domain, + AttributeDomain to_domain) const final; +}; + +/** + * A geometry component that stores a group of curves, corresponding the the #Curves and + * #CurvesGeometry types. + */ +class CurveComponent : public GeometryComponent { + private: + Curves *curves_ = nullptr; + GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned; + /** * Curve data necessary to hold the draw cache for rendering, consistent over multiple redraws. * This is necessary because Blender assumes that objects evaluate to an object data type, and @@ -658,15 +701,15 @@ class CurveComponent : public GeometryComponent { GeometryComponent *copy() const override; void clear(); - bool has_curve() const; + bool has_curves() const; /** * Clear the component and replace it with the new curve. */ - void replace(CurveEval *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); - CurveEval *release(); + void replace(Curves *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); + Curves *release(); - const CurveEval *get_for_read() const; - CurveEval *get_for_write(); + const Curves *get_for_read() const; + Curves *get_for_write(); int attribute_domain_size(AttributeDomain domain) const final; @@ -693,7 +736,7 @@ class CurveComponent : public GeometryComponent { /** * Holds a reference to conceptually unique geometry or a pointer to object/collection data - * that is is instanced with a transform in #InstancesComponent. + * that is instanced with a transform in #InstancesComponent. */ class InstanceReference { public: diff --git a/source/blender/blenkernel/BKE_gpencil_geom.h b/source/blender/blenkernel/BKE_gpencil_geom.h index eb61bd32eee..4127030e96f 100644 --- a/source/blender/blenkernel/BKE_gpencil_geom.h +++ b/source/blender/blenkernel/BKE_gpencil_geom.h @@ -208,11 +208,13 @@ void BKE_gpencil_point_coords_apply_with_mat4(struct bGPdata *gpd, * \param gpd: Grease pencil data-block * \param gps: Stroke to sample * \param dist: Distance of one segment + * \param sharp_threshold: Threshold for preserving sharp corners */ bool BKE_gpencil_stroke_sample(struct bGPdata *gpd, struct bGPDstroke *gps, - float dist, - bool select); + const float dist, + const bool select, + const float sharp_threshold); /** * Apply smooth position to stroke point. * \param gps: Stroke to smooth diff --git a/source/blender/blenkernel/BKE_idprop.h b/source/blender/blenkernel/BKE_idprop.h index b4dda3689e6..3f7d9498e39 100644 --- a/source/blender/blenkernel/BKE_idprop.h +++ b/source/blender/blenkernel/BKE_idprop.h @@ -169,7 +169,7 @@ struct IDProperty *IDP_GetPropertyTypeFromGroup(const struct IDProperty *prop, /*-------- Main Functions --------*/ /** - * Get the Group property that contains the id properties for ID id. + * Get the Group property that contains the id properties for ID `id`. * * \param create_if_needed: Set to create the group property and attach it to id if it doesn't * exist; otherwise the function will return NULL if there's no Group property attached to the ID. diff --git a/source/blender/blenkernel/BKE_idtype.h b/source/blender/blenkernel/BKE_idtype.h index e9abbb4575e..bd64b03cc7d 100644 --- a/source/blender/blenkernel/BKE_idtype.h +++ b/source/blender/blenkernel/BKE_idtype.h @@ -230,7 +230,7 @@ extern IDTypeInfo IDType_ID_SCE; extern IDTypeInfo IDType_ID_LI; extern IDTypeInfo IDType_ID_OB; extern IDTypeInfo IDType_ID_ME; -extern IDTypeInfo IDType_ID_CU; +extern IDTypeInfo IDType_ID_CU_LEGACY; extern IDTypeInfo IDType_ID_MB; extern IDTypeInfo IDType_ID_MA; extern IDTypeInfo IDType_ID_TE; diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h index 858bc2f5625..98301ca7a70 100644 --- a/source/blender/blenkernel/BKE_lib_override.h +++ b/source/blender/blenkernel/BKE_lib_override.h @@ -92,7 +92,13 @@ struct ID *BKE_lib_override_library_create_from_id(struct Main *bmain, * * \param id_hierarchy_root: the override ID that is the root of the hierarchy. May be NULL, in * which case it is assumed that the given `id_root_reference` is tagged for override, and its - * newly created override will be used as hierarchy root. + * newly created override will be used as hierarchy root. Must be NULL if + * `id_hierarchy_root_reference` is not NULL. + * + * \param id_hierarchy_root_reference: the linked ID that is the root of the hierarchy. Must be + * tagged for override. May be NULL, in which case it is assumed that the given `id_root_reference` + * is tagged for override, and its newly created override will be used as hierarchy root. Must be + * NULL if `id_hierarchy_root` is not NULL. * * \param do_no_main: Create the new override data outside of Main database. * Used for resyncing of linked overrides. @@ -103,6 +109,7 @@ bool BKE_lib_override_library_create_from_tag(struct Main *bmain, struct Library *owner_library, const struct ID *id_root_reference, struct ID *id_hierarchy_root, + const struct ID *id_hierarchy_root_reference, bool do_no_main); /** * Advanced 'smart' function to create fully functional overrides. @@ -119,11 +126,18 @@ bool BKE_lib_override_library_create_from_tag(struct Main *bmain, * \param owner_library: the library in which the overrides should be created. Besides versioning * and resync code path, this should always be NULL (i.e. the local .blend file). * - * \param id_root: The root ID to create an override from. + * \param id_root_reference: The linked root ID to create an override from. May be a sub-root of + * the overall hierarchy, in which case calling code is expected to have already tagged required + * 'path' of IDs leading from the given `id_hierarchy_root` to the given `id_root`. + * + * \param id_hierarchy_root_reference: The ID to be used a hierarchy root of the overrides to be + * created. Can be either the linked root ID of the whole override hierarchy, (typically the same + * as `id_root`, unless a sub-part only of the hierarchy is overridden), or the already existing + * override hierarchy root if part of the hierarchy is already overridden. * - * \param id_reference: Some reference ID used to do some post-processing after overrides have been - * created, may be NULL. Typically, the Empty object instantiating the linked collection we - * override, currently. + * \param id_instance_hint: Some ID used as hint/reference to do some post-processing after + * overrides have been created, may be NULL. Typically, the Empty object instantiating the linked + * collection we override, currently. * * \param r_id_root_override: if not NULL, the override generated for the given \a id_root. * @@ -133,8 +147,9 @@ bool BKE_lib_override_library_create(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer, struct Library *owner_library, - struct ID *id_root, - struct ID *id_reference, + struct ID *id_root_reference, + struct ID *id_hierarchy_root_reference, + struct ID *id_instance_hint, struct ID **r_id_root_override); /** * Create a library override template. diff --git a/source/blender/blenkernel/BKE_lib_remap.h b/source/blender/blenkernel/BKE_lib_remap.h index 94b94303ec9..fd7d39fc250 100644 --- a/source/blender/blenkernel/BKE_lib_remap.h +++ b/source/blender/blenkernel/BKE_lib_remap.h @@ -220,7 +220,7 @@ IDRemapperApplyResult BKE_id_remapper_apply(const struct IDRemapper *id_remapper * Use this function when `ID_REMAP_APPLY_UNMAP_WHEN_REMAPPING_TO_SELF`. In this case * the #id_self parameter is required. Otherwise the #BKE_id_remapper_apply can be used. * - * \param id_self required for ID_REMAP_APPLY_UNMAP_WHEN_REMAPPING_TO_SELF. + * \param id_self: required for ID_REMAP_APPLY_UNMAP_WHEN_REMAPPING_TO_SELF. * When remapping to id_self it will then be remapped to NULL. */ IDRemapperApplyResult BKE_id_remapper_apply_ex(const struct IDRemapper *id_remapper, diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index a84f6058d67..2373bb289cd 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -70,6 +70,13 @@ struct Mesh *BKE_mesh_from_bmesh_for_eval_nomain(struct BMesh *bm, const struct Mesh *me_settings); /** + * Add original index (#CD_ORIGINDEX) layers if they don't already exist. This is meant to be used + * when creating an evaluated mesh from an original edit mode mesh, to allow mapping from the + * evaluated vertices to the originals. + */ +void BKE_mesh_ensure_default_orig_index_customdata(struct Mesh *mesh); + +/** * Find the index of the loop in 'poly' which references vertex, * returns -1 if not found */ @@ -332,8 +339,7 @@ int BKE_mesh_tessface_calc_ex(struct CustomData *fdata, struct MVert *mvert, int totface, int totloop, - int totpoly, - bool do_face_nor_copy); + int totpoly); void BKE_mesh_tessface_calc(struct Mesh *mesh); /** @@ -399,6 +405,9 @@ void BKE_mesh_assert_normals_dirty_or_calculated(const struct Mesh *mesh); * \note In order to clear the dirty flag, this function should be followed by a call to * #BKE_mesh_vertex_normals_clear_dirty. This is separate so that normals are still tagged dirty * while they are being assigned. + * + * \warning The memory returned by this function is not initialized if it was not previously + * allocated. */ float (*BKE_mesh_vertex_normals_for_write(struct Mesh *mesh))[3]; @@ -409,10 +418,24 @@ float (*BKE_mesh_vertex_normals_for_write(struct Mesh *mesh))[3]; * \note In order to clear the dirty flag, this function should be followed by a call to * #BKE_mesh_poly_normals_clear_dirty. This is separate so that normals are still tagged dirty * while they are being assigned. + * + * \warning The memory returned by this function is not initialized if it was not previously + * allocated. */ float (*BKE_mesh_poly_normals_for_write(struct Mesh *mesh))[3]; /** + * Free any cached vertex or poly normals. Face corner (loop) normals are also derived data, + * but are not handled with the same method yet, so they are not included. It's important that this + * is called after the mesh changes size, since otherwise cached normal arrays might not be large + * enough (though it may be called indirectly by other functions). + * + * \note Normally it's preferred to call #BKE_mesh_normals_tag_dirty instead, + * but this can be used in specific situations to reset a mesh or reduce memory usage. + */ +void BKE_mesh_clear_derived_normals(struct Mesh *mesh); + +/** * Mark the mesh's vertex normals non-dirty, for when they are calculated or assigned manually. */ void BKE_mesh_vertex_normals_clear_dirty(struct Mesh *mesh); @@ -919,7 +942,7 @@ void BKE_mesh_calc_relative_deform(const struct MPoly *mpoly, const float (*vert_cos_org)[3], float (*vert_cos_new)[3]); -/* *** mesh_validate.c *** */ +/* *** mesh_validate.cc *** */ /** * Validates and corrects a Mesh. diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h index 9a212cb1275..acdca23b21c 100644 --- a/source/blender/blenkernel/BKE_modifier.h +++ b/source/blender/blenkernel/BKE_modifier.h @@ -409,14 +409,18 @@ void BKE_modifier_session_uuid_generate(struct ModifierData *md); bool BKE_modifier_unique_name(struct ListBase *modifiers, struct ModifierData *md); +struct ModifierData *BKE_modifier_copy_ex(const struct ModifierData *md, int flag); + /** * Callback's can use this to avoid copying every member. */ void BKE_modifier_copydata_generic(const struct ModifierData *md, struct ModifierData *md_dst, int flag); -void BKE_modifier_copydata(struct ModifierData *md, struct ModifierData *target); -void BKE_modifier_copydata_ex(struct ModifierData *md, struct ModifierData *target, int flag); +void BKE_modifier_copydata(const struct ModifierData *md, struct ModifierData *target); +void BKE_modifier_copydata_ex(const struct ModifierData *md, + struct ModifierData *target, + int flag); 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); diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 933585b1f8f..315e24485fa 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -681,6 +681,7 @@ void nodeRemLink(struct bNodeTree *ntree, struct bNodeLink *link); void nodeRemSocketLinks(struct bNodeTree *ntree, struct bNodeSocket *sock); void nodeMuteLinkToggle(struct bNodeTree *ntree, struct bNodeLink *link); bool nodeLinkIsHidden(const struct bNodeLink *link); +bool nodeLinkIsSelected(const struct bNodeLink *link); void nodeInternalRelink(struct bNodeTree *ntree, struct bNode *node); void nodeToView(const struct bNode *node, float x, float y, float *rx, float *ry); @@ -1514,6 +1515,8 @@ struct TexResult; #define GEO_NODE_SCALE_ELEMENTS 1151 #define GEO_NODE_EXTRUDE_MESH 1152 #define GEO_NODE_MERGE_BY_DISTANCE 1153 +#define GEO_NODE_DUPLICATE_ELEMENTS 1154 +#define GEO_NODE_INPUT_MESH_FACE_IS_PLANAR 1155 /** \} */ diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 1f37e95a023..8ab89b6c244 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -74,9 +74,11 @@ typedef enum ePaintMode { PAINT_MODE_VERTEX_GPENCIL = 7, PAINT_MODE_SCULPT_GPENCIL = 8, PAINT_MODE_WEIGHT_GPENCIL = 9, + /** Curves. */ + PAINT_MODE_SCULPT_CURVES = 10, /** Keep last. */ - PAINT_MODE_INVALID = 10, + PAINT_MODE_INVALID = 11, } ePaintMode; #define PAINT_MODE_HAS_BRUSH(mode) !ELEM(mode, PAINT_MODE_SCULPT_UV) diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh index 846dcd3ca8e..42b4702ee44 100644 --- a/source/blender/blenkernel/BKE_spline.hh +++ b/source/blender/blenkernel/BKE_spline.hh @@ -10,6 +10,8 @@ #include "FN_generic_virtual_array.hh" +#include "DNA_curves_types.h" + #include "BLI_float4x4.hh" #include "BLI_math_vec_types.hh" #include "BLI_vector.hh" @@ -18,6 +20,7 @@ #include "BKE_attribute_math.hh" struct Curve; +struct Curves; struct ListBase; class Spline; @@ -49,12 +52,6 @@ using SplinePtr = std::unique_ptr<Spline>; */ class Spline { public: - enum class Type { - Bezier, - NURBS, - Poly, - }; - enum NormalCalculationMode { ZUp, Minimum, @@ -65,7 +62,7 @@ class Spline { blender::bke::CustomDataAttributes attributes; protected: - Type type_; + CurveType type_; bool is_cyclic_ = false; /** Direction of the spline at each evaluated point. */ @@ -85,7 +82,7 @@ class Spline { public: virtual ~Spline() = default; - Spline(const Type type) : type_(type) + Spline(const CurveType type) : type_(type) { } Spline(Spline &other) : attributes(other.attributes), type_(other.type_) @@ -107,7 +104,7 @@ class Spline { SplinePtr copy_without_attributes() const; static void copy_base_settings(const Spline &src, Spline &dst); - Spline::Type type() const; + CurveType type() const; /** Return the number of control points. */ virtual int size() const = 0; @@ -252,26 +249,13 @@ class Spline { * factors and indices in a list of floats, which is then used to interpolate any other data. */ class BezierSpline final : public Spline { - public: - enum class HandleType { - /** The handle can be moved anywhere, and doesn't influence the point's other handle. */ - Free, - /** The location is automatically calculated to be smooth. */ - Auto, - /** The location is calculated to point to the next/previous control point. */ - Vector, - /** The location is constrained to point in the opposite direction as the other handle. */ - Align, - }; - - private: blender::Vector<blender::float3> positions_; blender::Vector<float> radii_; blender::Vector<float> tilts_; int resolution_; - blender::Vector<HandleType> handle_types_left_; - blender::Vector<HandleType> handle_types_right_; + blender::Vector<int8_t> handle_types_left_; + blender::Vector<int8_t> handle_types_right_; /* These are mutable to allow lazy recalculation of #Auto and #Vector handle positions. */ mutable blender::Vector<blender::float3> handle_positions_left_; @@ -296,7 +280,7 @@ class BezierSpline final : public Spline { mutable bool mapping_cache_dirty_ = true; public: - BezierSpline() : Spline(Type::Bezier) + BezierSpline() : Spline(CURVE_TYPE_BEZIER) { } BezierSpline(const BezierSpline &other) @@ -323,8 +307,8 @@ class BezierSpline final : public Spline { blender::Span<float> radii() const final; blender::MutableSpan<float> tilts() final; blender::Span<float> tilts() const final; - blender::Span<HandleType> handle_types_left() const; - blender::MutableSpan<HandleType> handle_types_left(); + blender::Span<int8_t> handle_types_left() const; + blender::MutableSpan<int8_t> handle_types_left(); blender::Span<blender::float3> handle_positions_left() const; /** * Get writable access to the handle position. @@ -333,8 +317,8 @@ class BezierSpline final : public Spline { * uninitialized memory while auto-generating handles. */ blender::MutableSpan<blender::float3> handle_positions_left(bool write_only = false); - blender::Span<HandleType> handle_types_right() const; - blender::MutableSpan<HandleType> handle_types_right(); + blender::Span<int8_t> handle_types_right() const; + blender::MutableSpan<int8_t> handle_types_right(); blender::Span<blender::float3> handle_positions_right() const; /** * Get writable access to the handle position. @@ -519,7 +503,7 @@ class NURBSpline final : public Spline { mutable bool position_cache_dirty_ = true; public: - NURBSpline() : Spline(Type::NURBS) + NURBSpline() : Spline(CURVE_TYPE_NURBS) { } NURBSpline(const NURBSpline &other) @@ -586,7 +570,7 @@ class PolySpline final : public Spline { blender::Vector<float> tilts_; public: - PolySpline() : Spline(Type::Poly) + PolySpline() : Spline(CURVE_TYPE_POLY) { } PolySpline(const PolySpline &other) @@ -658,7 +642,7 @@ struct CurveEval { * \note If you are looping over all of the splines in the same scope anyway, * it's better to avoid calling this function, in case there are many splines. */ - bool has_spline_with_type(const Spline::Type type) const; + bool has_spline_with_type(const CurveType type) const; void resize(int size); /** @@ -708,3 +692,5 @@ struct CurveEval { std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &curve, const ListBase &nurbs_list); std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve); +std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves); +Curves *curve_eval_to_curves(const CurveEval &curve_eval); diff --git a/source/blender/blenkernel/BKE_subdiv_modifier.h b/source/blender/blenkernel/BKE_subdiv_modifier.h index a9ab5b91e1a..40e8ee2f999 100644 --- a/source/blender/blenkernel/BKE_subdiv_modifier.h +++ b/source/blender/blenkernel/BKE_subdiv_modifier.h @@ -24,18 +24,30 @@ void BKE_subsurf_modifier_subdiv_settings_init(struct SubdivSettings *settings, const struct SubsurfModifierData *smd, bool use_render_params); +bool BKE_subsurf_modifier_use_custom_loop_normals(const struct SubsurfModifierData *smd, + const struct Mesh *mesh); + +/** + * Return true if GPU subdivision evaluation is disabled by force due to incompatible mesh or + * modifier settings. This will only return true if GPU subdivision is enabled in the preferences + * and supported by the GPU. It is mainly useful for showing UI messages. + */ +bool BKE_subsurf_modifier_force_disable_gpu_evaluation_for_mesh( + const struct SubsurfModifierData *smd, const struct Mesh *mesh); /** * \param skip_check_is_last: When true, we assume that the modifier passed is the last enabled * modifier in the stack. */ bool BKE_subsurf_modifier_can_do_gpu_subdiv_ex(const struct Scene *scene, const struct Object *ob, + const struct Mesh *mesh, const struct SubsurfModifierData *smd, int required_mode, bool skip_check_is_last); bool BKE_subsurf_modifier_can_do_gpu_subdiv(const struct Scene *scene, const struct Object *ob, + const struct Mesh *mesh, int required_mode); extern void (*BKE_subsurf_modifier_free_gpu_cache_cb)(struct Subdiv *subdiv); diff --git a/source/blender/blenkernel/BKE_volume.h b/source/blender/blenkernel/BKE_volume.h index c73417d7740..77f01e7919d 100644 --- a/source/blender/blenkernel/BKE_volume.h +++ b/source/blender/blenkernel/BKE_volume.h @@ -90,7 +90,6 @@ typedef enum VolumeGridType { VOLUME_GRID_INT, VOLUME_GRID_INT64, VOLUME_GRID_MASK, - VOLUME_GRID_STRING, VOLUME_GRID_VECTOR_FLOAT, VOLUME_GRID_VECTOR_DOUBLE, VOLUME_GRID_VECTOR_INT, @@ -204,8 +203,6 @@ auto BKE_volume_grid_type_operation(const VolumeGridType grid_type, OpType &&op) return op.template operator()<openvdb::Vec3IGrid>(); case VOLUME_GRID_VECTOR_DOUBLE: return op.template operator()<openvdb::Vec3dGrid>(); - case VOLUME_GRID_STRING: - return op.template operator()<openvdb::StringGrid>(); case VOLUME_GRID_MASK: return op.template operator()<openvdb::MaskGrid>(); case VOLUME_GRID_POINTS: diff --git a/source/blender/blenkernel/BKE_writeffmpeg.h b/source/blender/blenkernel/BKE_writeffmpeg.h index cf4a8137638..3f92d6fa117 100644 --- a/source/blender/blenkernel/BKE_writeffmpeg.h +++ b/source/blender/blenkernel/BKE_writeffmpeg.h @@ -69,12 +69,8 @@ void BKE_ffmpeg_filepath_get(char *string, void BKE_ffmpeg_preset_set(struct RenderData *rd, int preset); void BKE_ffmpeg_image_type_verify(struct RenderData *rd, struct ImageFormatData *imf); -void BKE_ffmpeg_codec_settings_verify(struct RenderData *rd); bool BKE_ffmpeg_alpha_channel_is_supported(const struct RenderData *rd); -int BKE_ffmpeg_property_add_string(struct RenderData *rd, const char *type, const char *str); -void BKE_ffmpeg_property_del(struct RenderData *rd, void *type, void *prop_); - void *BKE_ffmpeg_context_create(void); void BKE_ffmpeg_context_free(void *context_v); diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index bf720fa1341..a12a956cbf5 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -103,6 +103,7 @@ set(SRC intern/cryptomatte.cc intern/curve.cc intern/curves.cc + intern/curves_geometry.cc intern/curve_bevel.c intern/curve_convert.c intern/curve_decimate.c @@ -130,6 +131,7 @@ set(SRC intern/fmodifier.c intern/freestyle.c intern/geometry_component_curve.cc + intern/geometry_component_curves.cc intern/geometry_component_instances.cc intern/geometry_component_mesh.cc intern/geometry_component_pointcloud.cc @@ -199,7 +201,7 @@ set(SRC intern/mesh_sample.cc intern/mesh_tangent.c intern/mesh_tessellate.c - intern/mesh_validate.c + intern/mesh_validate.cc intern/mesh_wrapper.c intern/modifier.c intern/movieclip.c @@ -341,6 +343,7 @@ set(SRC BKE_cryptomatte.hh BKE_curve.h BKE_curves.h + BKE_curves.hh BKE_curve_to_mesh.hh BKE_curveprofile.h BKE_customdata.h @@ -375,6 +378,7 @@ set(SRC BKE_idprop.hh BKE_idtype.h BKE_image.h + BKE_image_partial_update.hh BKE_image_save.h BKE_ipo.h BKE_kelvinlet.h diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc index 542be4027bc..39074a5c75f 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.cc +++ b/source/blender/blenkernel/intern/DerivedMesh.cc @@ -546,6 +546,7 @@ static Mesh *create_orco_mesh(Object *ob, Mesh *me, BMEditMesh *em, int layer) if (em) { mesh = BKE_mesh_from_bmesh_for_eval_nomain(em->bm, nullptr, me); + BKE_mesh_ensure_default_orig_index_customdata(mesh); } else { mesh = BKE_mesh_copy_for_eval(me, true); @@ -1142,12 +1143,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, * we need to apply these back onto the Mesh. If we have no * Mesh then we need to build one. */ if (mesh_final == nullptr) { - /* NOTE: this check on cdmask is a bit dodgy, it handles the issue at stake here (see T68211), - * but other cases might require similar handling? - * Could be a good idea to define a proper CustomData_MeshMask for that then. */ - if (deformed_verts == nullptr && allow_shared_mesh && - (final_datamask.lmask & CD_MASK_NORMAL) == 0 && - (final_datamask.pmask & CD_MASK_NORMAL) == 0) { + if (deformed_verts == nullptr && allow_shared_mesh) { mesh_final = mesh_input; } else { @@ -1364,6 +1360,12 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, em_input, &final_datamask, nullptr, mesh_input); } + /* The mesh from edit mode should not have any original index layers already, since those + * are added during evaluation when necessary and are redundant on an original mesh. */ + BLI_assert(CustomData_get_layer(&em_input->bm->pdata, CD_ORIGINDEX) == nullptr && + CustomData_get_layer(&em_input->bm->edata, CD_ORIGINDEX) == nullptr && + CustomData_get_layer(&em_input->bm->pdata, CD_ORIGINDEX) == nullptr); + /* Clear errors before evaluation. */ BKE_modifiers_clear_errors(ob); @@ -1402,6 +1404,7 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, else if (isPrevDeform && mti->dependsOnNormals && mti->dependsOnNormals(md)) { if (mesh_final == nullptr) { mesh_final = BKE_mesh_from_bmesh_for_eval_nomain(em_input->bm, nullptr, mesh_input); + BKE_mesh_ensure_default_orig_index_customdata(mesh_final); ASSERT_IS_VALID_MESH(mesh_final); } BLI_assert(deformed_verts != nullptr); @@ -1510,7 +1513,9 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, } else { Mesh *me_orig = mesh_input; - if (me_orig->id.tag & LIB_TAG_COPIED_ON_WRITE) { + /* Modifying the input mesh is weak, however as there can only be one object in edit mode + * even if multiple are sharing the same mesh this should be thread safe. */ + if ((me_orig->id.tag & LIB_TAG_COPIED_ON_WRITE) && (ob->mode & OB_MODE_EDIT)) { if (!BKE_mesh_runtime_ensure_edit_data(me_orig)) { BKE_mesh_runtime_reset_edit_data(me_orig); } @@ -1819,9 +1824,8 @@ Mesh *mesh_get_eval_final(struct Depsgraph *depsgraph, mesh_eval = BKE_object_get_evaluated_mesh(ob); } - if (mesh_eval != nullptr) { - BLI_assert(!(mesh_eval->runtime.cd_dirty_vert & CD_MASK_NORMAL)); - } + BKE_mesh_assert_normals_dirty_or_calculated(mesh_eval); + return mesh_eval; } diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index 6a999cde6eb..55aba1d22c3 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -1244,7 +1244,7 @@ void BKE_pose_update_constraint_flags(bPose *pose) /* if we have a valid target, make sure that this will get updated on frame-change * (needed for when there is no anim-data for this pose) */ - if ((data->tar) && (data->tar->type == OB_CURVE)) { + if ((data->tar) && (data->tar->type == OB_CURVES_LEGACY)) { pose->flag |= POSE_CONSTRAINTS_TIMEDEPEND; } } diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c index 0c9202400ad..861a89ea9d7 100644 --- a/source/blender/blenkernel/intern/anim_data.c +++ b/source/blender/blenkernel/intern/anim_data.c @@ -763,7 +763,7 @@ static bool fcurves_path_rename_fix(ID *owner_id, if (fcu->rna_path != old_path) { bActionGroup *agrp = fcu->grp; is_changed = true; - if ((agrp != NULL) && STREQ(oldName, agrp->name)) { + if (oldName != NULL && (agrp != NULL) && STREQ(oldName, agrp->name)) { BLI_strncpy(agrp->name, newName, sizeof(agrp->name)); } } diff --git a/source/blender/blenkernel/intern/anim_path.c b/source/blender/blenkernel/intern/anim_path.c index 57f64d7a0f8..1f8c6df6147 100644 --- a/source/blender/blenkernel/intern/anim_path.c +++ b/source/blender/blenkernel/intern/anim_path.c @@ -55,7 +55,7 @@ float BKE_anim_path_get_length(const CurveCache *curve_cache) void BKE_anim_path_calc_data(Object *ob) { - if (ob == NULL || ob->type != OB_CURVE) { + if (ob == NULL || ob->type != OB_CURVES_LEGACY) { return; } if (ob->runtime.curve_cache == NULL) { @@ -222,7 +222,7 @@ bool BKE_where_on_path(const Object *ob, float *r_radius, float *r_weight) { - if (ob == NULL || ob->type != OB_CURVE) { + if (ob == NULL || ob->type != OB_CURVES_LEGACY) { return false; } Curve *cu = ob->data; diff --git a/source/blender/blenkernel/intern/anim_visualization.c b/source/blender/blenkernel/intern/anim_visualization.c index 53a3a7e3712..f4c6a29c252 100644 --- a/source/blender/blenkernel/intern/anim_visualization.c +++ b/source/blender/blenkernel/intern/anim_visualization.c @@ -153,6 +153,11 @@ bMotionPath *animviz_verify_motionpaths(ReportList *reports, if ((mpath->start_frame != mpath->end_frame) && (mpath->length > 0)) { /* outer check ensures that we have some curve data for this path */ if (mpath->length == expected_length) { + /* The length might be the same, but the start and end could be different */ + if (mpath->start_frame != avs->path_sf) { + mpath->start_frame = avs->path_sf; + mpath->end_frame = avs->path_ef; + } /* return/use this as it is already valid length */ return mpath; } diff --git a/source/blender/blenkernel/intern/armature_update.c b/source/blender/blenkernel/intern/armature_update.c index 099588a0e14..361ab176abd 100644 --- a/source/blender/blenkernel/intern/armature_update.c +++ b/source/blender/blenkernel/intern/armature_update.c @@ -70,7 +70,7 @@ static void splineik_init_tree_from_pchan(Scene *UNUSED(scene), ik_data = con->data; /* Target can only be a curve. */ - if ((ik_data->tar == NULL) || (ik_data->tar->type != OB_CURVE)) { + if ((ik_data->tar == NULL) || (ik_data->tar->type != OB_CURVES_LEGACY)) { continue; } /* Skip if disabled. */ diff --git a/source/blender/blenkernel/intern/asset_library_test.cc b/source/blender/blenkernel/intern/asset_library_test.cc index 45889099567..1d862e5e4d4 100644 --- a/source/blender/blenkernel/intern/asset_library_test.cc +++ b/source/blender/blenkernel/intern/asset_library_test.cc @@ -52,7 +52,7 @@ TEST_F(AssetLibraryTest, bke_asset_library_load) ASSERT_NE(nullptr, service); /* Check that the catalogs defined in the library are actually loaded. This just tests one single - * catalog, as that indicates the file has been loaded. Testing that that loading went OK is for + * catalog, as that indicates the file has been loaded. Testing that loading went OK is for * the asset catalog service tests. */ const bUUID uuid_poses_ellie("df60e1f6-2259-475b-93d9-69a1b4a8db78"); AssetCatalog *poses_ellie = service->find_catalog(uuid_poses_ellie); diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh index 4eff878778a..bfc4c8fcde0 100644 --- a/source/blender/blenkernel/intern/attribute_access_intern.hh +++ b/source/blender/blenkernel/intern/attribute_access_intern.hh @@ -207,6 +207,16 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider { void foreach_domain(const FunctionRef<void(AttributeDomain)> callback) const final; }; +template<typename T> GVArray make_array_read_attribute(const void *data, const int domain_size) +{ + return VArray<T>::ForSpan(Span<T>((const T *)data, domain_size)); +} + +template<typename T> GVMutableArray make_array_write_attribute(void *data, const int domain_size) +{ + return VMutableArray<T>::ForSpan(MutableSpan<T>((T *)data, domain_size)); +} + /** * This provider is used to provide access to builtin attributes. It supports making internal types * available as different types. For example, the vertex position attribute is stored as part of diff --git a/source/blender/blenkernel/intern/blendfile_link_append.c b/source/blender/blenkernel/intern/blendfile_link_append.c index f06274c34d7..ce36bfe81be 100644 --- a/source/blender/blenkernel/intern/blendfile_link_append.c +++ b/source/blender/blenkernel/intern/blendfile_link_append.c @@ -927,8 +927,13 @@ static int foreach_libblock_link_append_callback(LibraryIDLinkCallbackData *cb_d * processed, so we need to recursively deal with them here. */ /* NOTE: Since we are by-passing checks in `BKE_library_foreach_ID_link` by manually calling it * recursively, we need to take care of potential recursion cases ourselves (e.g.animdata of - * shape-key referencing the shape-key itself). */ - if (id != cb_data->id_self) { + * shape-key referencing the shape-key itself). + * NOTE: in case both IDs (owner and 'used' ones) are non-linkable, we can assume we can break + * the dependency here. Indeed, either they are both linked in another way (through their own + * meshes for shape keys e.g.), or this is an unsupported case (two shape-keys depending on + * each-other need to be also 'linked' in by their respective meshes, independent shape-keys + * are not allowed). ref T96048. */ + if (id != cb_data->id_self && BKE_idtype_idcode_is_linkable(GS(cb_data->id_self->name))) { BKE_library_foreach_ID_link( cb_data->bmain, id, foreach_libblock_link_append_callback, data, IDWALK_NOP); } @@ -1449,7 +1454,7 @@ void BKE_blendfile_library_relocate(BlendfileLinkAppendContext *lapp_context, BlendfileLinkAppendContextItem *item; /* We remove it from current Main, and add it to items to link... */ - /* Note that non-linkable IDs (like e.g. shapekeys) are also explicitly linked here... */ + /* Note that non-linkable IDs (like e.g. shape-keys) are also explicitly linked here... */ BLI_remlink(lbarray[lba_idx], id); /* Usual special code for ShapeKeys snowflakes... */ Key *old_key = BKE_key_from_id(id); diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index f091ebe1e32..6ee6ff7f41d 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -144,10 +144,10 @@ static void brush_make_local(Main *bmain, ID *id, const int flags) /* FIXME: Recursive calls affecting other non-embedded IDs are really bad and should be avoided * in IDType callbacks. Higher-level ID management code usually does not expect such things and * does not deal properly with it. */ - /* NOTE: assert below ensures that the comment above is valid, and that that exception is + /* NOTE: assert below ensures that the comment above is valid, and that exception is * acceptable for the time being. */ BKE_lib_id_make_local(bmain, &brush->clone.image->id, 0); - BLI_assert(brush->clone.image->id.lib == NULL && brush->clone.image->id.newid == NULL); + BLI_assert(!ID_IS_LINKED(brush->clone.image) && brush->clone.image->id.newid == NULL); } if (force_local) { diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index 57a95891a92..b840fb1e665 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -550,9 +550,8 @@ void BKE_camera_view_frame(const Scene *scene, const Camera *camera, float r_vec #define CAMERA_VIEWFRAME_NUM_PLANES 4 typedef struct CameraViewFrameData { - float plane_tx[CAMERA_VIEWFRAME_NUM_PLANES][4]; /* 4 planes */ - float normal_tx[CAMERA_VIEWFRAME_NUM_PLANES][3]; - float dist_vals_sq[CAMERA_VIEWFRAME_NUM_PLANES]; /* distance squared (signed) */ + float plane_tx[CAMERA_VIEWFRAME_NUM_PLANES][4]; /* 4 planes normalized */ + float dist_vals[CAMERA_VIEWFRAME_NUM_PLANES]; /* distance (signed) */ unsigned int tot; /* Ortho camera only. */ @@ -569,8 +568,8 @@ static void camera_to_frame_view_cb(const float co[3], void *user_data) CameraViewFrameData *data = (CameraViewFrameData *)user_data; for (uint i = 0; i < CAMERA_VIEWFRAME_NUM_PLANES; i++) { - const float nd = dist_signed_squared_to_plane_v3(co, data->plane_tx[i]); - CLAMP_MAX(data->dist_vals_sq[i], nd); + const float nd = plane_point_side_v3(data->plane_tx[i], co); + CLAMP_MAX(data->dist_vals[i], nd); } if (data->is_ortho) { @@ -625,10 +624,11 @@ static void camera_frame_fit_data_init(const Scene *scene, /* Rotate planes and get normals from them */ for (uint i = 0; i < CAMERA_VIEWFRAME_NUM_PLANES; i++) { mul_m4_v4(camera_rotmat_transposed_inversed, data->plane_tx[i]); - normalize_v3_v3(data->normal_tx[i], data->plane_tx[i]); + /* Normalize. */ + data->plane_tx[i][3] /= normalize_v3(data->plane_tx[i]); } - copy_v4_fl(data->dist_vals_sq, FLT_MAX); + copy_v4_fl(data->dist_vals, FLT_MAX); data->tot = 0; data->is_ortho = params->is_ortho; if (params->is_ortho) { @@ -653,14 +653,9 @@ static bool camera_frame_fit_calc_from_data(CameraParams *params, const float *cam_axis_x = data->camera_rotmat[0]; const float *cam_axis_y = data->camera_rotmat[1]; const float *cam_axis_z = data->camera_rotmat[2]; - float dists[CAMERA_VIEWFRAME_NUM_PLANES]; + const float *dists = data->dist_vals; float scale_diff; - /* apply the dist-from-plane's to the transformed plane points */ - for (int i = 0; i < CAMERA_VIEWFRAME_NUM_PLANES; i++) { - dists[i] = sqrtf_signed(data->dist_vals_sq[i]); - } - if ((dists[0] + dists[2]) > (dists[1] + dists[3])) { scale_diff = (dists[1] + dists[3]) * (BLI_rctf_size_x(¶ms->viewplane) / BLI_rctf_size_y(¶ms->viewplane)); @@ -687,8 +682,8 @@ static bool camera_frame_fit_calc_from_data(CameraParams *params, /* apply the dist-from-plane's to the transformed plane points */ for (int i = 0; i < CAMERA_VIEWFRAME_NUM_PLANES; i++) { float co[3]; - mul_v3_v3fl(co, data->normal_tx[i], sqrtf_signed(data->dist_vals_sq[i])); - plane_from_point_normal_v3(plane_tx[i], co, data->normal_tx[i]); + mul_v3_v3fl(co, data->plane_tx[i], data->dist_vals[i]); + plane_from_point_normal_v3(plane_tx[i], co, data->plane_tx[i]); } if ((!isect_plane_plane_v3(plane_tx[0], plane_tx[2], plane_isect_1, plane_isect_1_no)) || diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 69002a71f1d..2afe4dda35c 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -529,8 +529,8 @@ static void contarget_get_mesh_mat(Object *ob, const char *substring, float mat[ float vec[3] = {0.0f, 0.0f, 0.0f}; float normal[3] = {0.0f, 0.0f, 0.0f}; float weightsum = 0.0f; - const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(me_eval); if (me_eval) { + const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(me_eval); const MDeformVert *dvert = CustomData_get_layer(&me_eval->vdata, CD_MDEFORMVERT); int numVerts = me_eval->totvert; @@ -1493,7 +1493,7 @@ static void followpath_get_tarmat(struct Depsgraph *UNUSED(depsgraph), { bFollowPathConstraint *data = con->data; - if (VALID_CONS_TARGET(ct) && (ct->tar->type == OB_CURVE)) { + if (VALID_CONS_TARGET(ct) && (ct->tar->type == OB_CURVES_LEGACY)) { Curve *cu = ct->tar->data; float vec[4], dir[3], radius; float curvetime; @@ -2479,7 +2479,7 @@ static void pycon_get_tarmat(struct Depsgraph *UNUSED(depsgraph), #endif if (VALID_CONS_TARGET(ct)) { - if (ct->tar->type == OB_CURVE && ct->tar->runtime.curve_cache == NULL) { + if (ct->tar->type == OB_CURVES_LEGACY && ct->tar->runtime.curve_cache == NULL) { unit_m4(ct->matrix); return; } @@ -3867,7 +3867,7 @@ static void clampto_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar bConstraintTarget *ct = targets->first; /* only evaluate if there is a target and it is a curve */ - if (VALID_CONS_TARGET(ct) && (ct->tar->type == OB_CURVE)) { + if (VALID_CONS_TARGET(ct) && (ct->tar->type == OB_CURVES_LEGACY)) { float obmat[4][4], ownLoc[3]; float curveMin[3], curveMax[3]; float targetMatrix[4][4]; diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c index d1374958763..28bcd961e26 100644 --- a/source/blender/blenkernel/intern/context.c +++ b/source/blender/blenkernel/intern/context.c @@ -1148,7 +1148,7 @@ enum eContextObjectMode CTX_data_mode_enum_ex(const Object *obedit, switch (obedit->type) { case OB_MESH: return CTX_MODE_EDIT_MESH; - case OB_CURVE: + case OB_CURVES_LEGACY: return CTX_MODE_EDIT_CURVE; case OB_SURF: return CTX_MODE_EDIT_SURFACE; @@ -1160,6 +1160,8 @@ enum eContextObjectMode CTX_data_mode_enum_ex(const Object *obedit, return CTX_MODE_EDIT_METABALL; case OB_LATTICE: return CTX_MODE_EDIT_LATTICE; + case OB_CURVES: + return CTX_MODE_EDIT_CURVES; } } else { @@ -1227,6 +1229,7 @@ static const char *data_mode_strings[] = { "armature_edit", "mball_edit", "lattice_edit", + "curves_edit", "posemode", "sculpt_mode", "weightpaint", diff --git a/source/blender/blenkernel/intern/curve.cc b/source/blender/blenkernel/intern/curve.cc index c6c0111780e..6be04b79761 100644 --- a/source/blender/blenkernel/intern/curve.cc +++ b/source/blender/blenkernel/intern/curve.cc @@ -115,6 +115,8 @@ static void curve_free_data(ID *id) MEM_SAFE_FREE(curve->str); MEM_SAFE_FREE(curve->strinfo); MEM_SAFE_FREE(curve->tb); + + delete curve->curve_eval; } static void curve_foreach_id(ID *id, LibraryForeachIDData *data) @@ -293,14 +295,14 @@ static void curve_blend_read_expand(BlendExpander *expander, ID *id) BLO_expand(expander, cu->textoncurve); } -IDTypeInfo IDType_ID_CU = { - /* id_code */ ID_CU, - /* id_filter */ FILTER_ID_CU, - /* main_listbase_index */ INDEX_ID_CU, +IDTypeInfo IDType_ID_CU_LEGACY = { + /* id_code */ ID_CU_LEGACY, + /* id_filter */ FILTER_ID_CU_LEGACY, + /* main_listbase_index */ INDEX_ID_CU_LEGACY, /* struct_size */ sizeof(Curve), /* name */ "Curve", /* name_plural */ "curves", - /* translation_context */ BLT_I18NCONTEXT_ID_CURVE, + /* translation_context */ BLT_I18NCONTEXT_ID_CURVE_LEGACY, /* flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE, /* asset_type_info */ nullptr, @@ -406,7 +408,7 @@ Curve *BKE_curve_add(Main *bmain, const char *name, int type) Curve *cu; /* We cannot use #BKE_id_new here as we need some custom initialization code. */ - cu = (Curve *)BKE_libblock_alloc(bmain, ID_CU, name, 0); + cu = (Curve *)BKE_libblock_alloc(bmain, ID_CU_LEGACY, name, 0); BKE_curve_init(cu, type); @@ -440,7 +442,7 @@ short BKE_curve_type_get(const Curve *cu) } if (!cu->type) { - type = OB_CURVE; + type = OB_CURVES_LEGACY; LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) { if (nu->pntsv > 1) { @@ -473,7 +475,7 @@ void BKE_curve_type_test(Object *ob) { ob->type = BKE_curve_type_get((Curve *)ob->data); - if (ob->type == OB_CURVE) { + if (ob->type == OB_CURVES_LEGACY) { Curve *cu = (Curve *)ob->data; if (CU_IS_2D(cu)) { BKE_curve_dimension_update(cu); diff --git a/source/blender/blenkernel/intern/curve_bevel.c b/source/blender/blenkernel/intern/curve_bevel.c index ffef22fb498..6f32f0f5e6f 100644 --- a/source/blender/blenkernel/intern/curve_bevel.c +++ b/source/blender/blenkernel/intern/curve_bevel.c @@ -228,7 +228,7 @@ static void curve_bevel_make_from_object(const Curve *cu, ListBase *disp) if (cu->bevobj == NULL) { return; } - if (cu->bevobj->type != OB_CURVE) { + if (cu->bevobj->type != OB_CURVES_LEGACY) { return; } diff --git a/source/blender/blenkernel/intern/curve_convert.c b/source/blender/blenkernel/intern/curve_convert.c index 285e6978522..129e930a21e 100644 --- a/source/blender/blenkernel/intern/curve_convert.c +++ b/source/blender/blenkernel/intern/curve_convert.c @@ -27,7 +27,7 @@ static Curve *curve_from_font_object(Object *object, Depsgraph *depsgraph) Object *evaluated_object = DEG_get_evaluated_object(depsgraph, object); BKE_vfont_to_curve_nubase(evaluated_object, FO_EDIT, &new_curve->nurb); - new_curve->type = OB_CURVE; + new_curve->type = OB_CURVES_LEGACY; new_curve->flag &= ~CU_3D; BKE_curve_dimension_update(new_curve); @@ -55,7 +55,7 @@ static Curve *curve_from_curve_object(Object *object, Depsgraph *depsgraph, bool Curve *BKE_curve_new_from_object(Object *object, Depsgraph *depsgraph, bool apply_modifiers) { - if (!ELEM(object->type, OB_FONT, OB_CURVE)) { + if (!ELEM(object->type, OB_FONT, OB_CURVES_LEGACY)) { return NULL; } diff --git a/source/blender/blenkernel/intern/curve_deform.c b/source/blender/blenkernel/intern/curve_deform.c index f76e4202994..fb082fccc0b 100644 --- a/source/blender/blenkernel/intern/curve_deform.c +++ b/source/blender/blenkernel/intern/curve_deform.c @@ -211,7 +211,7 @@ static void curve_deform_coords_impl(const Object *ob_curve, bool use_dverts = false; int cd_dvert_offset; - if (ob_curve->type != OB_CURVE) { + if (ob_curve->type != OB_CURVES_LEGACY) { return; } @@ -404,7 +404,7 @@ void BKE_curve_deform_co(const Object *ob_curve, CurveDeform cd; float quat[4]; - if (ob_curve->type != OB_CURVE) { + if (ob_curve->type != OB_CURVES_LEGACY) { unit_m3(r_mat); return; } diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc index 7fb833e67f8..d6525a11cff 100644 --- a/source/blender/blenkernel/intern/curve_eval.cc +++ b/source/blender/blenkernel/intern/curve_eval.cc @@ -13,6 +13,8 @@ #include "BKE_anonymous_attribute.hh" #include "BKE_curve.h" +#include "BKE_curves.hh" +#include "BKE_geometry_set.hh" #include "BKE_spline.hh" using blender::Array; @@ -23,8 +25,15 @@ using blender::Map; using blender::MutableSpan; using blender::Span; using blender::StringRefNull; +using blender::VArray; +using blender::VArray_Span; using blender::Vector; using blender::bke::AttributeIDRef; +using blender::bke::OutputAttribute; +using blender::bke::OutputAttribute_Typed; +using blender::bke::ReadAttributeLookup; +using blender::fn::GVArray; +using blender::fn::GVArray_GSpan; blender::Span<SplinePtr> CurveEval::splines() const { @@ -36,7 +45,7 @@ blender::MutableSpan<SplinePtr> CurveEval::splines() return splines_; } -bool CurveEval::has_spline_with_type(const Spline::Type type) const +bool CurveEval::has_spline_with_type(const CurveType type) const { for (const SplinePtr &spline : this->splines()) { if (spline->type() == type) { @@ -160,24 +169,24 @@ void CurveEval::mark_cache_invalid() } } -static BezierSpline::HandleType handle_type_from_dna_bezt(const eBezTriple_Handle dna_handle_type) +static HandleType handle_type_from_dna_bezt(const eBezTriple_Handle dna_handle_type) { switch (dna_handle_type) { case HD_FREE: - return BezierSpline::HandleType::Free; + return BEZIER_HANDLE_FREE; case HD_AUTO: - return BezierSpline::HandleType::Auto; + return BEZIER_HANDLE_AUTO; case HD_VECT: - return BezierSpline::HandleType::Vector; + return BEZIER_HANDLE_VECTOR; case HD_ALIGN: - return BezierSpline::HandleType::Align; + return BEZIER_HANDLE_ALIGN; case HD_AUTO_ANIM: - return BezierSpline::HandleType::Auto; + return BEZIER_HANDLE_AUTO; case HD_ALIGN_DOUBLESIDE: - return BezierSpline::HandleType::Align; + return BEZIER_HANDLE_ALIGN; } BLI_assert_unreachable(); - return BezierSpline::HandleType::Auto; + return BEZIER_HANDLE_AUTO; } static Spline::NormalCalculationMode normal_mode_from_dna_curve(const int twist_mode) @@ -220,8 +229,8 @@ static SplinePtr spline_from_dna_bezier(const Nurb &nurb) MutableSpan<float3> positions = spline->positions(); MutableSpan<float3> handle_positions_left = spline->handle_positions_left(true); MutableSpan<float3> handle_positions_right = spline->handle_positions_right(true); - MutableSpan<BezierSpline::HandleType> handle_types_left = spline->handle_types_left(); - MutableSpan<BezierSpline::HandleType> handle_types_right = spline->handle_types_right(); + MutableSpan<int8_t> handle_types_left = spline->handle_types_left(); + MutableSpan<int8_t> handle_types_right = spline->handle_types_right(); MutableSpan<float> radii = spline->radii(); MutableSpan<float> tilts = spline->tilts(); @@ -336,6 +345,186 @@ std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve) return curve_eval_from_dna_curve(dna_curve, *BKE_curve_nurbs_get_for_read(&dna_curve)); } +static void copy_attributes_between_components(const GeometryComponent &src_component, + GeometryComponent &dst_component, + Span<std::string> skip) +{ + src_component.attribute_foreach( + [&](const AttributeIDRef &id, const AttributeMetaData meta_data) { + if (id.is_named() && skip.contains(id.name())) { + return true; + } + + GVArray src_attribute = src_component.attribute_try_get_for_read( + id, meta_data.domain, meta_data.data_type); + if (!src_attribute) { + return true; + } + GVArray_GSpan src_attribute_data{src_attribute}; + + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + id, meta_data.domain, meta_data.data_type); + if (!dst_attribute) { + return true; + } + dst_attribute.varray().set_all(src_attribute_data.data()); + dst_attribute.save(); + return true; + }); +} + +std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves) +{ + CurveComponent src_component; + src_component.replace(&const_cast<Curves &>(curves), GeometryOwnershipType::ReadOnly); + const blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap( + curves.geometry); + + VArray_Span<float> nurbs_weights{ + src_component.attribute_get_for_read<float>("nurbs_weight", ATTR_DOMAIN_POINT, 0.0f)}; + VArray_Span<int> nurbs_orders{ + src_component.attribute_get_for_read<int>("nurbs_order", ATTR_DOMAIN_CURVE, 4)}; + VArray_Span<int8_t> nurbs_knots_modes{ + src_component.attribute_get_for_read<int8_t>("knots_mode", ATTR_DOMAIN_CURVE, 0)}; + + VArray_Span<int8_t> handle_types_right{ + src_component.attribute_get_for_read<int8_t>("handle_type_right", ATTR_DOMAIN_POINT, 0)}; + VArray_Span<int8_t> handle_types_left{ + src_component.attribute_get_for_read<int8_t>("handle_type_left", ATTR_DOMAIN_POINT, 0)}; + + /* Create splines with the correct size and type. */ + VArray<int8_t> curve_types = geometry.curve_types(); + std::unique_ptr<CurveEval> curve_eval = std::make_unique<CurveEval>(); + for (const int curve_index : curve_types.index_range()) { + const IndexRange point_range = geometry.range_for_curve(curve_index); + + std::unique_ptr<Spline> spline; + switch (curve_types[curve_index]) { + case CURVE_TYPE_POLY: { + spline = std::make_unique<PolySpline>(); + spline->resize(point_range.size()); + break; + } + case CURVE_TYPE_BEZIER: { + std::unique_ptr<BezierSpline> bezier_spline = std::make_unique<BezierSpline>(); + bezier_spline->resize(point_range.size()); + bezier_spline->handle_types_left().copy_from(handle_types_left.slice(point_range)); + bezier_spline->handle_types_right().copy_from(handle_types_right.slice(point_range)); + + spline = std::move(bezier_spline); + break; + } + case CURVE_TYPE_NURBS: { + std::unique_ptr<NURBSpline> nurb_spline = std::make_unique<NURBSpline>(); + nurb_spline->resize(point_range.size()); + nurb_spline->weights().copy_from(nurbs_weights.slice(point_range)); + nurb_spline->set_order(nurbs_orders[curve_index]); + nurb_spline->knots_mode = static_cast<NURBSpline::KnotsMode>( + nurbs_knots_modes[curve_index]); + + spline = std::move(nurb_spline); + break; + } + case CURVE_TYPE_CATMULL_ROM: + /* Not supported yet. */ + BLI_assert_unreachable(); + continue; + } + spline->positions().fill(float3(0)); + spline->tilts().fill(0.0f); + spline->radii().fill(1.0f); + curve_eval->add_spline(std::move(spline)); + } + + CurveComponentLegacy dst_component; + dst_component.replace(curve_eval.get(), GeometryOwnershipType::Editable); + + copy_attributes_between_components(src_component, + dst_component, + {"curve_type", + "nurbs_weight", + "nurbs_order", + "knots_mode", + "handle_type_right", + "handle_type_left"}); + + return curve_eval; +} + +Curves *curve_eval_to_curves(const CurveEval &curve_eval) +{ + Curves *curves = blender::bke::curves_new_nomain(curve_eval.total_control_point_size(), + curve_eval.splines().size()); + CurveComponent dst_component; + dst_component.replace(curves, GeometryOwnershipType::Editable); + + blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap(curves->geometry); + geometry.offsets().copy_from(curve_eval.control_point_offsets()); + MutableSpan<int8_t> curve_types = geometry.curve_types(); + + OutputAttribute_Typed<float> nurbs_weight; + OutputAttribute_Typed<int> nurbs_order; + OutputAttribute_Typed<int8_t> nurbs_knots_mode; + if (curve_eval.has_spline_with_type(CURVE_TYPE_NURBS)) { + nurbs_weight = dst_component.attribute_try_get_for_output_only<float>("nurbs_weight", + ATTR_DOMAIN_POINT); + nurbs_order = dst_component.attribute_try_get_for_output_only<int>("nurbs_order", + ATTR_DOMAIN_CURVE); + nurbs_knots_mode = dst_component.attribute_try_get_for_output_only<int8_t>("knots_mode", + ATTR_DOMAIN_CURVE); + } + OutputAttribute_Typed<int8_t> handle_type_right; + OutputAttribute_Typed<int8_t> handle_type_left; + if (curve_eval.has_spline_with_type(CURVE_TYPE_BEZIER)) { + handle_type_right = dst_component.attribute_try_get_for_output_only<int8_t>( + "handle_type_right", ATTR_DOMAIN_POINT); + handle_type_left = dst_component.attribute_try_get_for_output_only<int8_t>("handle_type_left", + ATTR_DOMAIN_POINT); + } + + for (const int curve_index : curve_eval.splines().index_range()) { + const Spline &spline = *curve_eval.splines()[curve_index]; + curve_types[curve_index] = curve_eval.splines()[curve_index]->type(); + + const IndexRange point_range = geometry.range_for_curve(curve_index); + + switch (spline.type()) { + case CURVE_TYPE_POLY: + break; + case CURVE_TYPE_BEZIER: { + const BezierSpline &src = static_cast<const BezierSpline &>(spline); + handle_type_right.as_span().slice(point_range).copy_from(src.handle_types_right()); + handle_type_left.as_span().slice(point_range).copy_from(src.handle_types_left()); + break; + } + case CURVE_TYPE_NURBS: { + const NURBSpline &src = static_cast<const NURBSpline &>(spline); + nurbs_knots_mode.as_span()[curve_index] = static_cast<int8_t>(src.knots_mode); + nurbs_order.as_span()[curve_index] = src.order(); + nurbs_weight.as_span().slice(point_range).copy_from(src.weights()); + break; + } + case CURVE_TYPE_CATMULL_ROM: { + BLI_assert_unreachable(); + break; + } + } + } + + nurbs_weight.save(); + nurbs_order.save(); + nurbs_knots_mode.save(); + handle_type_right.save(); + handle_type_left.save(); + + CurveComponentLegacy src_component; + src_component.replace(&const_cast<CurveEval &>(curve_eval), GeometryOwnershipType::ReadOnly); + + copy_attributes_between_components(src_component, dst_component, {}); + + return curves; +} + void CurveEval::assert_valid_point_attributes() const { #ifdef DEBUG diff --git a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc index 51edf4a6591..5d80ef47908 100644 --- a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc +++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc @@ -172,7 +172,8 @@ static void spline_extrude_to_mesh_data(const ResultInfo &info, } } - if (fill_caps && profile.is_cyclic()) { + const bool has_caps = fill_caps && profile.is_cyclic() && !spline.is_cyclic(); + if (has_caps) { const int poly_size = info.spline_edge_len * info.profile_edge_len; const int cap_loop_offset = info.loop_offset + poly_size * 4; const int cap_poly_offset = info.poly_offset + poly_size; @@ -225,7 +226,7 @@ static void spline_extrude_to_mesh_data(const ResultInfo &info, } /* Mark edge loops from sharp vector control points sharp. */ - if (profile.type() == Spline::Type::Bezier) { + if (profile.type() == CURVE_TYPE_BEZIER) { const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(profile); Span<int> control_point_offsets = bezier_spline.control_point_offsets(); for (const int i : IndexRange(bezier_spline.size())) { @@ -256,7 +257,8 @@ static inline int spline_extrude_loop_size(const Spline &curve, const bool fill_caps) { const int tube = curve.evaluated_edges_size() * profile.evaluated_edges_size() * 4; - const int caps = (fill_caps && profile.is_cyclic()) ? profile.evaluated_edges_size() * 2 : 0; + const bool has_caps = fill_caps && profile.is_cyclic() && !curve.is_cyclic(); + const int caps = has_caps ? profile.evaluated_edges_size() * 2 : 0; return tube + caps; } @@ -265,7 +267,8 @@ static inline int spline_extrude_poly_size(const Spline &curve, const bool fill_caps) { const int tube = curve.evaluated_edges_size() * profile.evaluated_edges_size(); - const int caps = (fill_caps && profile.is_cyclic()) ? 2 : 0; + const bool has_caps = fill_caps && profile.is_cyclic() && !curve.is_cyclic(); + const int caps = has_caps ? 2 : 0; return tube + caps; } diff --git a/source/blender/blenkernel/intern/curves.cc b/source/blender/blenkernel/intern/curves.cc index ccc20d5118a..838f7f28e93 100644 --- a/source/blender/blenkernel/intern/curves.cc +++ b/source/blender/blenkernel/intern/curves.cc @@ -14,16 +14,18 @@ #include "DNA_material_types.h" #include "DNA_object_types.h" +#include "BLI_bounds.hh" #include "BLI_index_range.hh" #include "BLI_listbase.h" #include "BLI_math_base.h" #include "BLI_math_vector.hh" #include "BLI_rand.hh" +#include "BLI_span.hh" #include "BLI_string.h" #include "BLI_utildefines.h" #include "BKE_anim_data.h" -#include "BKE_curves.h" +#include "BKE_curves.hh" #include "BKE_customdata.h" #include "BKE_global.h" #include "BKE_idtype.h" @@ -44,11 +46,11 @@ using blender::float3; using blender::IndexRange; using blender::MutableSpan; using blender::RandomNumberGenerator; +using blender::Span; static const char *ATTR_POSITION = "position"; -static const char *ATTR_RADIUS = "radius"; -static void curves_random(Curves *curves); +static void update_custom_data_pointers(Curves &curves); static void curves_init_data(ID *id) { @@ -57,50 +59,36 @@ static void curves_init_data(ID *id) MEMCPY_STRUCT_AFTER(curves, DNA_struct_default_get(Curves), id); - CustomData_reset(&curves->geometry.point_data); - CustomData_reset(&curves->geometry.curve_data); - - CustomData_add_layer_named(&curves->geometry.point_data, - CD_PROP_FLOAT3, - CD_CALLOC, - nullptr, - curves->geometry.point_size, - ATTR_POSITION); - CustomData_add_layer_named(&curves->geometry.point_data, - CD_PROP_FLOAT, - CD_CALLOC, - nullptr, - curves->geometry.point_size, - ATTR_RADIUS); - - BKE_curves_update_customdata_pointers(curves); - - curves_random(curves); + new (&curves->geometry) blender::bke::CurvesGeometry(); } static void curves_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, const int flag) { + using namespace blender; + Curves *curves_dst = (Curves *)id_dst; const Curves *curves_src = (const Curves *)id_src; curves_dst->mat = static_cast<Material **>(MEM_dupallocN(curves_src->mat)); - curves_dst->geometry.point_size = curves_src->geometry.point_size; - curves_dst->geometry.curve_size = curves_src->geometry.curve_size; + const bke::CurvesGeometry &src = bke::CurvesGeometry::wrap(curves_src->geometry); + bke::CurvesGeometry &dst = bke::CurvesGeometry::wrap(curves_dst->geometry); + + /* We need special handling here because the generic ID management code has already done a + * shallow copy from the source to the destination, and because the copy-on-write functionality + * isn't supported more generically yet. */ + + dst.point_size = src.point_size; + dst.curve_size = src.curve_size; const eCDAllocType alloc_type = (flag & LIB_ID_COPY_CD_REFERENCE) ? CD_REFERENCE : CD_DUPLICATE; - CustomData_copy(&curves_src->geometry.point_data, - &curves_dst->geometry.point_data, - CD_MASK_ALL, - alloc_type, - curves_dst->geometry.point_size); - CustomData_copy(&curves_src->geometry.curve_data, - &curves_dst->geometry.curve_data, - CD_MASK_ALL, - alloc_type, - curves_dst->geometry.curve_size); - BKE_curves_update_customdata_pointers(curves_dst); - - curves_dst->geometry.offsets = static_cast<int *>(MEM_dupallocN(curves_src->geometry.offsets)); + CustomData_copy(&src.point_data, &dst.point_data, CD_MASK_ALL, alloc_type, dst.point_size); + CustomData_copy(&src.curve_data, &dst.curve_data, CD_MASK_ALL, alloc_type, dst.curve_size); + + dst.curve_offsets = static_cast<int *>(MEM_dupallocN(src.curve_offsets)); + + dst.runtime = MEM_new<bke::CurvesGeometryRuntime>(__func__); + + dst.update_customdata_pointers(); curves_dst->batch_cache = nullptr; } @@ -110,12 +98,9 @@ static void curves_free_data(ID *id) Curves *curves = (Curves *)id; BKE_animdata_free(&curves->id, false); - BKE_curves_batch_cache_free(curves); - - CustomData_free(&curves->geometry.point_data, curves->geometry.point_size); - CustomData_free(&curves->geometry.curve_data, curves->geometry.curve_size); + blender::bke::CurvesGeometry::wrap(curves->geometry).~CurvesGeometry(); - MEM_SAFE_FREE(curves->geometry.offsets); + BKE_curves_batch_cache_free(curves); MEM_SAFE_FREE(curves->mat); } @@ -126,6 +111,7 @@ static void curves_foreach_id(ID *id, LibraryForeachIDData *data) for (int i = 0; i < curves->totcol; i++) { BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curves->mat[i], IDWALK_CB_USER); } + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curves->surface, IDWALK_CB_NOP); } static void curves_blend_write(BlendWriter *writer, ID *id, const void *id_address) @@ -157,7 +143,7 @@ static void curves_blend_write(BlendWriter *writer, ID *id, const void *id_addre CD_MASK_ALL, &curves->id); - BLO_write_int32_array(writer, curves->geometry.curve_size + 1, curves->geometry.offsets); + BLO_write_int32_array(writer, curves->geometry.curve_size + 1, curves->geometry.curve_offsets); BLO_write_pointer_array(writer, curves->totcol, curves->mat); if (curves->adt) { @@ -182,9 +168,11 @@ static void curves_blend_read_data(BlendDataReader *reader, ID *id) /* Geometry */ CustomData_blend_read(reader, &curves->geometry.point_data, curves->geometry.point_size); CustomData_blend_read(reader, &curves->geometry.curve_data, curves->geometry.curve_size); - BKE_curves_update_customdata_pointers(curves); + update_custom_data_pointers(*curves); - BLO_read_int32_array(reader, curves->geometry.curve_size + 1, &curves->geometry.offsets); + BLO_read_int32_array(reader, curves->geometry.curve_size + 1, &curves->geometry.curve_offsets); + + curves->geometry.runtime = MEM_new<blender::bke::CurvesGeometryRuntime>(__func__); /* Materials */ BLO_read_pointer_array(reader, (void **)&curves->mat); @@ -196,6 +184,7 @@ static void curves_blend_read_lib(BlendLibReader *reader, ID *id) for (int a = 0; a < curves->totcol; a++) { BLO_read_id_address(reader, curves->id.lib, &curves->mat[a]); } + BLO_read_id_address(reader, curves->id.lib, &curves->surface); } static void curves_blend_read_expand(BlendExpander *expander, ID *id) @@ -204,6 +193,7 @@ static void curves_blend_read_expand(BlendExpander *expander, ID *id) for (int a = 0; a < curves->totcol; a++) { BLO_expand(expander, curves->mat[a]); } + BLO_expand(expander, curves->surface); } IDTypeInfo IDType_ID_CV = { @@ -236,53 +226,9 @@ IDTypeInfo IDType_ID_CV = { /*lib_override_apply_post */ nullptr, }; -static void curves_random(Curves *curves) +static void update_custom_data_pointers(Curves &curves) { - CurvesGeometry &geometry = curves->geometry; - const int numpoints = 8; - - geometry.curve_size = 500; - - geometry.curve_size = 500; - geometry.point_size = geometry.curve_size * numpoints; - - curves->geometry.offsets = (int *)MEM_calloc_arrayN( - curves->geometry.curve_size + 1, sizeof(int), __func__); - CustomData_realloc(&geometry.point_data, geometry.point_size); - CustomData_realloc(&geometry.curve_data, geometry.curve_size); - BKE_curves_update_customdata_pointers(curves); - - MutableSpan<int> offsets{geometry.offsets, geometry.curve_size + 1}; - MutableSpan<float3> positions{(float3 *)geometry.position, geometry.point_size}; - MutableSpan<float> radii{geometry.radius, geometry.point_size}; - - for (const int i : offsets.index_range()) { - geometry.offsets[i] = numpoints * i; - } - - RandomNumberGenerator rng; - - for (int i = 0; i < geometry.curve_size; i++) { - const IndexRange curve_range(offsets[i], offsets[i + 1] - offsets[i]); - MutableSpan<float3> curve_positions = positions.slice(curve_range); - MutableSpan<float> curve_radii = radii.slice(curve_range); - - const float theta = 2.0f * M_PI * rng.get_float(); - const float phi = saacosf(2.0f * rng.get_float() - 1.0f); - - float3 no = {std::sin(theta) * std::sin(phi), std::cos(theta) * std::sin(phi), std::cos(phi)}; - no = blender::math::normalize(no); - - float3 co = no; - for (int key = 0; key < numpoints; key++) { - float t = key / (float)(numpoints - 1); - curve_positions[key] = co; - curve_radii[key] = 0.02f * (1.0f - t); - - float3 offset = float3(rng.get_float(), rng.get_float(), rng.get_float()) * 2.0f - 1.0f; - co += (offset + no) / numpoints; - } - } + blender::bke::CurvesGeometry::wrap(curves.geometry).update_customdata_pointers(); } void *BKE_curves_add(Main *bmain, const char *name) @@ -304,18 +250,13 @@ BoundBox *BKE_curves_boundbox_get(Object *ob) if (ob->runtime.bb == nullptr) { ob->runtime.bb = MEM_cnew<BoundBox>(__func__); - float min[3], max[3]; - INIT_MINMAX(min, max); - - float(*curves_co)[3] = curves->geometry.position; - float *curves_radius = curves->geometry.radius; - for (int a = 0; a < curves->geometry.point_size; a++) { - float *co = curves_co[a]; - float radius = (curves_radius) ? curves_radius[a] : 0.0f; - const float co_min[3] = {co[0] - radius, co[1] - radius, co[2] - radius}; - const float co_max[3] = {co[0] + radius, co[1] + radius, co[2] + radius}; - DO_MIN(co_min, min); - DO_MAX(co_max, max); + blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap(curves->geometry); + + float3 min(FLT_MAX); + float3 max(-FLT_MAX); + if (!geometry.bounds_min_max(min, max)) { + min = float3(-1); + max = float3(1); } BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max); @@ -324,46 +265,11 @@ BoundBox *BKE_curves_boundbox_get(Object *ob) return ob->runtime.bb; } -void BKE_curves_update_customdata_pointers(Curves *curves) -{ - curves->geometry.position = (float(*)[3])CustomData_get_layer_named( - &curves->geometry.point_data, CD_PROP_FLOAT3, ATTR_POSITION); - curves->geometry.radius = (float *)CustomData_get_layer_named( - &curves->geometry.point_data, CD_PROP_FLOAT, ATTR_RADIUS); -} - bool BKE_curves_customdata_required(Curves *UNUSED(curves), CustomDataLayer *layer) { return layer->type == CD_PROP_FLOAT3 && STREQ(layer->name, ATTR_POSITION); } -/* Dependency Graph */ - -Curves *BKE_curves_new_for_eval(const Curves *curves_src, int totpoint, int totcurve) -{ - Curves *curves_dst = static_cast<Curves *>(BKE_id_new_nomain(ID_CV, nullptr)); - - STRNCPY(curves_dst->id.name, curves_src->id.name); - curves_dst->mat = static_cast<Material **>(MEM_dupallocN(curves_src->mat)); - curves_dst->totcol = curves_src->totcol; - - curves_dst->geometry.point_size = totpoint; - curves_dst->geometry.curve_size = totcurve; - CustomData_copy(&curves_src->geometry.point_data, - &curves_dst->geometry.point_data, - CD_MASK_ALL, - CD_CALLOC, - totpoint); - CustomData_copy(&curves_src->geometry.curve_data, - &curves_dst->geometry.curve_data, - CD_MASK_ALL, - CD_CALLOC, - totcurve); - BKE_curves_update_customdata_pointers(curves_dst); - - return curves_dst; -} - Curves *BKE_curves_copy_for_eval(Curves *curves_src, bool reference) { int flags = LIB_ID_COPY_LOCALIZE; @@ -409,16 +315,16 @@ static Curves *curves_evaluate_modifiers(struct Depsgraph *depsgraph, curves = BKE_curves_copy_for_eval(curves, true); } - /* Ensure we are not overwriting referenced data. */ - CustomData_duplicate_referenced_layer_named(&curves->geometry.point_data, - CD_PROP_FLOAT3, - ATTR_POSITION, - curves->geometry.point_size); - BKE_curves_update_customdata_pointers(curves); - /* Created deformed coordinates array on demand. */ - mti->deformVerts( - md, &mectx, nullptr, curves->geometry.position, curves->geometry.point_size); + blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap( + curves->geometry); + MutableSpan<float3> positions = geometry.positions(); + + mti->deformVerts(md, + &mectx, + nullptr, + reinterpret_cast<float(*)[3]>(positions.data()), + curves->geometry.point_size); } } @@ -457,3 +363,24 @@ void BKE_curves_batch_cache_free(Curves *curves) BKE_curves_batch_cache_free_cb(curves); } } + +namespace blender::bke { + +Curves *curves_new_nomain(const int point_size, const int curves_size) +{ + Curves *curves = static_cast<Curves *>(BKE_id_new_nomain(ID_CV, nullptr)); + CurvesGeometry &geometry = CurvesGeometry::wrap(curves->geometry); + geometry.resize(point_size, curves_size); + return curves; +} + +Curves *curves_new_nomain_single(const int point_size, const CurveType type) +{ + Curves *curves = curves_new_nomain(point_size, 1); + CurvesGeometry &geometry = CurvesGeometry::wrap(curves->geometry); + geometry.offsets().last() = point_size; + geometry.curve_types().first() = type; + return curves; +} + +} // namespace blender::bke diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc new file mode 100644 index 00000000000..3eea579230a --- /dev/null +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -0,0 +1,394 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup bke + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_bounds.hh" + +#include "DNA_curves_types.h" + +#include "BKE_attribute_math.hh" +#include "BKE_curves.hh" + +namespace blender::bke { + +static const std::string ATTR_POSITION = "position"; +static const std::string ATTR_RADIUS = "radius"; +static const std::string ATTR_CURVE_TYPE = "curve_type"; +static const std::string ATTR_CYCLIC = "cyclic"; + +/* -------------------------------------------------------------------- */ +/** \name Constructors/Destructor + * \{ */ + +CurvesGeometry::CurvesGeometry() : CurvesGeometry(0, 0) +{ +} + +CurvesGeometry::CurvesGeometry(const int point_size, const int curve_size) +{ + this->point_size = point_size; + this->curve_size = curve_size; + CustomData_reset(&this->point_data); + CustomData_reset(&this->curve_data); + + CustomData_add_layer_named(&this->point_data, + CD_PROP_FLOAT3, + CD_DEFAULT, + nullptr, + this->point_size, + ATTR_POSITION.c_str()); + + this->curve_offsets = (int *)MEM_calloc_arrayN(this->curve_size + 1, sizeof(int), __func__); + + this->update_customdata_pointers(); + + this->runtime = MEM_new<CurvesGeometryRuntime>(__func__); +} + +/** + * \note Expects `dst` to be initialized, since the original attributes must be freed. + */ +static void copy_curves_geometry(CurvesGeometry &dst, const CurvesGeometry &src) +{ + CustomData_free(&dst.point_data, dst.point_size); + CustomData_free(&dst.curve_data, dst.curve_size); + dst.point_size = src.point_size; + dst.curve_size = src.curve_size; + CustomData_copy(&src.point_data, &dst.point_data, CD_MASK_ALL, CD_DUPLICATE, dst.point_size); + CustomData_copy(&src.curve_data, &dst.curve_data, CD_MASK_ALL, CD_DUPLICATE, dst.curve_size); + + MEM_SAFE_FREE(dst.curve_offsets); + dst.curve_offsets = (int *)MEM_calloc_arrayN(dst.point_size + 1, sizeof(int), __func__); + dst.offsets().copy_from(src.offsets()); + + dst.tag_topology_changed(); + + dst.update_customdata_pointers(); +} + +CurvesGeometry::CurvesGeometry(const CurvesGeometry &other) + : CurvesGeometry(other.point_size, other.curve_size) +{ + copy_curves_geometry(*this, other); +} + +CurvesGeometry &CurvesGeometry::operator=(const CurvesGeometry &other) +{ + if (this != &other) { + copy_curves_geometry(*this, other); + } + return *this; +} + +CurvesGeometry::~CurvesGeometry() +{ + CustomData_free(&this->point_data, this->point_size); + CustomData_free(&this->curve_data, this->curve_size); + MEM_SAFE_FREE(this->curve_offsets); + MEM_delete(this->runtime); + this->runtime = nullptr; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Accessors + * \{ */ + +int CurvesGeometry::points_size() const +{ + return this->point_size; +} +int CurvesGeometry::curves_size() const +{ + return this->curve_size; +} +IndexRange CurvesGeometry::points_range() const +{ + return IndexRange(this->points_size()); +} +IndexRange CurvesGeometry::curves_range() const +{ + return IndexRange(this->curves_size()); +} + +int CurvesGeometry::evaluated_points_size() const +{ + /* TODO: Implement when there are evaluated points. */ + return 0; +} + +IndexRange CurvesGeometry::range_for_curve(const int index) const +{ + const int offset = this->curve_offsets[index]; + const int offset_next = this->curve_offsets[index + 1]; + return {offset, offset_next - offset}; +} + +VArray<int8_t> CurvesGeometry::curve_types() const +{ + if (const int8_t *data = (const int8_t *)CustomData_get_layer_named( + &this->curve_data, CD_PROP_INT8, ATTR_CURVE_TYPE.c_str())) { + return VArray<int8_t>::ForSpan({data, this->curve_size}); + } + return VArray<int8_t>::ForSingle(CURVE_TYPE_CATMULL_ROM, this->curve_size); +} + +MutableSpan<int8_t> CurvesGeometry::curve_types() +{ + int8_t *data = (int8_t *)CustomData_add_layer_named(&this->curve_data, + CD_PROP_INT8, + CD_CALLOC, + nullptr, + this->curve_size, + ATTR_CURVE_TYPE.c_str()); + return {data, this->curve_size}; +} + +MutableSpan<float3> CurvesGeometry::positions() +{ + this->position = (float(*)[3])CustomData_duplicate_referenced_layer_named( + &this->point_data, CD_PROP_FLOAT3, ATTR_POSITION.c_str(), this->point_size); + return {(float3 *)this->position, this->point_size}; +} +Span<float3> CurvesGeometry::positions() const +{ + return {(const float3 *)this->position, this->point_size}; +} + +MutableSpan<int> CurvesGeometry::offsets() +{ + return {this->curve_offsets, this->curve_size + 1}; +} +Span<int> CurvesGeometry::offsets() const +{ + return {this->curve_offsets, this->curve_size + 1}; +} + +VArray<bool> CurvesGeometry::cyclic() const +{ + const bool *data = (const bool *)CustomData_get_layer_named( + &this->curve_data, CD_PROP_INT8, ATTR_CURVE_TYPE.c_str()); + if (data != nullptr) { + return VArray<bool>::ForSpan(Span(data, this->curve_size)); + } + return VArray<bool>::ForSingle(false, this->curve_size); +} + +MutableSpan<bool> CurvesGeometry::cyclic() +{ + bool *data = (bool *)CustomData_add_layer_named( + &this->curve_data, CD_PROP_BOOL, CD_CALLOC, nullptr, this->curve_size, ATTR_CYCLIC.c_str()); + return {data, this->curve_size}; +} + +void CurvesGeometry::resize(const int point_size, const int curve_size) +{ + if (point_size != this->point_size) { + CustomData_realloc(&this->point_data, point_size); + this->point_size = point_size; + } + if (curve_size != this->curve_size) { + CustomData_realloc(&this->curve_data, curve_size); + this->curve_size = curve_size; + this->curve_offsets = (int *)MEM_reallocN(this->curve_offsets, sizeof(int) * (curve_size + 1)); + } + this->tag_topology_changed(); + this->update_customdata_pointers(); +} + +void CurvesGeometry::tag_positions_changed() +{ + this->runtime->position_cache_dirty = true; + this->runtime->tangent_cache_dirty = true; + this->runtime->normal_cache_dirty = true; +} +void CurvesGeometry::tag_topology_changed() +{ + this->runtime->position_cache_dirty = true; + this->runtime->tangent_cache_dirty = true; + this->runtime->normal_cache_dirty = true; +} +void CurvesGeometry::tag_normals_changed() +{ + this->runtime->normal_cache_dirty = true; +} + +void CurvesGeometry::translate(const float3 &translation) +{ + MutableSpan<float3> positions = this->positions(); + threading::parallel_for(positions.index_range(), 2048, [&](const IndexRange range) { + for (float3 &position : positions.slice(range)) { + position += translation; + } + }); +} + +void CurvesGeometry::transform(const float4x4 &matrix) +{ + MutableSpan<float3> positions = this->positions(); + threading::parallel_for(positions.index_range(), 1024, [&](const IndexRange range) { + for (float3 &position : positions.slice(range)) { + position = matrix * position; + } + }); +} + +static std::optional<bounds::MinMaxResult<float3>> curves_bounds(const CurvesGeometry &curves) +{ + Span<float3> positions = curves.positions(); + if (curves.radius) { + Span<float> radii{curves.radius, curves.points_size()}; + return bounds::min_max_with_radii(positions, radii); + } + return bounds::min_max(positions); +} + +bool CurvesGeometry::bounds_min_max(float3 &min, float3 &max) const +{ + const std::optional<bounds::MinMaxResult<float3>> bounds = curves_bounds(*this); + if (!bounds) { + return false; + } + min = math::min(bounds->min, min); + max = math::max(bounds->max, max); + return true; +} + +void CurvesGeometry::update_customdata_pointers() +{ + this->position = (float(*)[3])CustomData_get_layer_named( + &this->point_data, CD_PROP_FLOAT3, ATTR_POSITION.c_str()); + this->radius = (float *)CustomData_get_layer_named( + &this->point_data, CD_PROP_FLOAT, ATTR_RADIUS.c_str()); + this->curve_type = (int8_t *)CustomData_get_layer_named( + &this->point_data, CD_PROP_INT8, ATTR_CURVE_TYPE.c_str()); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Domain Interpolation + * \{ */ + +/** + * Mix together all of a curve's control point values. + * + * \note Theoretically this interpolation does not need to compute all values at once. + * However, doing that makes the implementation simpler, and this can be optimized in the future if + * only some values are required. + */ +template<typename T> +static void adapt_curve_domain_point_to_curve_impl(const CurvesGeometry &curves, + const VArray<T> &old_values, + MutableSpan<T> r_values) +{ + attribute_math::DefaultMixer<T> mixer(r_values); + for (const int i_curve : IndexRange(curves.curves_size())) { + for (const int i_point : curves.range_for_curve(i_curve)) { + mixer.mix_in(i_curve, old_values[i_point]); + } + } + mixer.finalize(); +} + +/** + * A curve is selected if all of its control points were selected. + * + * \note Theoretically this interpolation does not need to compute all values at once. + * However, doing that makes the implementation simpler, and this can be optimized in the future if + * only some values are required. + */ +template<> +void adapt_curve_domain_point_to_curve_impl(const CurvesGeometry &curves, + const VArray<bool> &old_values, + MutableSpan<bool> r_values) +{ + r_values.fill(true); + for (const int i_curve : IndexRange(curves.curves_size())) { + for (const int i_point : curves.range_for_curve(i_curve)) { + if (!old_values[i_point]) { + r_values[i_curve] = false; + break; + } + } + } +} + +static GVArray adapt_curve_domain_point_to_curve(const CurvesGeometry &curves, + const GVArray &varray) +{ + GVArray new_varray; + attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) { + using T = decltype(dummy); + if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) { + Array<T> values(curves.curves_size()); + adapt_curve_domain_point_to_curve_impl<T>(curves, varray.typed<T>(), values); + new_varray = VArray<T>::ForContainer(std::move(values)); + } + }); + return new_varray; +} + +/** + * Copy the value from a curve to all of its points. + * + * \note Theoretically this interpolation does not need to compute all values at once. + * However, doing that makes the implementation simpler, and this can be optimized in the future if + * only some values are required. + */ +template<typename T> +static void adapt_curve_domain_curve_to_point_impl(const CurvesGeometry &curves, + const VArray<T> &old_values, + MutableSpan<T> r_values) +{ + for (const int i_curve : IndexRange(curves.curves_size())) { + r_values.slice(curves.range_for_curve(i_curve)).fill(old_values[i_curve]); + } +} + +static GVArray adapt_curve_domain_curve_to_point(const CurvesGeometry &curves, + const GVArray &varray) +{ + GVArray new_varray; + attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) { + using T = decltype(dummy); + Array<T> values(curves.points_size()); + adapt_curve_domain_curve_to_point_impl<T>(curves, varray.typed<T>(), values); + new_varray = VArray<T>::ForContainer(std::move(values)); + }); + return new_varray; +} + +fn::GVArray CurvesGeometry::adapt_domain(const fn::GVArray &varray, + const AttributeDomain from, + const AttributeDomain to) const +{ + if (!varray) { + return {}; + } + if (varray.is_empty()) { + return {}; + } + if (from == to) { + return varray; + } + + if (from == ATTR_DOMAIN_POINT && to == ATTR_DOMAIN_CURVE) { + return adapt_curve_domain_point_to_curve(*this, varray); + } + if (from == ATTR_DOMAIN_CURVE && to == ATTR_DOMAIN_POINT) { + return adapt_curve_domain_curve_to_point(*this, varray); + } + + BLI_assert_unreachable(); + return {}; +} + +/** \} */ + +} // namespace blender::bke diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 867bdcd06bd..4492f8bbc64 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -1777,7 +1777,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { /* 44: CD_RADIUS */ {sizeof(float), "MFloatProperty", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, /* 45: CD_PROP_INT8 */ - {sizeof(int8_t), "MInt8Property", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, + {sizeof(int8_t), "MInt8Property", 1, N_("Int8"), nullptr, nullptr, nullptr, nullptr, nullptr}, /* 46: CD_HAIRMAPPING */ /* UNUSED */ {-1, "", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, /* 47: CD_PROP_COLOR */ @@ -1959,10 +1959,10 @@ const CustomData_MeshMasks CD_MASK_BMESH = { CD_MASK_GRID_PAINT_MASK | CD_MASK_PROP_ALL), }; const CustomData_MeshMasks CD_MASK_EVERYTHING = { - /* vmask */ (CD_MASK_MVERT | CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_NORMAL | - CD_MASK_MDEFORMVERT | CD_MASK_BWEIGHT | CD_MASK_MVERT_SKIN | CD_MASK_ORCO | - CD_MASK_CLOTH_ORCO | CD_MASK_SHAPEKEY | CD_MASK_SHAPE_KEYINDEX | - CD_MASK_PAINT_MASK | CD_MASK_PROP_ALL | CD_MASK_PROP_COLOR | CD_MASK_CREASE), + /* vmask */ (CD_MASK_MVERT | CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_MDEFORMVERT | + CD_MASK_BWEIGHT | CD_MASK_MVERT_SKIN | CD_MASK_ORCO | CD_MASK_CLOTH_ORCO | + CD_MASK_SHAPEKEY | CD_MASK_SHAPE_KEYINDEX | CD_MASK_PAINT_MASK | + CD_MASK_PROP_ALL | CD_MASK_PROP_COLOR | CD_MASK_CREASE), /* emask */ (CD_MASK_MEDGE | CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_BWEIGHT | CD_MASK_CREASE | CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL), @@ -1971,7 +1971,7 @@ const CustomData_MeshMasks CD_MASK_EVERYTHING = { CD_MASK_ORIGSPACE | CD_MASK_TANGENT | CD_MASK_TESSLOOPNORMAL | CD_MASK_PREVIEW_MCOL | CD_MASK_PROP_ALL), /* pmask */ - (CD_MASK_MPOLY | CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_NORMAL | CD_MASK_FACEMAP | + (CD_MASK_MPOLY | CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_FACEMAP | CD_MASK_FREESTYLE_FACE | CD_MASK_PROP_ALL | CD_MASK_SCULPT_FACE_SETS), /* lmask */ (CD_MASK_MLOOP | CD_MASK_BM_ELEM_PYPTR | CD_MASK_MDISPS | CD_MASK_NORMAL | CD_MASK_MLOOPUV | diff --git a/source/blender/blenkernel/intern/data_transfer.c b/source/blender/blenkernel/intern/data_transfer.c index 6f1026f170f..1818e5a9490 100644 --- a/source/blender/blenkernel/intern/data_transfer.c +++ b/source/blender/blenkernel/intern/data_transfer.c @@ -263,7 +263,6 @@ static void data_transfer_dtdata_type_preprocess(Mesh *me_src, /* This should be ensured by cddata_masks we pass to code generating/giving us me_src now. */ BLI_assert(CustomData_get_layer(&me_src->ldata, CD_NORMAL) != NULL); - BLI_assert(CustomData_get_layer(&me_src->pdata, CD_NORMAL) != NULL); (void)me_src; float(*loop_nors_dst)[3]; @@ -318,15 +317,12 @@ static void data_transfer_dtdata_type_postprocess(Object *UNUSED(ob_src), const int num_polys_dst = me_dst->totpoly; MLoop *loops_dst = me_dst->mloop; const int num_loops_dst = me_dst->totloop; - CustomData *pdata_dst = &me_dst->pdata; CustomData *ldata_dst = &me_dst->ldata; - const float(*poly_nors_dst)[3] = CustomData_get_layer(pdata_dst, CD_NORMAL); + const float(*poly_nors_dst)[3] = BKE_mesh_poly_normals_ensure(me_dst); float(*loop_nors_dst)[3] = CustomData_get_layer(ldata_dst, CD_NORMAL); short(*custom_nors_dst)[2] = CustomData_get_layer(ldata_dst, CD_CUSTOMLOOPNORMAL); - BLI_assert(poly_nors_dst); - if (!custom_nors_dst) { custom_nors_dst = CustomData_add_layer( ldata_dst, CD_CUSTOMLOOPNORMAL, CD_CALLOC, NULL, num_loops_dst); @@ -1379,7 +1375,7 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph, BLI_assert((ob_src != ob_dst) && (ob_src->type == OB_MESH) && (ob_dst->type == OB_MESH)); if (me_dst) { - dirty_nors_dst = (me_dst->runtime.cd_dirty_vert & CD_NORMAL) != 0; + dirty_nors_dst = BKE_mesh_vertex_normals_are_dirty(me_dst); /* Never create needed custom layers on passed destination mesh * (assumed to *not* be ob_dst->data, aka modifier case). */ use_create = false; diff --git a/source/blender/blenkernel/intern/displist.cc b/source/blender/blenkernel/intern/displist.cc index 793af80ec74..f0894ee04e2 100644 --- a/source/blender/blenkernel/intern/displist.cc +++ b/source/blender/blenkernel/intern/displist.cc @@ -590,7 +590,7 @@ static float displist_calc_taper(Depsgraph *depsgraph, Object *taperobj, float fac) { - if (taperobj == nullptr || taperobj->type != OB_CURVE) { + if (taperobj == nullptr || taperobj->type != OB_CURVES_LEGACY) { return 1.0; } @@ -865,7 +865,7 @@ static GeometrySet curve_calc_modifiers_post(Depsgraph *depsgraph, else { std::unique_ptr<CurveEval> curve_eval = curve_eval_from_dna_curve( *cu, ob->runtime.curve_cache->deformed_nurbs); - geometry_set.replace_curve(curve_eval.release()); + geometry_set.replace_curve(curve_eval_to_curves(*curve_eval)); } for (; md; md = md->next) { @@ -1263,7 +1263,7 @@ static GeometrySet evaluate_curve_type_object(Depsgraph *depsgraph, const bool for_render, ListBase *r_dispbase) { - BLI_assert(ELEM(ob->type, OB_CURVE, OB_FONT)); + BLI_assert(ELEM(ob->type, OB_CURVES_LEGACY, OB_FONT)); const Curve *cu = (const Curve *)ob->data; ListBase *deformed_nurbs = &ob->runtime.curve_cache->deformed_nurbs; @@ -1473,7 +1473,7 @@ void BKE_displist_make_curveTypes(Depsgraph *depsgraph, Object *ob, const bool for_render) { - BLI_assert(ELEM(ob->type, OB_SURF, OB_CURVE, OB_FONT)); + BLI_assert(ELEM(ob->type, OB_SURF, OB_CURVES_LEGACY, OB_FONT)); Curve &cow_curve = *(Curve *)ob->data; BKE_object_free_derived_caches(ob); @@ -1490,14 +1490,14 @@ void BKE_displist_make_curveTypes(Depsgraph *depsgraph, else { GeometrySet geometry = evaluate_curve_type_object(depsgraph, scene, ob, for_render, dispbase); - if (geometry.has_curve()) { + if (geometry.has_curves()) { /* Assign the evaluated curve to the object's "data_eval". In addition to the curve_eval * added to the curve here, it will also contain a copy of the original curve's data. This is * essential, because it maintains the expected behavior for evaluated curve data from before * the CurveEval data type was introduced, when an evaluated object's curve data was just a * copy of the original curve and everything else ended up in #CurveCache. */ CurveComponent &curve_component = geometry.get_component_for_write<CurveComponent>(); - cow_curve.curve_eval = curve_component.get_for_write(); + cow_curve.curve_eval = curves_to_curve_eval(*curve_component.get_for_read()).release(); BKE_object_eval_assign_data(ob, &cow_curve.id, false); } diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c index 2f760597e1a..f2915a97746 100644 --- a/source/blender/blenkernel/intern/effect.c +++ b/source/blender/blenkernel/intern/effect.c @@ -143,7 +143,7 @@ static void precalculate_effector(struct Depsgraph *depsgraph, EffectorCache *ef BLI_rng_srandom(eff->pd->rng, eff->pd->seed + cfra); } - if (eff->pd->forcefield == PFIELD_GUIDE && eff->ob->type == OB_CURVE) { + if (eff->pd->forcefield == PFIELD_GUIDE && eff->ob->type == OB_CURVES_LEGACY) { Curve *cu = eff->ob->data; if (cu->flag & CU_PATH) { if (eff->ob->runtime.curve_cache == NULL || @@ -161,7 +161,7 @@ static void precalculate_effector(struct Depsgraph *depsgraph, EffectorCache *ef } else if (eff->pd->shape == PFIELD_SHAPE_SURFACE) { eff->surmd = (SurfaceModifierData *)BKE_modifiers_findby_type(eff->ob, eModifierType_Surface); - if (eff->ob->type == OB_CURVE) { + if (eff->ob->type == OB_CURVES_LEGACY) { eff->flag |= PE_USE_NORMAL_DATA; } } diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index ef2fa2266c4..6f2760e91a6 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -3292,7 +3292,7 @@ static Mesh *create_liquid_geometry(FluidDomainSettings *fds, mpolys = me->mpoly; mloops = me->mloop; - /* Get size (dimension) but considering scaling scaling. */ + /* Get size (dimension) but considering scaling. */ copy_v3_v3(cell_size_scaled, fds->cell_size); mul_v3_v3(cell_size_scaled, ob->scale); madd_v3fl_v3fl_v3fl_v3i(min, fds->p0, cell_size_scaled, fds->res_min); diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index 5921f853389..0926d65b306 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -23,18 +23,18 @@ using blender::fn::GVArray_GSpan; /** \name Geometry Component Implementation * \{ */ -CurveComponent::CurveComponent() : GeometryComponent(GEO_COMPONENT_TYPE_CURVE) +CurveComponentLegacy::CurveComponentLegacy() : GeometryComponent(GEO_COMPONENT_TYPE_CURVE) { } -CurveComponent::~CurveComponent() +CurveComponentLegacy::~CurveComponentLegacy() { this->clear(); } -GeometryComponent *CurveComponent::copy() const +GeometryComponent *CurveComponentLegacy::copy() const { - CurveComponent *new_component = new CurveComponent(); + CurveComponentLegacy *new_component = new CurveComponentLegacy(); if (curve_ != nullptr) { new_component->curve_ = new CurveEval(*curve_); new_component->ownership_ = GeometryOwnershipType::Owned; @@ -42,30 +42,23 @@ GeometryComponent *CurveComponent::copy() const return new_component; } -void CurveComponent::clear() +void CurveComponentLegacy::clear() { BLI_assert(this->is_mutable()); if (curve_ != nullptr) { if (ownership_ == GeometryOwnershipType::Owned) { delete curve_; } - if (curve_for_render_ != nullptr) { - /* The curve created by this component should not have any edit mode data. */ - BLI_assert(curve_for_render_->editfont == nullptr && curve_for_render_->editnurb == nullptr); - BKE_id_free(nullptr, curve_for_render_); - curve_for_render_ = nullptr; - } - curve_ = nullptr; } } -bool CurveComponent::has_curve() const +bool CurveComponentLegacy::has_curve() const { return curve_ != nullptr; } -void CurveComponent::replace(CurveEval *curve, GeometryOwnershipType ownership) +void CurveComponentLegacy::replace(CurveEval *curve, GeometryOwnershipType ownership) { BLI_assert(this->is_mutable()); this->clear(); @@ -73,7 +66,7 @@ void CurveComponent::replace(CurveEval *curve, GeometryOwnershipType ownership) ownership_ = ownership; } -CurveEval *CurveComponent::release() +CurveEval *CurveComponentLegacy::release() { BLI_assert(this->is_mutable()); CurveEval *curve = curve_; @@ -81,12 +74,12 @@ CurveEval *CurveComponent::release() return curve; } -const CurveEval *CurveComponent::get_for_read() const +const CurveEval *CurveComponentLegacy::get_for_read() const { return curve_; } -CurveEval *CurveComponent::get_for_write() +CurveEval *CurveComponentLegacy::get_for_write() { BLI_assert(this->is_mutable()); if (ownership_ == GeometryOwnershipType::ReadOnly) { @@ -96,17 +89,17 @@ CurveEval *CurveComponent::get_for_write() return curve_; } -bool CurveComponent::is_empty() const +bool CurveComponentLegacy::is_empty() const { return curve_ == nullptr; } -bool CurveComponent::owns_direct_data() const +bool CurveComponentLegacy::owns_direct_data() const { return ownership_ == GeometryOwnershipType::Owned; } -void CurveComponent::ensure_owns_direct_data() +void CurveComponentLegacy::ensure_owns_direct_data() { BLI_assert(this->is_mutable()); if (ownership_ != GeometryOwnershipType::Owned) { @@ -115,32 +108,13 @@ void CurveComponent::ensure_owns_direct_data() } } -const Curve *CurveComponent::get_curve_for_render() const -{ - if (curve_ == nullptr) { - return nullptr; - } - if (curve_for_render_ != nullptr) { - return curve_for_render_; - } - std::lock_guard lock{curve_for_render_mutex_}; - if (curve_for_render_ != nullptr) { - return curve_for_render_; - } - - curve_for_render_ = (Curve *)BKE_id_new_nomain(ID_CU, nullptr); - curve_for_render_->curve_eval = curve_; - - return curve_for_render_; -} - /** \} */ /* -------------------------------------------------------------------- */ /** \name Attribute Access Helper Functions * \{ */ -int CurveComponent::attribute_domain_size(const AttributeDomain domain) const +int CurveComponentLegacy::attribute_domain_size(const AttributeDomain domain) const { if (curve_ == nullptr) { return 0; @@ -334,9 +308,10 @@ static GVArray adapt_curve_domain_spline_to_point(const CurveEval &curve, GVArra } // namespace blender::bke -GVArray CurveComponent::attribute_try_adapt_domain_impl(const GVArray &varray, - const AttributeDomain from_domain, - const AttributeDomain to_domain) const +GVArray CurveComponentLegacy::attribute_try_adapt_domain_impl( + const GVArray &varray, + const AttributeDomain from_domain, + const AttributeDomain to_domain) const { if (!varray) { return {}; @@ -361,14 +336,15 @@ GVArray CurveComponent::attribute_try_adapt_domain_impl(const GVArray &varray, static CurveEval *get_curve_from_component_for_write(GeometryComponent &component) { BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE); - CurveComponent &curve_component = static_cast<CurveComponent &>(component); + CurveComponentLegacy &curve_component = static_cast<CurveComponentLegacy &>(component); return curve_component.get_for_write(); } static const CurveEval *get_curve_from_component_for_read(const GeometryComponent &component) { BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE); - const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); + const CurveComponentLegacy &curve_component = static_cast<const CurveComponentLegacy &>( + component); return curve_component.get_for_read(); } @@ -377,98 +353,6 @@ static const CurveEval *get_curve_from_component_for_read(const GeometryComponen namespace blender::bke { /* -------------------------------------------------------------------- */ -/** \name Curve Normals Access - * \{ */ - -static void calculate_bezier_normals(const BezierSpline &spline, MutableSpan<float3> normals) -{ - Span<int> offsets = spline.control_point_offsets(); - Span<float3> evaluated_normals = spline.evaluated_normals(); - for (const int i : IndexRange(spline.size())) { - normals[i] = evaluated_normals[offsets[i]]; - } -} - -static void calculate_poly_normals(const PolySpline &spline, MutableSpan<float3> normals) -{ - normals.copy_from(spline.evaluated_normals()); -} - -/** - * Because NURBS control points are not necessarily on the path, the normal at the control points - * is not well defined, so create a temporary poly spline to find the normals. This requires extra - * copying currently, but may be more efficient in the future if attributes have some form of CoW. - */ -static void calculate_nurbs_normals(const NURBSpline &spline, MutableSpan<float3> normals) -{ - PolySpline poly_spline; - poly_spline.resize(spline.size()); - poly_spline.positions().copy_from(spline.positions()); - poly_spline.tilts().copy_from(spline.tilts()); - normals.copy_from(poly_spline.evaluated_normals()); -} - -static Array<float3> curve_normal_point_domain(const CurveEval &curve) -{ - Span<SplinePtr> splines = curve.splines(); - Array<int> offsets = curve.control_point_offsets(); - const int total_size = offsets.last(); - Array<float3> normals(total_size); - - threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { - for (const int i : range) { - const Spline &spline = *splines[i]; - MutableSpan spline_normals{normals.as_mutable_span().slice(offsets[i], spline.size())}; - switch (splines[i]->type()) { - case Spline::Type::Bezier: - calculate_bezier_normals(static_cast<const BezierSpline &>(spline), spline_normals); - break; - case Spline::Type::Poly: - calculate_poly_normals(static_cast<const PolySpline &>(spline), spline_normals); - break; - case Spline::Type::NURBS: - calculate_nurbs_normals(static_cast<const NURBSpline &>(spline), spline_normals); - break; - } - } - }); - return normals; -} - -VArray<float3> curve_normals_varray(const CurveComponent &component, const AttributeDomain domain) -{ - const CurveEval *curve = component.get_for_read(); - if (curve == nullptr) { - return nullptr; - } - - if (domain == ATTR_DOMAIN_POINT) { - const Span<SplinePtr> splines = curve->splines(); - - /* Use a reference to evaluated normals if possible to avoid an allocation and a copy. - * This is only possible when there is only one poly spline. */ - if (splines.size() == 1 && splines.first()->type() == Spline::Type::Poly) { - const PolySpline &spline = static_cast<PolySpline &>(*splines.first()); - return VArray<float3>::ForSpan(spline.evaluated_normals()); - } - - Array<float3> normals = curve_normal_point_domain(*curve); - return VArray<float3>::ForContainer(std::move(normals)); - } - - if (domain == ATTR_DOMAIN_CURVE) { - Array<float3> point_normals = curve_normal_point_domain(*curve); - VArray<float3> varray = VArray<float3>::ForContainer(std::move(point_normals)); - return component.attribute_try_adapt_domain<float3>( - std::move(varray), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE); - } - - return nullptr; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Builtin Spline Attributes * * Attributes with a value for every spline, stored contiguously or in every spline separately. @@ -877,15 +761,7 @@ class VArrayImpl_For_SplinePosition final : public VMutableArrayImpl<float3> { { const PointIndices indices = lookup_point_indices(offsets_, index); Spline &spline = *splines_[indices.spline_index]; - if (BezierSpline *bezier_spline = dynamic_cast<BezierSpline *>(&spline)) { - const float3 delta = value - bezier_spline->positions()[indices.point_index]; - bezier_spline->handle_positions_left()[indices.point_index] += delta; - bezier_spline->handle_positions_right()[indices.point_index] += delta; - bezier_spline->positions()[indices.point_index] = value; - } - else { - spline.positions()[indices.point_index] = value; - } + spline.positions()[indices.point_index] = value; } void set_all(Span<float3> src) final @@ -894,20 +770,7 @@ class VArrayImpl_For_SplinePosition final : public VMutableArrayImpl<float3> { Spline &spline = *splines_[spline_index]; const int offset = offsets_[spline_index]; const int next_offset = offsets_[spline_index + 1]; - if (BezierSpline *bezier_spline = dynamic_cast<BezierSpline *>(&spline)) { - MutableSpan<float3> positions = bezier_spline->positions(); - MutableSpan<float3> handle_positions_left = bezier_spline->handle_positions_left(); - MutableSpan<float3> handle_positions_right = bezier_spline->handle_positions_right(); - for (const int i : IndexRange(next_offset - offset)) { - const float3 delta = src[offset + i] - positions[i]; - handle_positions_left[i] += delta; - handle_positions_right[i] += delta; - positions[i] = src[offset + i]; - } - } - else { - spline.positions().copy_from(src.slice(offset, next_offset - offset)); - } + spline.positions().copy_from(src.slice(offset, next_offset - offset)); } } @@ -955,7 +818,7 @@ class VArrayImpl_For_BezierHandles final : public VMutableArrayImpl<float3> { { const PointIndices indices = lookup_point_indices(offsets_, index); const Spline &spline = *splines_[indices.spline_index]; - if (spline.type() == Spline::Type::Bezier) { + if (spline.type() == CURVE_TYPE_BEZIER) { const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(spline); return is_right_ ? bezier_spline.handle_positions_right()[indices.point_index] : bezier_spline.handle_positions_left()[indices.point_index]; @@ -967,13 +830,13 @@ class VArrayImpl_For_BezierHandles final : public VMutableArrayImpl<float3> { { const PointIndices indices = lookup_point_indices(offsets_, index); Spline &spline = *splines_[indices.spline_index]; - if (spline.type() == Spline::Type::Bezier) { + if (spline.type() == CURVE_TYPE_BEZIER) { BezierSpline &bezier_spline = static_cast<BezierSpline &>(spline); if (is_right_) { - bezier_spline.set_handle_position_right(indices.point_index, value); + bezier_spline.handle_positions_right()[indices.point_index] = value; } else { - bezier_spline.set_handle_position_left(indices.point_index, value); + bezier_spline.handle_positions_left()[indices.point_index] = value; } bezier_spline.mark_cache_invalid(); } @@ -983,18 +846,18 @@ class VArrayImpl_For_BezierHandles final : public VMutableArrayImpl<float3> { { for (const int spline_index : splines_.index_range()) { Spline &spline = *splines_[spline_index]; - if (spline.type() == Spline::Type::Bezier) { + if (spline.type() == CURVE_TYPE_BEZIER) { const int offset = offsets_[spline_index]; BezierSpline &bezier_spline = static_cast<BezierSpline &>(spline); if (is_right_) { for (const int i : IndexRange(bezier_spline.size())) { - bezier_spline.set_handle_position_right(i, src[offset + i]); + bezier_spline.handle_positions_right()[i] = src[offset + i]; } } else { for (const int i : IndexRange(bezier_spline.size())) { - bezier_spline.set_handle_position_left(i, src[offset + i]); + bezier_spline.handle_positions_left()[i] = src[offset + i]; } } bezier_spline.mark_cache_invalid(); @@ -1024,7 +887,7 @@ class VArrayImpl_For_BezierHandles final : public VMutableArrayImpl<float3> { { Array<Span<float3>> spans(splines.size()); for (const int i : spans.index_range()) { - if (splines[i]->type() == Spline::Type::Bezier) { + if (splines[i]->type() == CURVE_TYPE_BEZIER) { BezierSpline &bezier_spline = static_cast<BezierSpline &>(*splines[i]); spans[i] = is_right ? bezier_spline.handle_positions_right() : bezier_spline.handle_positions_left(); @@ -1214,7 +1077,7 @@ class PositionAttributeProvider final : public BuiltinPointAttributeProvider<flo /* Use the regular position virtual array when there aren't any Bezier splines * to avoid the overhead of checking the spline type for every point. */ - if (!curve->has_spline_with_type(Spline::Type::Bezier)) { + if (!curve->has_spline_with_type(CURVE_TYPE_BEZIER)) { return BuiltinPointAttributeProvider<float3>::try_get_for_write(component); } @@ -1255,7 +1118,7 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider { return {}; } - if (!curve->has_spline_with_type(Spline::Type::Bezier)) { + if (!curve->has_spline_with_type(CURVE_TYPE_BEZIER)) { return {}; } @@ -1273,7 +1136,7 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider { return {}; } - if (!curve->has_spline_with_type(Spline::Type::Bezier)) { + if (!curve->has_spline_with_type(CURVE_TYPE_BEZIER)) { return {}; } @@ -1304,7 +1167,7 @@ class BezierHandleAttributeProvider : public BuiltinAttributeProvider { return false; } - return curve->has_spline_with_type(Spline::Type::Bezier) && + return curve->has_spline_with_type(CURVE_TYPE_BEZIER) && component.attribute_domain_size(ATTR_DOMAIN_POINT) != 0; } }; @@ -1324,7 +1187,8 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { private: static constexpr uint64_t supported_types_mask = CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 | CD_MASK_PROP_FLOAT3 | CD_MASK_PROP_INT32 | - CD_MASK_PROP_COLOR | CD_MASK_PROP_BOOL; + CD_MASK_PROP_COLOR | CD_MASK_PROP_BOOL | + CD_MASK_PROP_INT8; public: ReadAttributeLookup try_get_for_read(const GeometryComponent &component, @@ -1569,7 +1433,8 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() } // namespace blender::bke -const blender::bke::ComponentAttributeProviders *CurveComponent::get_attribute_providers() const +const blender::bke::ComponentAttributeProviders *CurveComponentLegacy::get_attribute_providers() + const { static blender::bke::ComponentAttributeProviders providers = blender::bke::create_attribute_providers_for_curve(); diff --git a/source/blender/blenkernel/intern/geometry_component_curves.cc b/source/blender/blenkernel/intern/geometry_component_curves.cc new file mode 100644 index 00000000000..5723d110aa0 --- /dev/null +++ b/source/blender/blenkernel/intern/geometry_component_curves.cc @@ -0,0 +1,521 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_task.hh" + +#include "DNA_ID_enums.h" +#include "DNA_curve_types.h" + +#include "BKE_attribute_access.hh" +#include "BKE_attribute_math.hh" +#include "BKE_curve.h" +#include "BKE_curves.hh" +#include "BKE_geometry_set.hh" +#include "BKE_lib_id.h" +#include "BKE_spline.hh" + +#include "attribute_access_intern.hh" + +using blender::fn::GVArray; + +/* -------------------------------------------------------------------- */ +/** \name Geometry Component Implementation + * \{ */ + +CurveComponent::CurveComponent() : GeometryComponent(GEO_COMPONENT_TYPE_CURVE) +{ +} + +CurveComponent::~CurveComponent() +{ + this->clear(); +} + +GeometryComponent *CurveComponent::copy() const +{ + CurveComponent *new_component = new CurveComponent(); + if (curves_ != nullptr) { + new_component->curves_ = BKE_curves_copy_for_eval(curves_, false); + new_component->ownership_ = GeometryOwnershipType::Owned; + } + return new_component; +} + +void CurveComponent::clear() +{ + BLI_assert(this->is_mutable()); + if (curves_ != nullptr) { + if (ownership_ == GeometryOwnershipType::Owned) { + BKE_id_free(nullptr, curves_); + } + if (curve_for_render_ != nullptr) { + /* The curve created by this component should not have any edit mode data. */ + BLI_assert(curve_for_render_->editfont == nullptr && curve_for_render_->editnurb == nullptr); + BKE_id_free(nullptr, curve_for_render_); + curve_for_render_ = nullptr; + } + + curves_ = nullptr; + } +} + +bool CurveComponent::has_curves() const +{ + return curves_ != nullptr; +} + +void CurveComponent::replace(Curves *curves, GeometryOwnershipType ownership) +{ + BLI_assert(this->is_mutable()); + this->clear(); + curves_ = curves; + ownership_ = ownership; +} + +Curves *CurveComponent::release() +{ + BLI_assert(this->is_mutable()); + Curves *curves = curves_; + curves_ = nullptr; + return curves; +} + +const Curves *CurveComponent::get_for_read() const +{ + return curves_; +} + +Curves *CurveComponent::get_for_write() +{ + BLI_assert(this->is_mutable()); + if (ownership_ == GeometryOwnershipType::ReadOnly) { + curves_ = BKE_curves_copy_for_eval(curves_, false); + ownership_ = GeometryOwnershipType::Owned; + } + return curves_; +} + +bool CurveComponent::is_empty() const +{ + return curves_ == nullptr; +} + +bool CurveComponent::owns_direct_data() const +{ + return ownership_ == GeometryOwnershipType::Owned; +} + +void CurveComponent::ensure_owns_direct_data() +{ + BLI_assert(this->is_mutable()); + if (ownership_ != GeometryOwnershipType::Owned) { + curves_ = BKE_curves_copy_for_eval(curves_, false); + ownership_ = GeometryOwnershipType::Owned; + } +} + +const Curve *CurveComponent::get_curve_for_render() const +{ + if (curves_ == nullptr) { + return nullptr; + } + if (curve_for_render_ != nullptr) { + return curve_for_render_; + } + std::lock_guard lock{curve_for_render_mutex_}; + if (curve_for_render_ != nullptr) { + return curve_for_render_; + } + + curve_for_render_ = (Curve *)BKE_id_new_nomain(ID_CU_LEGACY, nullptr); + curve_for_render_->curve_eval = curves_to_curve_eval(*curves_).release(); + + return curve_for_render_; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Curve Normals Access + * \{ */ + +namespace blender::bke { + +static void calculate_bezier_normals(const BezierSpline &spline, MutableSpan<float3> normals) +{ + Span<int> offsets = spline.control_point_offsets(); + Span<float3> evaluated_normals = spline.evaluated_normals(); + for (const int i : IndexRange(spline.size())) { + normals[i] = evaluated_normals[offsets[i]]; + } +} + +static void calculate_poly_normals(const PolySpline &spline, MutableSpan<float3> normals) +{ + normals.copy_from(spline.evaluated_normals()); +} + +/** + * Because NURBS control points are not necessarily on the path, the normal at the control points + * is not well defined, so create a temporary poly spline to find the normals. This requires extra + * copying currently, but may be more efficient in the future if attributes have some form of CoW. + */ +static void calculate_nurbs_normals(const NURBSpline &spline, MutableSpan<float3> normals) +{ + PolySpline poly_spline; + poly_spline.resize(spline.size()); + poly_spline.positions().copy_from(spline.positions()); + poly_spline.tilts().copy_from(spline.tilts()); + normals.copy_from(poly_spline.evaluated_normals()); +} + +static Array<float3> curve_normal_point_domain(const CurveEval &curve) +{ + Span<SplinePtr> splines = curve.splines(); + Array<int> offsets = curve.control_point_offsets(); + const int total_size = offsets.last(); + Array<float3> normals(total_size); + + threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { + for (const int i : range) { + const Spline &spline = *splines[i]; + MutableSpan spline_normals{normals.as_mutable_span().slice(offsets[i], spline.size())}; + switch (splines[i]->type()) { + case CURVE_TYPE_BEZIER: + calculate_bezier_normals(static_cast<const BezierSpline &>(spline), spline_normals); + break; + case CURVE_TYPE_POLY: + calculate_poly_normals(static_cast<const PolySpline &>(spline), spline_normals); + break; + case CURVE_TYPE_NURBS: + calculate_nurbs_normals(static_cast<const NURBSpline &>(spline), spline_normals); + break; + case CURVE_TYPE_CATMULL_ROM: + BLI_assert_unreachable(); + break; + } + } + }); + return normals; +} + +VArray<float3> curve_normals_varray(const CurveComponent &component, const AttributeDomain domain) +{ + if (component.is_empty()) { + return nullptr; + } + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_read()); + + if (domain == ATTR_DOMAIN_POINT) { + Array<float3> normals = curve_normal_point_domain(*curve); + return VArray<float3>::ForContainer(std::move(normals)); + } + + if (domain == ATTR_DOMAIN_CURVE) { + Array<float3> point_normals = curve_normal_point_domain(*curve); + VArray<float3> varray = VArray<float3>::ForContainer(std::move(point_normals)); + return component.attribute_try_adapt_domain<float3>( + std::move(varray), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE); + } + + return nullptr; +} + +} // namespace blender::bke + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Attribute Access Helper Functions + * \{ */ + +int CurveComponent::attribute_domain_size(const AttributeDomain domain) const +{ + if (curves_ == nullptr) { + return 0; + } + const blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap( + curves_->geometry); + if (domain == ATTR_DOMAIN_POINT) { + return geometry.points_size(); + } + if (domain == ATTR_DOMAIN_CURVE) { + return geometry.curves_size(); + } + return 0; +} + +GVArray CurveComponent::attribute_try_adapt_domain_impl(const GVArray &varray, + const AttributeDomain from_domain, + const AttributeDomain to_domain) const +{ + return blender::bke::CurvesGeometry::wrap(curves_->geometry) + .adapt_domain(varray, from_domain, to_domain); +} + +static Curves *get_curves_from_component_for_write(GeometryComponent &component) +{ + BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE); + CurveComponent &curve_component = static_cast<CurveComponent &>(component); + return curve_component.get_for_write(); +} + +static const Curves *get_curves_from_component_for_read(const GeometryComponent &component) +{ + BLI_assert(component.type() == GEO_COMPONENT_TYPE_CURVE); + const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); + return curve_component.get_for_read(); +} + +static void tag_component_topology_changed(GeometryComponent &component) +{ + Curves *curves = get_curves_from_component_for_write(component); + if (curves) { + blender::bke::CurvesGeometry::wrap(curves->geometry).tag_topology_changed(); + } +} + +static void tag_component_positions_changed(GeometryComponent &component) +{ + Curves *curves = get_curves_from_component_for_write(component); + if (curves) { + blender::bke::CurvesGeometry::wrap(curves->geometry).tag_positions_changed(); + } +} + +static void tag_component_normals_changed(GeometryComponent &component) +{ + Curves *curves = get_curves_from_component_for_write(component); + if (curves) { + blender::bke::CurvesGeometry::wrap(curves->geometry).tag_normals_changed(); + } +} + +/** \} */ + +namespace blender::bke { + +/* -------------------------------------------------------------------- */ +/** \name Attribute Provider Declaration + * \{ */ + +/** + * In this function all the attribute providers for a curves component are created. + * Most data in this function is statically allocated, because it does not change over time. + */ +static ComponentAttributeProviders create_attribute_providers_for_curve() +{ + static CustomDataAccessInfo curve_access = { + [](GeometryComponent &component) -> CustomData * { + Curves *curves = get_curves_from_component_for_write(component); + return curves ? &curves->geometry.curve_data : nullptr; + }, + [](const GeometryComponent &component) -> const CustomData * { + const Curves *curves = get_curves_from_component_for_read(component); + return curves ? &curves->geometry.curve_data : nullptr; + }, + [](GeometryComponent &component) { + Curves *curves = get_curves_from_component_for_write(component); + if (curves) { + blender::bke::CurvesGeometry::wrap(curves->geometry).update_customdata_pointers(); + } + }}; + static CustomDataAccessInfo point_access = { + [](GeometryComponent &component) -> CustomData * { + Curves *curves = get_curves_from_component_for_write(component); + return curves ? &curves->geometry.point_data : nullptr; + }, + [](const GeometryComponent &component) -> const CustomData * { + const Curves *curves = get_curves_from_component_for_read(component); + return curves ? &curves->geometry.point_data : nullptr; + }, + [](GeometryComponent &component) { + Curves *curves = get_curves_from_component_for_write(component); + if (curves) { + blender::bke::CurvesGeometry::wrap(curves->geometry).update_customdata_pointers(); + } + }}; + + static BuiltinCustomDataLayerProvider position("position", + ATTR_DOMAIN_POINT, + CD_PROP_FLOAT3, + CD_PROP_FLOAT3, + BuiltinAttributeProvider::NonCreatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::NonDeletable, + point_access, + make_array_read_attribute<float3>, + make_array_write_attribute<float3>, + tag_component_positions_changed); + + static BuiltinCustomDataLayerProvider radius("radius", + ATTR_DOMAIN_POINT, + CD_PROP_FLOAT, + CD_PROP_FLOAT, + BuiltinAttributeProvider::Creatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::Deletable, + point_access, + make_array_read_attribute<float>, + make_array_write_attribute<float>, + nullptr); + + static BuiltinCustomDataLayerProvider id("id", + ATTR_DOMAIN_POINT, + CD_PROP_INT32, + CD_PROP_INT32, + BuiltinAttributeProvider::Creatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::Deletable, + point_access, + make_array_read_attribute<int>, + make_array_write_attribute<int>, + nullptr); + + static BuiltinCustomDataLayerProvider tilt("tilt", + ATTR_DOMAIN_POINT, + CD_PROP_FLOAT, + CD_PROP_FLOAT, + BuiltinAttributeProvider::Creatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::Deletable, + point_access, + make_array_read_attribute<float>, + make_array_write_attribute<float>, + tag_component_normals_changed); + + static BuiltinCustomDataLayerProvider handle_right("handle_right", + ATTR_DOMAIN_POINT, + CD_PROP_FLOAT3, + CD_PROP_FLOAT3, + BuiltinAttributeProvider::Creatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::Deletable, + point_access, + make_array_read_attribute<float3>, + make_array_write_attribute<float3>, + tag_component_positions_changed); + + static BuiltinCustomDataLayerProvider handle_left("handle_left", + ATTR_DOMAIN_POINT, + CD_PROP_FLOAT3, + CD_PROP_FLOAT3, + BuiltinAttributeProvider::Creatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::Deletable, + point_access, + make_array_read_attribute<float3>, + make_array_write_attribute<float3>, + tag_component_positions_changed); + + static BuiltinCustomDataLayerProvider handle_type_right("handle_type_right", + ATTR_DOMAIN_POINT, + CD_PROP_INT8, + CD_PROP_INT8, + BuiltinAttributeProvider::Creatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::Deletable, + point_access, + make_array_read_attribute<int8_t>, + make_array_write_attribute<int8_t>, + tag_component_topology_changed); + + static BuiltinCustomDataLayerProvider handle_type_left("handle_type_left", + ATTR_DOMAIN_POINT, + CD_PROP_INT8, + CD_PROP_INT8, + BuiltinAttributeProvider::Creatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::Deletable, + point_access, + make_array_read_attribute<int8_t>, + make_array_write_attribute<int8_t>, + tag_component_topology_changed); + + static BuiltinCustomDataLayerProvider nurbs_weight("nurbs_weight", + ATTR_DOMAIN_POINT, + CD_PROP_FLOAT, + CD_PROP_FLOAT, + BuiltinAttributeProvider::Creatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::Deletable, + point_access, + make_array_read_attribute<float>, + make_array_write_attribute<float>, + tag_component_positions_changed); + + static BuiltinCustomDataLayerProvider nurbs_order("nurbs_order", + ATTR_DOMAIN_CURVE, + CD_PROP_INT32, + CD_PROP_INT32, + BuiltinAttributeProvider::Creatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::Deletable, + curve_access, + make_array_read_attribute<int>, + make_array_write_attribute<int>, + tag_component_topology_changed); + + static BuiltinCustomDataLayerProvider nurbs_knots_mode("knots_mode", + ATTR_DOMAIN_CURVE, + CD_PROP_INT8, + CD_PROP_INT8, + BuiltinAttributeProvider::Creatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::Deletable, + curve_access, + make_array_read_attribute<int8_t>, + make_array_write_attribute<int8_t>, + tag_component_topology_changed); + + static BuiltinCustomDataLayerProvider resolution("resolution", + ATTR_DOMAIN_CURVE, + CD_PROP_INT32, + CD_PROP_INT32, + BuiltinAttributeProvider::Creatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::Deletable, + curve_access, + make_array_read_attribute<int>, + make_array_write_attribute<int>, + tag_component_positions_changed); + + static BuiltinCustomDataLayerProvider cyclic("cyclic", + ATTR_DOMAIN_CURVE, + CD_PROP_BOOL, + CD_PROP_BOOL, + BuiltinAttributeProvider::Creatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::Deletable, + curve_access, + make_array_read_attribute<bool>, + make_array_write_attribute<bool>, + tag_component_topology_changed); + + static CustomDataAttributeProvider curve_custom_data(ATTR_DOMAIN_CURVE, curve_access); + static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access); + + return ComponentAttributeProviders({&position, + &radius, + &id, + &tilt, + &handle_right, + &handle_left, + &handle_type_right, + &handle_type_left, + &nurbs_order, + &nurbs_weight, + &resolution, + &cyclic}, + {&curve_custom_data, &point_custom_data}); +} + +/** \} */ + +} // namespace blender::bke + +const blender::bke::ComponentAttributeProviders *CurveComponent::get_attribute_providers() const +{ + static blender::bke::ComponentAttributeProviders providers = + blender::bke::create_attribute_providers_for_curve(); + return &providers; +} diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc index b83a8b1ee94..0cb2b0e812b 100644 --- a/source/blender/blenkernel/intern/geometry_component_instances.cc +++ b/source/blender/blenkernel/intern/geometry_component_instances.cc @@ -439,18 +439,6 @@ class InstancePositionAttributeProvider final : public BuiltinAttributeProvider } }; -template<typename T> -static GVArray make_array_read_attribute(const void *data, const int domain_size) -{ - return VArray<T>::ForSpan(Span<T>((const T *)data, domain_size)); -} - -template<typename T> -static GVMutableArray make_array_write_attribute(void *data, const int domain_size) -{ - return VMutableArray<T>::ForSpan(MutableSpan<T>((T *)data, domain_size)); -} - static ComponentAttributeProviders create_attribute_providers_for_instances() { static InstancePositionAttributeProvider position; diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index 2f8ff944420..104166df913 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -854,18 +854,6 @@ static GVMutableArray make_derived_write_attribute(void *data, const int domain_ MutableSpan<StructT>((StructT *)data, domain_size)); } -template<typename T> -static GVArray make_array_read_attribute(const void *data, const int domain_size) -{ - return VArray<T>::ForSpan(Span<T>((const T *)data, domain_size)); -} - -template<typename T> -static GVMutableArray make_array_write_attribute(void *data, const int domain_size) -{ - return VMutableArray<T>::ForSpan(MutableSpan<T>((T *)data, domain_size)); -} - static float3 get_vertex_position(const MVert &vert) { return float3(vert.co); diff --git a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc index f6f3c4e1b4e..3db4db307a3 100644 --- a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc +++ b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc @@ -117,18 +117,6 @@ int PointCloudComponent::attribute_domain_size(const AttributeDomain domain) con namespace blender::bke { -template<typename T> -static GVArray make_array_read_attribute(const void *data, const int domain_size) -{ - return VArray<T>::ForSpan(Span<T>((const T *)data, domain_size)); -} - -template<typename T> -static GVMutableArray make_array_write_attribute(void *data, const int domain_size) -{ - return VMutableArray<T>::ForSpan(MutableSpan<T>((T *)data, domain_size)); -} - /** * In this function all the attribute providers for a point cloud component are created. Most data * in this function is statically allocated, because it does not change over time. diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index 9c7cfa04e0b..ca372ba8f38 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -7,6 +7,7 @@ #include "BKE_attribute.h" #include "BKE_attribute_access.hh" +#include "BKE_curves.hh" #include "BKE_geometry_set.hh" #include "BKE_lib_id.h" #include "BKE_mesh.h" @@ -186,8 +187,9 @@ bool GeometrySet::compute_boundbox_without_instances(float3 *r_min, float3 *r_ma if (volume != nullptr) { have_minmax |= BKE_volume_min_max(volume, *r_min, *r_max); } - const CurveEval *curve = this->get_curve_for_read(); - if (curve != nullptr) { + const Curves *curves = this->get_curves_for_read(); + if (curves != nullptr) { + std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curves); /* Using the evaluated positions is somewhat arbitrary, but it is probably expected. */ have_minmax |= curve->bounds_min_max(*r_min, *r_max, true); } @@ -258,7 +260,7 @@ const Volume *GeometrySet::get_volume_for_read() const return (component == nullptr) ? nullptr : component->get_for_read(); } -const CurveEval *GeometrySet::get_curve_for_read() const +const Curves *GeometrySet::get_curves_for_read() const { const CurveComponent *component = this->get_component_for_read<CurveComponent>(); return (component == nullptr) ? nullptr : component->get_for_read(); @@ -282,10 +284,10 @@ bool GeometrySet::has_volume() const return component != nullptr && component->has_volume(); } -bool GeometrySet::has_curve() const +bool GeometrySet::has_curves() const { const CurveComponent *component = this->get_component_for_read<CurveComponent>(); - return component != nullptr && component->has_curve(); + return component != nullptr && component->has_curves(); } bool GeometrySet::has_realized_data() const @@ -302,8 +304,8 @@ bool GeometrySet::has_realized_data() const bool GeometrySet::is_empty() const { - return !(this->has_mesh() || this->has_curve() || this->has_pointcloud() || this->has_volume() || - this->has_instances()); + return !(this->has_mesh() || this->has_curves() || this->has_pointcloud() || + this->has_volume() || this->has_instances()); } GeometrySet GeometrySet::create_with_mesh(Mesh *mesh, GeometryOwnershipType ownership) @@ -327,12 +329,12 @@ GeometrySet GeometrySet::create_with_pointcloud(PointCloud *pointcloud, return geometry_set; } -GeometrySet GeometrySet::create_with_curve(CurveEval *curve, GeometryOwnershipType ownership) +GeometrySet GeometrySet::create_with_curves(Curves *curves, GeometryOwnershipType ownership) { GeometrySet geometry_set; - if (curve != nullptr) { + if (curves != nullptr) { CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>(); - component.replace(curve, ownership); + component.replace(curves, ownership); } return geometry_set; } @@ -351,18 +353,18 @@ void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership) component.replace(mesh, ownership); } -void GeometrySet::replace_curve(CurveEval *curve, GeometryOwnershipType ownership) +void GeometrySet::replace_curve(Curves *curves, GeometryOwnershipType ownership) { - if (curve == nullptr) { + if (curves == nullptr) { this->remove<CurveComponent>(); return; } - if (curve == this->get_curve_for_read()) { + if (curves == this->get_curves_for_read()) { return; } this->remove<CurveComponent>(); CurveComponent &component = this->get_component_for_write<CurveComponent>(); - component.replace(curve, ownership); + component.replace(curves, ownership); } void GeometrySet::replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership) @@ -411,7 +413,7 @@ Volume *GeometrySet::get_volume_for_write() return component == nullptr ? nullptr : component->get_for_write(); } -CurveEval *GeometrySet::get_curve_for_write() +Curves *GeometrySet::get_curves_for_write() { CurveComponent *component = this->get_component_ptr<CurveComponent>(); return component == nullptr ? nullptr : component->get_for_write(); @@ -632,7 +634,7 @@ bool BKE_object_has_geometry_set_instances(const Object *ob) is_instance = ob->type != OB_VOLUME; break; case GEO_COMPONENT_TYPE_CURVE: - is_instance = !ELEM(ob->type, OB_CURVE, OB_FONT); + is_instance = !ELEM(ob->type, OB_CURVES_LEGACY, OB_FONT); break; } if (is_instance) { diff --git a/source/blender/blenkernel/intern/gpencil_curve.c b/source/blender/blenkernel/intern/gpencil_curve.c index 5ce45f6df5a..ee6b77e6463 100644 --- a/source/blender/blenkernel/intern/gpencil_curve.c +++ b/source/blender/blenkernel/intern/gpencil_curve.c @@ -443,7 +443,7 @@ static void gpencil_convert_spline(Main *bmain, } if (sample > 0.0f) { - BKE_gpencil_stroke_sample(gpd, gps, sample, false); + BKE_gpencil_stroke_sample(gpd, gps, sample, false, 0); } /* Recalc fill geometry. */ diff --git a/source/blender/blenkernel/intern/gpencil_geom.cc b/source/blender/blenkernel/intern/gpencil_geom.cc index 365171b300f..865bcebee25 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.cc +++ b/source/blender/blenkernel/intern/gpencil_geom.cc @@ -202,8 +202,8 @@ static int stroke_march_next_point(const bGPDstroke *gps, int next_point_index = index_next_pt; bGPDspoint *pt = nullptr; - if (!(next_point_index < gps->totpoints)) { - return -1; + if (next_point_index == gps->totpoints) { + next_point_index = 0; } copy_v3_v3(step_start, current); @@ -211,15 +211,33 @@ static int stroke_march_next_point(const bGPDstroke *gps, copy_v3_v3(point, &pt->x); remaining_till_next = len_v3v3(point, step_start); - while (remaining_till_next < remaining_march) { + while (remaining_till_next < remaining_march && next_point_index) { remaining_march -= remaining_till_next; pt = &gps->points[next_point_index]; + if (pt->flag & GP_SPOINT_TEMP_TAG) { + pt = &gps->points[next_point_index]; + copy_v3_v3(result, &pt->x); + *pressure = gps->points[next_point_index].pressure; + *strength = gps->points[next_point_index].strength; + memcpy(vert_color, gps->points[next_point_index].vert_color, sizeof(float[4])); + + *index_from = next_point_index == 0 ? (gps->totpoints - 1) : (next_point_index - 1); + *index_to = next_point_index; + *ratio_result = 1.0f; + next_point_index++; + return next_point_index == 0 ? gps->totpoints : next_point_index; + } + next_point_index++; copy_v3_v3(point, &pt->x); copy_v3_v3(step_start, point); - next_point_index++; if (!(next_point_index < gps->totpoints)) { - next_point_index = gps->totpoints - 1; - break; + if (gps->flag & GP_STROKE_CYCLIC) { + next_point_index = 0; + } + else { + next_point_index = gps->totpoints - 1; + break; + } } pt = &gps->points[next_point_index]; copy_v3_v3(point, &pt->x); @@ -232,35 +250,37 @@ static int stroke_march_next_point(const bGPDstroke *gps, *strength = gps->points[next_point_index].strength; memcpy(vert_color, gps->points[next_point_index].vert_color, sizeof(float[4])); - *index_from = next_point_index - 1; + *index_from = next_point_index == 0 ? (gps->totpoints - 1) : (next_point_index - 1); *index_to = next_point_index; *ratio_result = 1.0f; return 0; } + *index_from = next_point_index == 0 ? (gps->totpoints - 1) : (next_point_index - 1); + *index_to = next_point_index; + float ratio = remaining_march / remaining_till_next; interp_v3_v3v3(result, step_start, point, ratio); + *ratio_result = ratio; + *pressure = interpf( - gps->points[next_point_index].pressure, gps->points[next_point_index - 1].pressure, ratio); + gps->points[next_point_index].pressure, gps->points[*index_from].pressure, ratio); *strength = interpf( - gps->points[next_point_index].strength, gps->points[next_point_index - 1].strength, ratio); + gps->points[next_point_index].strength, gps->points[*index_from].strength, ratio); interp_v4_v4v4(vert_color, - gps->points[next_point_index - 1].vert_color, + gps->points[*index_from].vert_color, gps->points[next_point_index].vert_color, ratio); - *index_from = next_point_index - 1; - *index_to = next_point_index; - *ratio_result = ratio; - - return next_point_index; + return next_point_index == 0 ? gps->totpoints : next_point_index; } static int stroke_march_next_point_no_interp(const bGPDstroke *gps, const int index_next_pt, const float *current, const float dist, + const float sharp_threshold, float *result) { float remaining_till_next = 0.0f; @@ -270,8 +290,8 @@ static int stroke_march_next_point_no_interp(const bGPDstroke *gps, int next_point_index = index_next_pt; bGPDspoint *pt = nullptr; - if (!(next_point_index < gps->totpoints)) { - return -1; + if (next_point_index == gps->totpoints) { + next_point_index = 0; } copy_v3_v3(step_start, current); @@ -279,15 +299,29 @@ static int stroke_march_next_point_no_interp(const bGPDstroke *gps, copy_v3_v3(point, &pt->x); remaining_till_next = len_v3v3(point, step_start); - while (remaining_till_next < remaining_march) { + while (remaining_till_next < remaining_march && next_point_index) { remaining_march -= remaining_till_next; pt = &gps->points[next_point_index]; + if (next_point_index < gps->totpoints - 1 && + angle_v3v3v3(&gps->points[next_point_index - 1].x, + &gps->points[next_point_index].x, + &gps->points[next_point_index + 1].x) < sharp_threshold) { + copy_v3_v3(result, &pt->x); + pt->flag |= GP_SPOINT_TEMP_TAG; + next_point_index++; + return next_point_index == 0 ? gps->totpoints : next_point_index; + } + next_point_index++; copy_v3_v3(point, &pt->x); copy_v3_v3(step_start, point); - next_point_index++; if (!(next_point_index < gps->totpoints)) { - next_point_index = gps->totpoints - 1; - break; + if (gps->flag & GP_STROKE_CYCLIC) { + next_point_index = 0; + } + else { + next_point_index = gps->totpoints - 1; + break; + } } pt = &gps->points[next_point_index]; copy_v3_v3(point, &pt->x); @@ -296,15 +330,16 @@ static int stroke_march_next_point_no_interp(const bGPDstroke *gps, if (remaining_till_next < remaining_march) { pt = &gps->points[next_point_index]; copy_v3_v3(result, &pt->x); + /* Stroke marching only terminates here. */ return 0; } float ratio = remaining_march / remaining_till_next; interp_v3_v3v3(result, step_start, point, ratio); - return next_point_index; + return next_point_index == 0 ? gps->totpoints : next_point_index; } -static int stroke_march_count(const bGPDstroke *gps, const float dist) +static int stroke_march_count(const bGPDstroke *gps, const float dist, const float sharp_threshold) { int point_count = 0; float point[3]; @@ -315,8 +350,13 @@ static int stroke_march_count(const bGPDstroke *gps, const float dist) copy_v3_v3(point, &pt->x); point_count++; + /* Sharp points will be tagged by the stroke_march_next_point_no_interp() call below. */ + for (int i = 0; i < gps->totpoints; i++) { + gps->points[i].flag &= (~GP_SPOINT_TEMP_TAG); + } + while ((next_point_index = stroke_march_next_point_no_interp( - gps, next_point_index, point, dist, point)) > -1) { + gps, next_point_index, point, dist, sharp_threshold, point)) > -1) { point_count++; if (next_point_index == 0) { break; /* last point finished */ @@ -394,7 +434,11 @@ static void stroke_interpolate_deform_weights( } } -bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist, const bool select) +bool BKE_gpencil_stroke_sample(bGPdata *gpd, + bGPDstroke *gps, + const float dist, + const bool select, + const float sharp_threshold) { bGPDspoint *pt = gps->points; bGPDspoint *pt1 = nullptr; @@ -406,7 +450,7 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist, return false; } /* TODO: Implement feature point preservation. */ - int count = stroke_march_count(gps, dist); + int count = stroke_march_count(gps, dist, sharp_threshold); bGPDspoint *new_pt = (bGPDspoint *)MEM_callocN(sizeof(bGPDspoint) * count, "gp_stroke_points_sampled"); @@ -491,6 +535,8 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist, gps->totpoints = i; + gps->flag &= (~GP_STROKE_CYCLIC); + /* Calc geometry data. */ BKE_gpencil_stroke_geometry_update(gpd, gps); @@ -2052,27 +2098,30 @@ void BKE_gpencil_stroke_subdivide(bGPdata *gpd, bGPDstroke *gps, int level, int MDeformVert *dvert_final = nullptr; MDeformVert *dvert_next = nullptr; int totnewpoints, oldtotpoints; - int i2; + + bool cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0; for (int s = 0; s < level; s++) { - totnewpoints = gps->totpoints - 1; + totnewpoints = gps->totpoints; + if (!cyclic) { + totnewpoints--; + } /* duplicate points in a temp area */ - temp_points = (bGPDspoint *)MEM_dupallocN(gps->points); + temp_points = gps->points; oldtotpoints = gps->totpoints; /* resize the points arrays */ gps->totpoints += totnewpoints; - gps->points = (bGPDspoint *)MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints); + gps->points = (bGPDspoint *)MEM_malloc_arrayN(gps->totpoints, sizeof(*gps->points), __func__); if (gps->dvert != nullptr) { - temp_dverts = (MDeformVert *)MEM_dupallocN(gps->dvert); - gps->dvert = (MDeformVert *)MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints); + temp_dverts = gps->dvert; + gps->dvert = (MDeformVert *)MEM_malloc_arrayN(gps->totpoints, sizeof(*gps->dvert), __func__); } /* move points from last to first to new place */ - i2 = gps->totpoints - 1; - for (int i = oldtotpoints - 1; i > 0; i--) { + for (int i = 0; i < oldtotpoints; i++) { bGPDspoint *pt = &temp_points[i]; - bGPDspoint *pt_final = &gps->points[i2]; + bGPDspoint *pt_final = &gps->points[i * 2]; copy_v3_v3(&pt_final->x, &pt->x); pt_final->pressure = pt->pressure; @@ -2085,18 +2134,16 @@ void BKE_gpencil_stroke_subdivide(bGPdata *gpd, bGPDstroke *gps, int level, int if (gps->dvert != nullptr) { dvert = &temp_dverts[i]; - dvert_final = &gps->dvert[i2]; + dvert_final = &gps->dvert[i * 2]; dvert_final->totweight = dvert->totweight; dvert_final->dw = dvert->dw; } - i2 -= 2; } /* interpolate mid points */ - i2 = 1; - for (int i = 0; i < oldtotpoints - 1; i++) { - bGPDspoint *pt = &temp_points[i]; - bGPDspoint *next = &temp_points[i + 1]; - bGPDspoint *pt_final = &gps->points[i2]; + for (int i = cyclic ? 0 : 1, j = cyclic ? oldtotpoints - 1 : 0; i < oldtotpoints; j = i, i++) { + bGPDspoint *pt = &temp_points[j]; + bGPDspoint *next = &temp_points[i]; + bGPDspoint *pt_final = &gps->points[j * 2 + 1]; /* add a half way point */ interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f); @@ -2109,9 +2156,9 @@ void BKE_gpencil_stroke_subdivide(bGPdata *gpd, bGPDstroke *gps, int level, int interp_v4_v4v4(pt_final->vert_color, pt->vert_color, next->vert_color, 0.5f); if (gps->dvert != nullptr) { - dvert = &temp_dverts[i]; - dvert_next = &temp_dverts[i + 1]; - dvert_final = &gps->dvert[i2]; + dvert = &temp_dverts[j]; + dvert_next = &temp_dverts[i]; + dvert_final = &gps->dvert[j * 2 + 1]; dvert_final->totweight = dvert->totweight; dvert_final->dw = (MDeformWeight *)MEM_dupallocN(dvert->dw); @@ -2126,8 +2173,6 @@ void BKE_gpencil_stroke_subdivide(bGPdata *gpd, bGPDstroke *gps, int level, int } } } - - i2 += 2; } MEM_SAFE_FREE(temp_points); @@ -2135,20 +2180,18 @@ void BKE_gpencil_stroke_subdivide(bGPdata *gpd, bGPDstroke *gps, int level, int /* Move points to smooth stroke (not simple type). */ if (type != GP_SUBDIV_SIMPLE) { - /* duplicate points in a temp area with the new subdivide data */ - temp_points = (bGPDspoint *)MEM_dupallocN(gps->points); - + float mid[3]; /* extreme points are not changed */ - for (int i = 0; i < gps->totpoints - 2; i++) { - bGPDspoint *pt = &temp_points[i]; - bGPDspoint *next = &temp_points[i + 1]; - bGPDspoint *pt_final = &gps->points[i + 1]; + for (int i = cyclic ? 0 : 2, j = cyclic ? gps->totpoints - 2 : 0; i < gps->totpoints - 2; + j = i, i += 2) { + bGPDspoint *prev = &gps->points[j + 1]; + bGPDspoint *pt = &gps->points[i]; + bGPDspoint *next = &gps->points[i + 1]; /* move point */ - interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f); + interp_v3_v3v3(mid, &prev->x, &next->x, 0.5f); + interp_v3_v3v3(&pt->x, mid, &pt->x, 0.5f); } - /* free temp memory */ - MEM_SAFE_FREE(temp_points); } } diff --git a/source/blender/blenkernel/intern/idtype.c b/source/blender/blenkernel/intern/idtype.c index 2551bb12511..5b9dfa55c45 100644 --- a/source/blender/blenkernel/intern/idtype.c +++ b/source/blender/blenkernel/intern/idtype.c @@ -59,7 +59,7 @@ static void id_type_init(void) INIT_TYPE(ID_LI); INIT_TYPE(ID_OB); INIT_TYPE(ID_ME); - INIT_TYPE(ID_CU); + INIT_TYPE(ID_CU_LEGACY); INIT_TYPE(ID_MB); INIT_TYPE(ID_MA); INIT_TYPE(ID_TE); @@ -215,7 +215,7 @@ uint64_t BKE_idtype_idcode_to_idfilter(const short idcode) CASE_IDFILTER(BR); CASE_IDFILTER(CA); CASE_IDFILTER(CF); - CASE_IDFILTER(CU); + CASE_IDFILTER(CU_LEGACY); CASE_IDFILTER(GD); CASE_IDFILTER(GR); CASE_IDFILTER(CV); @@ -264,7 +264,7 @@ short BKE_idtype_idcode_from_idfilter(const uint64_t idfilter) CASE_IDFILTER(BR); CASE_IDFILTER(CA); CASE_IDFILTER(CF); - CASE_IDFILTER(CU); + CASE_IDFILTER(CU_LEGACY); CASE_IDFILTER(GD); CASE_IDFILTER(GR); CASE_IDFILTER(CV); @@ -312,7 +312,7 @@ int BKE_idtype_idcode_to_index(const short idcode) CASE_IDINDEX(BR); CASE_IDINDEX(CA); CASE_IDINDEX(CF); - CASE_IDINDEX(CU); + CASE_IDINDEX(CU_LEGACY); CASE_IDINDEX(GD); CASE_IDINDEX(GR); CASE_IDINDEX(CV); @@ -371,7 +371,7 @@ short BKE_idtype_idcode_from_index(const int index) CASE_IDCODE(BR); CASE_IDCODE(CA); CASE_IDCODE(CF); - CASE_IDCODE(CU); + CASE_IDCODE(CU_LEGACY); CASE_IDCODE(GD); CASE_IDCODE(GR); CASE_IDCODE(CV); diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index fa63f99d3f1..8a212ed0d7d 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -3569,6 +3569,7 @@ static void image_tag_reload(Image *ima, ID *iuser_id, ImageUser *iuser, void *c /* Must copy image user changes to CoW data-block. */ DEG_id_tag_update(iuser_id, ID_RECALC_COPY_ON_WRITE); } + BKE_image_partial_update_mark_full_update(ima); } } @@ -3609,6 +3610,7 @@ static void image_free_tile(Image *ima, ImageTile *tile) } } } + BKE_image_partial_update_mark_full_update(ima); if (BKE_image_is_multiview(ima)) { const int totviews = BLI_listbase_count(&ima->views); @@ -3949,6 +3951,7 @@ ImageTile *BKE_image_add_tile(struct Image *ima, int tile_number, const char *la } } } + BKE_image_partial_update_mark_full_update(ima); return tile; } @@ -4014,6 +4017,7 @@ void BKE_image_reassign_tile(struct Image *ima, ImageTile *tile, int new_tile_nu } } } + BKE_image_partial_update_mark_full_update(ima); } static int tile_sort_cb(const void *a, const void *b) diff --git a/source/blender/blenkernel/intern/image_gpu.cc b/source/blender/blenkernel/intern/image_gpu.cc index 5675641deb4..c4a43d8b023 100644 --- a/source/blender/blenkernel/intern/image_gpu.cc +++ b/source/blender/blenkernel/intern/image_gpu.cc @@ -322,19 +322,25 @@ static void image_gpu_texture_partial_update_changes_available( Image *image, PartialUpdateChecker<ImageTileData>::CollectResult &changes) { while (changes.get_next_change() == ePartialUpdateIterResult::ChangeAvailable) { - const int tile_offset_x = changes.changed_region.region.xmin; - const int tile_offset_y = changes.changed_region.region.ymin; - const int tile_width = min_ii(changes.tile_data.tile_buffer->x, - BLI_rcti_size_x(&changes.changed_region.region)); - const int tile_height = min_ii(changes.tile_data.tile_buffer->y, - BLI_rcti_size_y(&changes.changed_region.region)); + /* Calculate the clipping region with the tile buffer. + * TODO(jbakker): should become part of ImageTileData to deduplicate with image engine. */ + rcti buffer_rect; + BLI_rcti_init( + &buffer_rect, 0, changes.tile_data.tile_buffer->x, 0, changes.tile_data.tile_buffer->y); + rcti clipped_update_region; + const bool has_overlap = BLI_rcti_isect( + &buffer_rect, &changes.changed_region.region, &clipped_update_region); + if (!has_overlap) { + continue; + } + image_update_gputexture_ex(image, changes.tile_data.tile, changes.tile_data.tile_buffer, - tile_offset_x, - tile_offset_y, - tile_width, - tile_height); + clipped_update_region.xmin, + clipped_update_region.ymin, + BLI_rcti_size_x(&clipped_update_region), + BLI_rcti_size_y(&clipped_update_region)); } } @@ -431,7 +437,8 @@ static GPUTexture *image_get_gpu_texture(Image *ima, if (ibuf_intern == nullptr) { ibuf_intern = BKE_image_acquire_ibuf(ima, iuser, nullptr); if (ibuf_intern == nullptr) { - return image_gpu_texture_error_create(textarget); + *tex = image_gpu_texture_error_create(textarget); + return *tex; } } diff --git a/source/blender/blenkernel/intern/image_partial_update.cc b/source/blender/blenkernel/intern/image_partial_update.cc index 9d5635f49ab..4606a14ab69 100644 --- a/source/blender/blenkernel/intern/image_partial_update.cc +++ b/source/blender/blenkernel/intern/image_partial_update.cc @@ -198,8 +198,8 @@ struct TileChangeset { tile_width = image_buffer->x; tile_height = image_buffer->y; - int chunk_x_len = tile_width / CHUNK_SIZE; - int chunk_y_len = tile_height / CHUNK_SIZE; + int chunk_x_len = (tile_width + CHUNK_SIZE - 1) / CHUNK_SIZE; + int chunk_y_len = (tile_height + CHUNK_SIZE - 1) / CHUNK_SIZE; init_chunks(chunk_x_len, chunk_y_len); return true; } diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c index e5c1cf96f8c..abd6505456e 100644 --- a/source/blender/blenkernel/intern/ipo.c +++ b/source/blender/blenkernel/intern/ipo.c @@ -1090,7 +1090,7 @@ static char *get_rna_access(ID *id, propname = particle_adrcodes_to_paths(adrcode, &dummy_index); break; - case ID_CU: /* curve */ + case ID_CU_LEGACY: /* curve */ /* this used to be a 'dummy' curve which got evaluated on the fly... * now we've got real var for this! */ diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index b28d9db92cf..e28094c0abc 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -281,7 +281,7 @@ Key *BKE_key_add(Main *bmain, ID *id) /* common function */ key->elemsize = sizeof(float[KEYELEM_FLOAT_LEN_COORD]); break; - case ID_CU: + case ID_CU_LEGACY: el = key->elemstr; el[0] = KEYELEM_ELEM_SIZE_CURVE; @@ -659,7 +659,7 @@ static bool key_pointer_size(const Key *key, const int mode, int *poinsize, int *ofs = sizeof(float[KEYELEM_FLOAT_LEN_COORD]); *poinsize = *ofs; break; - case ID_CU: + case ID_CU_LEGACY: if (mode == KEY_MODE_BPOINT) { *ofs = sizeof(float[KEYELEM_FLOAT_LEN_BPOINT]); *step = KEYELEM_ELEM_LEN_BPOINT; @@ -1524,7 +1524,7 @@ float *BKE_key_evaluate_object_ex(Object *ob, int *r_totelem, float *arr, size_t tot = lt->pntsu * lt->pntsv * lt->pntsw; size = tot * sizeof(float[KEYELEM_FLOAT_LEN_COORD]); } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { Curve *cu = ob->data; tot = BKE_keyblock_curve_element_count(&cu->nurb); @@ -1570,7 +1570,7 @@ float *BKE_key_evaluate_object_ex(Object *ob, int *r_totelem, float *arr, size_t MEM_freeN(weights); } } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { cp_cu_key(ob->data, key, actkb, kb, 0, tot, out, tot); } } @@ -1582,7 +1582,7 @@ float *BKE_key_evaluate_object_ex(Object *ob, int *r_totelem, float *arr, size_t else if (ob->type == OB_LATTICE) { do_latt_key(ob, key, out, tot); } - else if (ob->type == OB_CURVE) { + else if (ob->type == OB_CURVES_LEGACY) { do_curve_key(ob, key, out, tot); } else if (ob->type == OB_SURF) { @@ -1714,7 +1714,7 @@ bool BKE_key_idtype_support(const short id_type) { switch (id_type) { case ID_ME: - case ID_CU: + case ID_CU_LEGACY: case ID_LT: return true; default: @@ -1729,7 +1729,7 @@ Key **BKE_key_from_id_p(ID *id) Mesh *me = (Mesh *)id; return &me->key; } - case ID_CU: { + case ID_CU_LEGACY: { Curve *cu = (Curve *)id; if (cu->vfont == NULL) { return &cu->key; @@ -2269,7 +2269,7 @@ void BKE_keyblock_update_from_vertcos(Object *ob, KeyBlock *kb, const float (*ve Lattice *lt = ob->data; BLI_assert((lt->pntsu * lt->pntsv * lt->pntsw) == kb->totelem); } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { Curve *cu = ob->data; BLI_assert(BKE_keyblock_curve_element_count(&cu->nurb) == kb->totelem); } @@ -2293,7 +2293,7 @@ void BKE_keyblock_update_from_vertcos(Object *ob, KeyBlock *kb, const float (*ve copy_v3_v3(fp, *co); } } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { Curve *cu = (Curve *)ob->data; Nurb *nu; BezTriple *bezt; @@ -2335,7 +2335,7 @@ void BKE_keyblock_convert_from_vertcos(Object *ob, KeyBlock *kb, const float (*v tot = lt->pntsu * lt->pntsv * lt->pntsw; elemsize = lt->key->elemsize; } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { Curve *cu = (Curve *)ob->data; elemsize = cu->key->elemsize; tot = BKE_keyblock_curve_element_count(&cu->nurb); @@ -2366,7 +2366,7 @@ float (*BKE_keyblock_convert_to_vertcos(Object *ob, KeyBlock *kb))[3] Lattice *lt = (Lattice *)ob->data; tot = lt->pntsu * lt->pntsv * lt->pntsw; } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { Curve *cu = (Curve *)ob->data; tot = BKE_nurbList_verts_count(&cu->nurb); } @@ -2383,7 +2383,7 @@ float (*BKE_keyblock_convert_to_vertcos(Object *ob, KeyBlock *kb))[3] copy_v3_v3(*co, fp); } } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { Curve *cu = (Curve *)ob->data; Nurb *nu; BezTriple *bezt; @@ -2422,7 +2422,7 @@ void BKE_keyblock_update_from_offset(Object *ob, KeyBlock *kb, const float (*ofs add_v3_v3(fp, *ofs); } } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { Curve *cu = (Curve *)ob->data; Nurb *nu; BezTriple *bezt; diff --git a/source/blender/blenkernel/intern/layer_utils.c b/source/blender/blenkernel/intern/layer_utils.c index 057309d0896..0903c2a2cac 100644 --- a/source/blender/blenkernel/intern/layer_utils.c +++ b/source/blender/blenkernel/intern/layer_utils.c @@ -67,9 +67,11 @@ Object **BKE_view_layer_array_selected_objects_params( } FOREACH_SELECTED_OBJECT_END; - object_array = MEM_reallocN(object_array, sizeof(*object_array) * BLI_array_len(object_array)); - /* We always need a valid allocation (prevent crash on free). */ - if (object_array == NULL) { + if (object_array != NULL) { + BLI_array_trim(object_array); + } + else { + /* We always need a valid allocation (prevent crash on free). */ object_array = MEM_mallocN(0, __func__); } *r_len = BLI_array_len(object_array); @@ -121,9 +123,11 @@ Base **BKE_view_layer_array_from_bases_in_mode_params(ViewLayer *view_layer, } FOREACH_BASE_IN_MODE_END; - base_array = MEM_reallocN(base_array, sizeof(*base_array) * BLI_array_len(base_array)); /* We always need a valid allocation (prevent crash on free). */ - if (base_array == NULL) { + if (base_array != NULL) { + BLI_array_trim(base_array); + } + else { base_array = MEM_mallocN(0, __func__); } *r_len = BLI_array_len(base_array); diff --git a/source/blender/blenkernel/intern/lib_id_delete.c b/source/blender/blenkernel/intern/lib_id_delete.c index cf25af1c637..ba5556c8b2d 100644 --- a/source/blender/blenkernel/intern/lib_id_delete.c +++ b/source/blender/blenkernel/intern/lib_id_delete.c @@ -234,7 +234,7 @@ static size_t id_delete(Main *bmain, const bool do_tagged_deletion) for (id = lb->first; id; id = id_next) { id_next = id->next; /* NOTE: in case we delete a library, we also delete all its datablocks! */ - if ((id->tag & tag) || (id->lib != NULL && (id->lib->id.tag & tag))) { + if ((id->tag & tag) || (ID_IS_LINKED(id) && (id->lib->id.tag & tag))) { BLI_remlink(lb, id); BLI_addtail(&tagged_deleted_ids, id); /* Do not tag as no_main now, we want to unlink it first (lower-level ID management @@ -290,7 +290,7 @@ static size_t id_delete(Main *bmain, const bool do_tagged_deletion) for (id = lb->first; id; id = id_next) { id_next = id->next; /* NOTE: in case we delete a library, we also delete all its datablocks! */ - if ((id->tag & tag) || (id->lib != NULL && (id->lib->id.tag & tag))) { + if ((id->tag & tag) || (ID_IS_LINKED(id) && (id->lib->id.tag & tag))) { id->tag |= tag; BKE_id_remapper_add(remapper, id, NULL); } diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 02cdd6fcd20..922c1beda38 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -72,12 +72,19 @@ static void lib_override_library_property_operation_clear( IDOverrideLibraryPropertyOperation *opop); /** Get override data for a given ID. Needed because of our beloved shape keys snowflake. */ -BLI_INLINE IDOverrideLibrary *lib_override_get(Main *bmain, ID *id) +BLI_INLINE IDOverrideLibrary *lib_override_get(Main *bmain, ID *id, ID **r_owner_id) { + if (r_owner_id != NULL) { + *r_owner_id = id; + } if (id->flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE) { const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); if (id_type->owner_get != NULL) { - return id_type->owner_get(bmain, id)->override_library; + ID *owner_id = id_type->owner_get(bmain, id); + if (r_owner_id != NULL) { + *r_owner_id = owner_id; + } + return owner_id->override_library; } BLI_assert_msg(0, "IDTypeInfo of liboverride-embedded ID with no owner getter"); } @@ -146,6 +153,7 @@ void BKE_lib_override_library_copy(ID *dst_id, const ID *src_id, const bool do_f id_us_plus(dst_id->override_library->reference); dst_id->override_library->hierarchy_root = src_id->override_library->hierarchy_root; + dst_id->override_library->flag = src_id->override_library->flag; if (do_full_copy) { BLI_duplicatelist(&dst_id->override_library->properties, @@ -313,17 +321,36 @@ ID *BKE_lib_override_library_create_from_id(Main *bmain, return local_id; } +/* TODO: Make this static local function instead? API is becoming complex, and it's not used + * outside of this file anyway. */ bool BKE_lib_override_library_create_from_tag(Main *bmain, Library *owner_library, const ID *id_root_reference, ID *id_hierarchy_root, + const ID *id_hierarchy_root_reference, const bool do_no_main) { - BLI_assert(id_root_reference != NULL); - BLI_assert(id_hierarchy_root != NULL || (id_root_reference->tag & LIB_TAG_DOIT) != 0); - BLI_assert(id_hierarchy_root == NULL || - (ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root) && - id_hierarchy_root->override_library->reference == id_root_reference)); + BLI_assert(id_root_reference != NULL && ID_IS_LINKED(id_root_reference)); + /* If we do not have any hierarchy root given, then the root reference must be tagged for + * override. */ + BLI_assert(id_hierarchy_root != NULL || id_hierarchy_root_reference != NULL || + (id_root_reference->tag & LIB_TAG_DOIT) != 0); + /* At least one of the hierarchy root pointers must be NULL, passing both is useless and can + * create confusion. */ + BLI_assert(ELEM(NULL, id_hierarchy_root, id_hierarchy_root_reference)); + + if (id_hierarchy_root != NULL) { + /* If the hierarchy root is given, it must be a valid existing override (used during partial + * resync process mainly). */ + BLI_assert((ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root) && + id_hierarchy_root->override_library->reference->lib == id_root_reference->lib)); + } + if (!ELEM(id_hierarchy_root_reference, NULL, id_root_reference)) { + /* If the reference hierarchy root is given, it must be from the same library as the reference + * root, and also tagged for override. */ + BLI_assert((id_hierarchy_root_reference->lib == id_root_reference->lib && + (id_hierarchy_root_reference->tag & LIB_TAG_DOIT) != 0)); + } const Library *reference_library = id_root_reference->lib; @@ -379,7 +406,12 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain, /* Only remap new local ID's pointers, we don't want to force our new overrides onto our whole * existing linked IDs usages. */ if (success) { - if (id_root_reference->newid != NULL) { + if (id_hierarchy_root_reference != NULL) { + id_hierarchy_root = id_hierarchy_root_reference->newid; + } + else if (id_root_reference->newid != NULL && + (id_hierarchy_root == NULL || + id_hierarchy_root->override_library->reference == id_root_reference)) { id_hierarchy_root = id_root_reference->newid; } BLI_assert(id_hierarchy_root != NULL); @@ -832,8 +864,8 @@ static void lib_override_overrides_group_tag_recursive(LibOverrideGroupTagData * continue; } - Library *reference_lib = lib_override_get(bmain, id_owner)->reference->lib; - ID *to_id_reference = lib_override_get(bmain, to_id)->reference; + Library *reference_lib = lib_override_get(bmain, id_owner, NULL)->reference->lib; + ID *to_id_reference = lib_override_get(bmain, to_id, NULL)->reference; if (to_id_reference->lib != reference_lib) { /* We do not override data-blocks from other libraries, nor do we process them. */ continue; @@ -879,12 +911,13 @@ static void lib_override_overrides_group_tag(LibOverrideGroupTagData *data) static bool lib_override_library_create_do(Main *bmain, Scene *scene, Library *owner_library, - ID *id_root) + ID *id_root_reference, + ID *id_hierarchy_root_reference) { BKE_main_relations_create(bmain, 0); LibOverrideGroupTagData data = {.bmain = bmain, .scene = scene, - .id_root = id_root, + .id_root = id_root_reference, .tag = LIB_TAG_DOIT, .missing_tag = LIB_TAG_MISSING, .is_override = false, @@ -898,8 +931,18 @@ static bool lib_override_library_create_do(Main *bmain, BKE_main_relations_free(bmain); lib_override_group_tag_data_clear(&data); - const bool success = BKE_lib_override_library_create_from_tag( - bmain, owner_library, id_root, NULL, false); + bool success = false; + if (id_hierarchy_root_reference->lib != id_root_reference->lib) { + BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference)); + BLI_assert(id_hierarchy_root_reference->override_library->reference->lib == + id_root_reference->lib); + success = BKE_lib_override_library_create_from_tag( + bmain, owner_library, id_root_reference, id_hierarchy_root_reference, NULL, false); + } + else { + success = BKE_lib_override_library_create_from_tag( + bmain, owner_library, id_root_reference, NULL, id_hierarchy_root_reference, false); + } return success; } @@ -909,7 +952,7 @@ static void lib_override_library_create_post_process(Main *bmain, ViewLayer *view_layer, const Library *owner_library, ID *id_root, - ID *id_reference, + ID *id_instance_hint, Collection *residual_storage, const bool is_resync) { @@ -933,8 +976,8 @@ static void lib_override_library_create_post_process(Main *bmain, (!ID_IS_LINKED(id_root->newid) || id_root->newid->lib == owner_library)) { switch (GS(id_root->name)) { case ID_GR: { - Object *ob_reference = id_reference != NULL && GS(id_reference->name) == ID_OB ? - (Object *)id_reference : + Object *ob_reference = id_instance_hint != NULL && GS(id_instance_hint->name) == ID_OB ? + (Object *)id_instance_hint : NULL; Collection *collection_new = ((Collection *)id_root->newid); if (is_resync && BKE_collection_is_in_scene(collection_new)) { @@ -943,10 +986,10 @@ static void lib_override_library_create_post_process(Main *bmain, if (ob_reference != NULL) { BKE_collection_add_from_object(bmain, scene, ob_reference, collection_new); } - else if (id_reference != NULL) { - BLI_assert(GS(id_reference->name) == ID_GR); + else if (id_instance_hint != NULL) { + BLI_assert(GS(id_instance_hint->name) == ID_GR); BKE_collection_add_from_collection( - bmain, scene, ((Collection *)id_reference), collection_new); + bmain, scene, ((Collection *)id_instance_hint), collection_new); } else { BKE_collection_add_from_collection( @@ -1039,26 +1082,32 @@ bool BKE_lib_override_library_create(Main *bmain, Scene *scene, ViewLayer *view_layer, Library *owner_library, - ID *id_root, - ID *id_reference, + ID *id_root_reference, + ID *id_hierarchy_root_reference, + ID *id_instance_hint, ID **r_id_root_override) { if (r_id_root_override != NULL) { *r_id_root_override = NULL; } - const bool success = lib_override_library_create_do(bmain, scene, owner_library, id_root); + if (id_hierarchy_root_reference == NULL) { + id_hierarchy_root_reference = id_root_reference; + } + + const bool success = lib_override_library_create_do( + bmain, scene, owner_library, id_root_reference, id_hierarchy_root_reference); if (!success) { return success; } if (r_id_root_override != NULL) { - *r_id_root_override = id_root->newid; + *r_id_root_override = id_root_reference->newid; } lib_override_library_create_post_process( - bmain, scene, view_layer, owner_library, id_root, id_reference, NULL, false); + bmain, scene, view_layer, owner_library, id_root_reference, id_instance_hint, NULL, false); /* Cleanup. */ BKE_main_id_newptr_and_tag_clear(bmain); @@ -1102,11 +1151,18 @@ static ID *lib_override_root_find(Main *bmain, ID *id, const int curr_level, int MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, id); BLI_assert(entry != NULL); - if (entry->tags & MAINIDRELATIONS_ENTRY_TAGS_PROCESSED && ID_IS_OVERRIDE_LIBRARY_REAL(id)) { - /* This ID has already been processed. */ - BLI_assert(id->override_library != NULL); - *r_best_level = curr_level; - return id->override_library->hierarchy_root; + if (entry->tags & MAINIDRELATIONS_ENTRY_TAGS_PROCESSED) { + if (ID_IS_OVERRIDE_LIBRARY_REAL(id)) { + /* This ID has already been processed. */ + *r_best_level = curr_level; + return id->override_library->hierarchy_root; + } + + BLI_assert(id->flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE); + ID *id_owner; + int best_level_placeholder = 0; + lib_override_get(bmain, id, &id_owner); + return lib_override_root_find(bmain, id_owner, curr_level + 1, &best_level_placeholder); } /* This way we won't process again that ID, should we encounter it again through another * relationship hierarchy. */ @@ -1140,7 +1196,17 @@ static ID *lib_override_root_find(Main *bmain, ID *id, const int curr_level, int } } + if (!ID_IS_OVERRIDE_LIBRARY_REAL(best_root_id_candidate)) { + BLI_assert(id->flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE); + ID *id_owner; + int best_level_placeholder = 0; + lib_override_get(bmain, best_root_id_candidate, &id_owner); + best_root_id_candidate = lib_override_root_find( + bmain, id_owner, curr_level + 1, &best_level_placeholder); + } + BLI_assert(best_root_id_candidate != NULL); + BLI_assert((best_root_id_candidate->flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE) == 0); *r_best_level = best_level_candidate; return best_root_id_candidate; @@ -1430,7 +1496,7 @@ static bool lib_override_library_resync(Main *bmain, /* While this should not happen in typical cases (and won't be properly supported here), * user is free to do all kind of very bad things, including having different local * overrides of a same linked ID in a same hierarchy. */ - IDOverrideLibrary *id_override_library = lib_override_get(bmain, id); + IDOverrideLibrary *id_override_library = lib_override_get(bmain, id, NULL); ID *reference_id = id_override_library->reference; if (GS(reference_id->name) != GS(id->name)) { switch (GS(id->name)) { @@ -1502,7 +1568,7 @@ static bool lib_override_library_resync(Main *bmain, * override IDs (including within the old overrides themselves, since those are tagged too * above). */ const bool success = BKE_lib_override_library_create_from_tag( - bmain, NULL, id_root_reference, id_root->override_library->hierarchy_root, true); + bmain, NULL, id_root_reference, id_root->override_library->hierarchy_root, NULL, true); if (!success) { BLI_ghash_free(linkedref_to_old_override, NULL, NULL); @@ -2127,7 +2193,7 @@ static void lib_override_library_main_resync_on_library_indirect_level( "ID override %s from library level %d still found as needing resync, when all " "IDs from that level should have been processed after tackling library level %d", id->name, - id->lib != NULL ? id->lib->temp_index : 0, + ID_IS_LINKED(id) ? id->lib->temp_index : 0, library_indirect_level); id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC; } @@ -2136,6 +2202,10 @@ static void lib_override_library_main_resync_on_library_indirect_level( BLI_ghash_free(id_roots, NULL, MEM_freeN); + /* In some fairly rare (and degenerate) cases, some root ID from other liboverrides may have been + * freed, and therefore set to NULL. Attempt to fix this as best as possible. */ + BKE_lib_override_library_main_hierarchy_root_ensure(bmain); + if (do_reports_recursive_resync_timing) { reports->duration.lib_overrides_recursive_resync += PIL_check_seconds_timer() - init_time; } diff --git a/source/blender/blenkernel/intern/lib_override_proxy_conversion.c b/source/blender/blenkernel/intern/lib_override_proxy_conversion.c index 809235ad24c..dc164313788 100644 --- a/source/blender/blenkernel/intern/lib_override_proxy_conversion.c +++ b/source/blender/blenkernel/intern/lib_override_proxy_conversion.c @@ -42,7 +42,7 @@ bool BKE_lib_override_library_proxy_convert(Main *bmain, const bool is_override_instancing_object = ob_proxy_group != NULL; ID *id_root = is_override_instancing_object ? &ob_proxy_group->instance_collection->id : &ob_proxy->proxy->id; - ID *id_reference = is_override_instancing_object ? &ob_proxy_group->id : &ob_proxy->id; + ID *id_instance_hint = is_override_instancing_object ? &ob_proxy_group->id : &ob_proxy->id; /* In some cases the instance collection of a proxy object may be local (see e.g. T83875). Not * sure this is a valid state, but for now just abort the overriding process. */ @@ -81,7 +81,7 @@ bool BKE_lib_override_library_proxy_convert(Main *bmain, FOREACH_MAIN_ID_END; return BKE_lib_override_library_create( - bmain, scene, view_layer, ob_proxy->id.lib, id_root, id_reference, NULL); + bmain, scene, view_layer, ob_proxy->id.lib, id_root, id_root, id_instance_hint, NULL); } static void lib_override_library_proxy_convert_do(Main *bmain, diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index ba009072db8..5de8704e13b 100644 --- a/source/blender/blenkernel/intern/lib_query.c +++ b/source/blender/blenkernel/intern/lib_query.c @@ -402,7 +402,7 @@ uint64_t BKE_library_id_can_use_filter_id(const ID *id_owner) return FILTER_ID_ALL; case ID_ME: return FILTER_ID_ME | FILTER_ID_MA | FILTER_ID_IM; - case ID_CU: + case ID_CU_LEGACY: return FILTER_ID_OB | FILTER_ID_MA | FILTER_ID_VF; case ID_MB: return FILTER_ID_MA; @@ -418,7 +418,7 @@ uint64_t BKE_library_id_can_use_filter_id(const ID *id_owner) return FILTER_ID_OB | FILTER_ID_IM; case ID_KE: /* Warning! key->from, could be more types in future? */ - return FILTER_ID_ME | FILTER_ID_CU | FILTER_ID_LT; + return FILTER_ID_ME | FILTER_ID_CU_LEGACY | FILTER_ID_LT; case ID_SCR: return FILTER_ID_SCE; case ID_WO: @@ -448,7 +448,7 @@ uint64_t BKE_library_id_can_use_filter_id(const ID *id_owner) case ID_WS: return FILTER_ID_SCE; case ID_CV: - return FILTER_ID_MA; + return FILTER_ID_MA | FILTER_ID_OB; case ID_PT: return FILTER_ID_MA; case ID_VO: @@ -490,7 +490,7 @@ bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used) /* Exception: ID_KE aren't available as filter_id. */ if (id_type_used == ID_KE) { - return ELEM(id_type_owner, ID_ME, ID_CU, ID_LT); + return ELEM(id_type_owner, ID_ME, ID_CU_LEGACY, ID_LT); } /* Exception: ID_SCR aren't available as filter_id. */ diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index 9329a09f1b6..24e7178dd63 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -380,7 +380,7 @@ static void libblock_remap_data_postprocess_obdata_relink(Main *bmain, Object *o case ID_ME: multires_force_sculpt_rebuild(ob); break; - case ID_CU: + case ID_CU_LEGACY: BKE_curve_type_test(ob); break; default: @@ -573,7 +573,7 @@ static void libblock_remap_foreach_idpair_cb(ID *old_id, ID *new_id, void *user_ bmain, NULL, (Collection *)old_id, (Collection *)new_id); break; case ID_ME: - case ID_CU: + case ID_CU_LEGACY: case ID_MB: case ID_CV: case ID_PT: diff --git a/source/blender/blenkernel/intern/main.c b/source/blender/blenkernel/intern/main.c index b25432780ed..03e03dacfbc 100644 --- a/source/blender/blenkernel/intern/main.c +++ b/source/blender/blenkernel/intern/main.c @@ -563,7 +563,7 @@ ListBase *which_libbase(Main *bmain, short type) return &(bmain->objects); case ID_ME: return &(bmain->meshes); - case ID_CU: + case ID_CU_LEGACY: return &(bmain->curves); case ID_MB: return &(bmain->metaballs); @@ -670,7 +670,7 @@ int set_listbasepointers(Main *bmain, ListBase *lb[/*INDEX_ID_MAX*/]) lb[INDEX_ID_CF] = &(bmain->cachefiles); lb[INDEX_ID_ME] = &(bmain->meshes); - lb[INDEX_ID_CU] = &(bmain->curves); + lb[INDEX_ID_CU_LEGACY] = &(bmain->curves); lb[INDEX_ID_MB] = &(bmain->metaballs); lb[INDEX_ID_CV] = &(bmain->hair_curves); lb[INDEX_ID_PT] = &(bmain->pointclouds); diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index c559c6fe807..7d01a92e829 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -313,7 +313,7 @@ Material ***BKE_object_material_array_p(Object *ob) Mesh *me = ob->data; return &(me->mat); } - if (ELEM(ob->type, OB_CURVE, OB_FONT, OB_SURF)) { + if (ELEM(ob->type, OB_CURVES_LEGACY, OB_FONT, OB_SURF)) { Curve *cu = ob->data; return &(cu->mat); } @@ -346,7 +346,7 @@ short *BKE_object_material_len_p(Object *ob) Mesh *me = ob->data; return &(me->totcol); } - if (ELEM(ob->type, OB_CURVE, OB_FONT, OB_SURF)) { + if (ELEM(ob->type, OB_CURVES_LEGACY, OB_FONT, OB_SURF)) { Curve *cu = ob->data; return &(cu->totcol); } @@ -381,7 +381,7 @@ Material ***BKE_id_material_array_p(ID *id) switch (GS(id->name)) { case ID_ME: return &(((Mesh *)id)->mat); - case ID_CU: + case ID_CU_LEGACY: return &(((Curve *)id)->mat); case ID_MB: return &(((MetaBall *)id)->mat); @@ -407,7 +407,7 @@ short *BKE_id_material_len_p(ID *id) switch (GS(id->name)) { case ID_ME: return &(((Mesh *)id)->totcol); - case ID_CU: + case ID_CU_LEGACY: return &(((Curve *)id)->totcol); case ID_MB: return &(((MetaBall *)id)->totcol); @@ -434,7 +434,7 @@ static void material_data_index_remove_id(ID *id, short index) case ID_ME: BKE_mesh_material_index_remove((Mesh *)id, index); break; - case ID_CU: + case ID_CU_LEGACY: BKE_curve_material_index_remove((Curve *)id, index); break; case ID_MB: @@ -468,7 +468,7 @@ bool BKE_object_material_slot_used(Object *object, short actcol) switch (GS(ob_data->name)) { case ID_ME: return BKE_mesh_material_index_used((Mesh *)ob_data, actcol - 1); - case ID_CU: + case ID_CU_LEGACY: return BKE_curve_material_index_used((Curve *)ob_data, actcol - 1); case ID_MB: /* Meta-elements don't support materials at the moment. */ @@ -489,7 +489,7 @@ static void material_data_index_clear_id(ID *id) case ID_ME: BKE_mesh_material_index_clear((Mesh *)id); break; - case ID_CU: + case ID_CU_LEGACY: BKE_curve_material_index_clear((Curve *)id); break; case ID_MB: @@ -1062,7 +1062,7 @@ void BKE_object_material_remap(Object *ob, const unsigned int *remap) if (ob->type == OB_MESH) { BKE_mesh_material_remap(ob->data, remap, ob->totcol); } - else if (ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF, OB_FONT)) { BKE_curve_material_remap(ob->data, remap, ob->totcol); } else if (ob->type == OB_GPENCIL) { @@ -1314,7 +1314,7 @@ bool BKE_object_material_slot_remove(Main *bmain, Object *ob) } /* check indices from mesh */ - if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT)) { + if (ELEM(ob->type, OB_MESH, OB_CURVES_LEGACY, OB_SURF, OB_FONT)) { material_data_index_remove_id((ID *)ob->data, actcol - 1); if (ob->runtime.curve_cache) { BKE_displist_free(&ob->runtime.curve_cache->disp); diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc index 6ca5babbf13..37564f9334f 100644 --- a/source/blender/blenkernel/intern/mesh.cc +++ b/source/blender/blenkernel/intern/mesh.cc @@ -59,6 +59,8 @@ #include "BLO_read_write.h" +using blender::float3; + static void mesh_clear_geometry(Mesh *mesh); static void mesh_tessface_clear_intern(Mesh *mesh, int free_customdata); @@ -1106,18 +1108,6 @@ Mesh *BKE_mesh_new_nomain_from_template_ex(const Mesh *me_src, mesh_tessface_clear_intern(me_dst, false); } - me_dst->runtime.cd_dirty_poly = me_src->runtime.cd_dirty_poly; - me_dst->runtime.cd_dirty_vert = me_src->runtime.cd_dirty_vert; - - /* Ensure that when no normal layers exist, they are marked dirty, because - * normals might not have been included in the mask of copied layers. */ - if (!CustomData_has_layer(&me_dst->vdata, CD_NORMAL)) { - me_dst->runtime.cd_dirty_vert |= CD_MASK_NORMAL; - } - if (!CustomData_has_layer(&me_dst->pdata, CD_NORMAL)) { - me_dst->runtime.cd_dirty_poly |= CD_MASK_NORMAL; - } - /* The destination mesh should at least have valid primary CD layers, * even in cases where the source mesh does not. */ mesh_ensure_cdlayers_primary(me_dst, do_tessface); @@ -1204,6 +1194,23 @@ Mesh *BKE_mesh_from_bmesh_for_eval_nomain(BMesh *bm, return mesh; } +static void ensure_orig_index_layer(CustomData &data, const int size) +{ + if (CustomData_has_layer(&data, CD_ORIGINDEX)) { + return; + } + int *indices = (int *)CustomData_add_layer(&data, CD_ORIGINDEX, CD_DEFAULT, nullptr, size); + range_vn_i(indices, size, 0); +} + +void BKE_mesh_ensure_default_orig_index_customdata(Mesh *mesh) +{ + BLI_assert(mesh->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA); + ensure_orig_index_layer(mesh->vdata, mesh->totvert); + ensure_orig_index_layer(mesh->edata, mesh->totedge); + ensure_orig_index_layer(mesh->pdata, mesh->totpoly); +} + BoundBox *BKE_mesh_boundbox_get(Object *ob) { /* This is Object-level data access, @@ -1968,8 +1975,6 @@ void BKE_mesh_calc_normals_split_ex(Mesh *mesh, MLoopNorSpaceArray *r_lnors_spac nullptr); BKE_mesh_assert_normals_dirty_or_calculated(mesh); - - mesh->runtime.cd_dirty_loop &= ~CD_MASK_NORMAL; } void BKE_mesh_calc_normals_split(Mesh *mesh) @@ -2147,6 +2152,10 @@ static void split_faces_split_new_verts(Mesh *mesh, MVert *mvert = mesh->mvert; float(*vert_normals)[3] = BKE_mesh_vertex_normals_for_write(mesh); + /* Normals were already calculated at the beginning of this operation, we rely on that to update + * them partially here. */ + BLI_assert(!BKE_mesh_vertex_normals_are_dirty(mesh)); + /* Remember new_verts is a single linklist, so its items are in reversed order... */ MVert *new_mv = &mvert[mesh->totvert - 1]; for (int i = mesh->totvert - 1; i >= verts_len; i--, new_mv--, new_verts = new_verts->next) { @@ -2157,7 +2166,6 @@ static void split_faces_split_new_verts(Mesh *mesh, copy_v3_v3(vert_normals[i], new_verts->vnor); } } - BKE_mesh_vertex_normals_clear_dirty(mesh); } /* Perform actual split of edges. */ @@ -2228,6 +2236,10 @@ void BKE_mesh_split_faces(Mesh *mesh, bool free_loop_normals) /* Update pointers to a newly allocated memory. */ BKE_mesh_update_customdata_pointers(mesh, false); + /* Update normals manually to avoid recalculation after this operation. */ + mesh->runtime.vert_normals = (float(*)[3])MEM_reallocN(mesh->runtime.vert_normals, + sizeof(float[3]) * mesh->totvert); + /* Perform actual split of vertices and edges. */ split_faces_split_new_verts(mesh, new_verts, num_new_verts); if (do_edges) { diff --git a/source/blender/blenkernel/intern/mesh_boolean_convert.cc b/source/blender/blenkernel/intern/mesh_boolean_convert.cc index ec66cd0d84d..eee1d3b9eec 100644 --- a/source/blender/blenkernel/intern/mesh_boolean_convert.cc +++ b/source/blender/blenkernel/intern/mesh_boolean_convert.cc @@ -292,7 +292,7 @@ static IMesh meshes_to_imesh(Span<const Mesh *> meshes, r_info->mesh_edge_offset[mi] = e; r_info->mesh_poly_offset[mi] = f; /* Get matrix that transforms a coordinate in objects[mi]'s local space - * to the target space space. */ + * to the target space. */ const float4x4 objn_mat = (obmats[mi] == nullptr) ? float4x4::identity() : clean_obmat(*obmats[mi]); r_info->to_target_transform[mi] = inv_target_mat * objn_mat; diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc index 86e5f2b3cfe..1542f7119d1 100644 --- a/source/blender/blenkernel/intern/mesh_convert.cc +++ b/source/blender/blenkernel/intern/mesh_convert.cc @@ -739,14 +739,14 @@ void BKE_mesh_to_curve(Main *bmain, Depsgraph *depsgraph, Scene *UNUSED(scene), BKE_mesh_to_curve_nurblist(me_eval, &nurblist, 1); if (nurblist.first) { - Curve *cu = BKE_curve_add(bmain, ob->id.name + 2, OB_CURVE); + Curve *cu = BKE_curve_add(bmain, ob->id.name + 2, OB_CURVES_LEGACY); cu->flag |= CU_3D; cu->nurb = nurblist; id_us_min(&((Mesh *)ob->data)->id); ob->data = cu; - ob->type = OB_CURVE; + ob->type = OB_CURVES_LEGACY; BKE_object_free_derived_caches(ob); } @@ -886,7 +886,7 @@ static void object_for_curve_to_mesh_free(Object *temp_object) { /* Clear edit mode pointers that were explicitly copied to the temporary curve. */ ID *final_object_data = static_cast<ID *>(temp_object->data); - if (GS(final_object_data->name) == ID_CU) { + if (GS(final_object_data->name) == ID_CU_LEGACY) { Curve &curve = *reinterpret_cast<Curve *>(final_object_data); curve.editfont = nullptr; curve.editnurb = nullptr; @@ -901,7 +901,7 @@ static void object_for_curve_to_mesh_free(Object *temp_object) */ static void curve_to_mesh_eval_ensure(Object &object) { - BLI_assert(GS(static_cast<ID *>(object.data)->name) == ID_CU); + BLI_assert(GS(static_cast<ID *>(object.data)->name) == ID_CU_LEGACY); Curve &curve = *static_cast<Curve *>(object.data); /* Clear all modifiers for the bevel object. * @@ -953,11 +953,11 @@ static const Mesh *get_evaluated_mesh_from_object(const Object *object) return nullptr; } -static const CurveEval *get_evaluated_curve_from_object(const Object *object) +static const Curves *get_evaluated_curves_from_object(const Object *object) { GeometrySet *geometry_set_eval = object->runtime.geometry_set_eval; if (geometry_set_eval) { - return geometry_set_eval->get_curve_for_read(); + return geometry_set_eval->get_curves_for_read(); } return nullptr; } @@ -968,8 +968,9 @@ static Mesh *mesh_new_from_evaluated_curve_type_object(const Object *evaluated_o if (mesh) { return BKE_mesh_copy_for_eval(mesh, false); } - const CurveEval *curve = get_evaluated_curve_from_object(evaluated_object); - if (curve) { + const Curves *curves = get_evaluated_curves_from_object(evaluated_object); + if (curves) { + std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curves); return blender::bke::curve_to_wire_mesh(*curve); } return nullptr; @@ -1110,7 +1111,7 @@ Mesh *BKE_mesh_new_from_object(Depsgraph *depsgraph, Mesh *new_mesh = nullptr; switch (object->type) { case OB_FONT: - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: new_mesh = mesh_new_from_curve_type_object(object); break; @@ -1182,7 +1183,7 @@ Mesh *BKE_mesh_new_from_object_to_bmain(Main *bmain, Object *object, bool preserve_all_data_layers) { - BLI_assert(ELEM(object->type, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL, OB_MESH)); + BLI_assert(ELEM(object->type, OB_FONT, OB_CURVES_LEGACY, OB_SURF, OB_MBALL, OB_MESH)); Mesh *mesh = BKE_mesh_new_from_object(depsgraph, object, preserve_all_data_layers, false); if (mesh == nullptr) { @@ -1222,6 +1223,9 @@ Mesh *BKE_mesh_new_from_object_to_bmain(Main *bmain, BKE_mesh_nomain_to_mesh(mesh, mesh_in_bmain, nullptr, &CD_MASK_MESH, true); + /* Anonymous attributes shouldn't exist on original data. */ + BKE_mesh_anonymous_attributes_remove(mesh_in_bmain); + /* User-count is required because so far mesh was in a limbo, where library management does * not perform any user management (i.e. copy of a mesh will not increase users of materials). */ BKE_library_foreach_ID_link( @@ -1478,17 +1482,8 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, tmp.cd_flag = mesh_src->cd_flag; tmp.runtime.deformed_only = mesh_src->runtime.deformed_only; - tmp.runtime.cd_dirty_poly = mesh_src->runtime.cd_dirty_poly; - tmp.runtime.cd_dirty_vert = mesh_src->runtime.cd_dirty_vert; - - /* Ensure that when no normal layers exist, they are marked dirty, because - * normals might not have been included in the mask of copied layers. */ - if (!CustomData_has_layer(&tmp.vdata, CD_NORMAL)) { - tmp.runtime.cd_dirty_vert |= CD_MASK_NORMAL; - } - if (!CustomData_has_layer(&tmp.pdata, CD_NORMAL)) { - tmp.runtime.cd_dirty_poly |= CD_MASK_NORMAL; - } + /* Clear the normals completely, since the new vertex / polygon count might be different. */ + BKE_mesh_clear_derived_normals(&tmp); if (CustomData_has_layer(&mesh_src->vdata, CD_SHAPEKEY)) { KeyBlock *kb; diff --git a/source/blender/blenkernel/intern/mesh_normals.cc b/source/blender/blenkernel/intern/mesh_normals.cc index 3d801b301f9..1c2a903d8c3 100644 --- a/source/blender/blenkernel/intern/mesh_normals.cc +++ b/source/blender/blenkernel/intern/mesh_normals.cc @@ -94,53 +94,72 @@ static void add_v3_v3_atomic(float r[3], const float a[3]) void BKE_mesh_normals_tag_dirty(Mesh *mesh) { - mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL; - mesh->runtime.cd_dirty_poly |= CD_MASK_NORMAL; + mesh->runtime.vert_normals_dirty = true; + mesh->runtime.poly_normals_dirty = true; } float (*BKE_mesh_vertex_normals_for_write(Mesh *mesh))[3] { - CustomData_duplicate_referenced_layer(&mesh->vdata, CD_NORMAL, mesh->totvert); - return (float(*)[3])CustomData_add_layer( - &mesh->vdata, CD_NORMAL, CD_CALLOC, nullptr, mesh->totvert); + if (mesh->runtime.vert_normals == nullptr) { + mesh->runtime.vert_normals = (float(*)[3])MEM_malloc_arrayN( + mesh->totvert, sizeof(float[3]), __func__); + } + + BLI_assert(MEM_allocN_len(mesh->runtime.vert_normals) >= sizeof(float[3]) * mesh->totvert); + + return mesh->runtime.vert_normals; } float (*BKE_mesh_poly_normals_for_write(Mesh *mesh))[3] { - CustomData_duplicate_referenced_layer(&mesh->pdata, CD_NORMAL, mesh->totpoly); - return (float(*)[3])CustomData_add_layer( - &mesh->pdata, CD_NORMAL, CD_CALLOC, nullptr, mesh->totpoly); + if (mesh->runtime.poly_normals == nullptr) { + mesh->runtime.poly_normals = (float(*)[3])MEM_malloc_arrayN( + mesh->totpoly, sizeof(float[3]), __func__); + } + + BLI_assert(MEM_allocN_len(mesh->runtime.poly_normals) >= sizeof(float[3]) * mesh->totpoly); + + return mesh->runtime.poly_normals; } void BKE_mesh_vertex_normals_clear_dirty(Mesh *mesh) { - mesh->runtime.cd_dirty_vert &= ~CD_MASK_NORMAL; + mesh->runtime.vert_normals_dirty = false; BKE_mesh_assert_normals_dirty_or_calculated(mesh); } void BKE_mesh_poly_normals_clear_dirty(Mesh *mesh) { - mesh->runtime.cd_dirty_poly &= ~CD_MASK_NORMAL; + mesh->runtime.poly_normals_dirty = false; BKE_mesh_assert_normals_dirty_or_calculated(mesh); } bool BKE_mesh_vertex_normals_are_dirty(const Mesh *mesh) { - return mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL; + return mesh->runtime.vert_normals_dirty; } bool BKE_mesh_poly_normals_are_dirty(const Mesh *mesh) { - return mesh->runtime.cd_dirty_poly & CD_MASK_NORMAL; + return mesh->runtime.poly_normals_dirty; +} + +void BKE_mesh_clear_derived_normals(Mesh *mesh) +{ + MEM_SAFE_FREE(mesh->runtime.vert_normals); + MEM_SAFE_FREE(mesh->runtime.poly_normals); + + mesh->runtime.vert_normals_dirty = true; + mesh->runtime.poly_normals_dirty = true; } void BKE_mesh_assert_normals_dirty_or_calculated(const Mesh *mesh) { - if (!(mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL)) { - BLI_assert(CustomData_has_layer(&mesh->vdata, CD_NORMAL) || mesh->totvert == 0); + if (!mesh->runtime.vert_normals_dirty) { + BLI_assert(mesh->runtime.vert_normals || mesh->totvert == 0); } - if (!(mesh->runtime.cd_dirty_poly & CD_MASK_NORMAL)) { - BLI_assert(CustomData_has_layer(&mesh->pdata, CD_NORMAL) || mesh->totpoly == 0); + if (!mesh->runtime.poly_normals_dirty) { + BLI_assert(mesh->runtime.poly_normals || mesh->totpoly == 0); } } @@ -201,14 +220,13 @@ void BKE_mesh_calc_normals_poly(const MVert *mvert, * \{ */ struct MeshCalcNormalsData_PolyAndVertex { - /** Write into vertex normals #MVert.no. */ - MVert *mvert; + const MVert *mvert; const MLoop *mloop; const MPoly *mpoly; /** Polygon normal output. */ float (*pnors)[3]; - /** Vertex normal output (may be freed, copied into #MVert.no). */ + /** Vertex normal output. */ float (*vnors)[3]; }; @@ -279,7 +297,7 @@ static void mesh_calc_normals_poly_and_vertex_finalize_fn( { MeshCalcNormalsData_PolyAndVertex *data = (MeshCalcNormalsData_PolyAndVertex *)userdata; - MVert *mv = &data->mvert[vidx]; + const MVert *mv = &data->mvert[vidx]; float *no = data->vnors[vidx]; if (UNLIKELY(normalize_v3(no) == 0.0f)) { @@ -288,7 +306,7 @@ static void mesh_calc_normals_poly_and_vertex_finalize_fn( } } -static void mesh_calc_normals_poly_and_vertex(MVert *mvert, +static void mesh_calc_normals_poly_and_vertex(const MVert *mvert, const int mvert_len, const MLoop *mloop, const int UNUSED(mloop_len), @@ -301,36 +319,22 @@ static void mesh_calc_normals_poly_and_vertex(MVert *mvert, BLI_parallel_range_settings_defaults(&settings); settings.min_iter_per_thread = 1024; - float(*vnors)[3] = r_vert_normals; - bool free_vnors = false; - - /* First go through and calculate normals for all the polys. */ - if (vnors == nullptr) { - vnors = (float(*)[3])MEM_calloc_arrayN((size_t)mvert_len, sizeof(*vnors), __func__); - free_vnors = true; - } - else { - memset(vnors, 0, sizeof(*vnors) * (size_t)mvert_len); - } + memset(r_vert_normals, 0, sizeof(*r_vert_normals) * (size_t)mvert_len); MeshCalcNormalsData_PolyAndVertex data = {}; data.mpoly = mpoly; data.mloop = mloop; data.mvert = mvert; data.pnors = r_poly_normals; - data.vnors = vnors; + data.vnors = r_vert_normals; - /* Compute poly normals (`pnors`), accumulating them into vertex normals (`vnors`). */ + /* Compute poly normals, accumulating them into vertex normals. */ BLI_task_parallel_range( 0, mpoly_len, &data, mesh_calc_normals_poly_and_vertex_accum_fn, &settings); - /* Normalize and validate computed vertex normals (`vnors`). */ + /* Normalize and validate computed vertex normals. */ BLI_task_parallel_range( 0, mvert_len, &data, mesh_calc_normals_poly_and_vertex_finalize_fn, &settings); - - if (free_vnors) { - MEM_freeN(vnors); - } } /** \} */ @@ -342,8 +346,8 @@ static void mesh_calc_normals_poly_and_vertex(MVert *mvert, const float (*BKE_mesh_vertex_normals_ensure(const Mesh *mesh))[3] { if (!(BKE_mesh_vertex_normals_are_dirty(mesh) || BKE_mesh_poly_normals_are_dirty(mesh))) { - BLI_assert(CustomData_has_layer(&mesh->vdata, CD_NORMAL) || mesh->totvert == 0); - return (const float(*)[3])CustomData_get_layer(&mesh->vdata, CD_NORMAL); + BLI_assert(mesh->runtime.vert_normals != nullptr || mesh->totvert == 0); + return mesh->runtime.vert_normals; } if (mesh->totvert == 0) { @@ -353,9 +357,9 @@ const float (*BKE_mesh_vertex_normals_ensure(const Mesh *mesh))[3] ThreadMutex *normals_mutex = (ThreadMutex *)mesh->runtime.normals_mutex; BLI_mutex_lock(normals_mutex); if (!(BKE_mesh_vertex_normals_are_dirty(mesh) || BKE_mesh_poly_normals_are_dirty(mesh))) { - BLI_assert(CustomData_has_layer(&mesh->vdata, CD_NORMAL)); + BLI_assert(mesh->runtime.vert_normals != nullptr); BLI_mutex_unlock(normals_mutex); - return (const float(*)[3])CustomData_get_layer(&mesh->vdata, CD_NORMAL); + return mesh->runtime.vert_normals; } float(*vert_normals)[3]; @@ -388,8 +392,8 @@ const float (*BKE_mesh_vertex_normals_ensure(const Mesh *mesh))[3] const float (*BKE_mesh_poly_normals_ensure(const Mesh *mesh))[3] { if (!BKE_mesh_poly_normals_are_dirty(mesh)) { - BLI_assert(CustomData_has_layer(&mesh->pdata, CD_NORMAL) || mesh->totpoly == 0); - return (const float(*)[3])CustomData_get_layer(&mesh->pdata, CD_NORMAL); + BLI_assert(mesh->runtime.poly_normals != nullptr || mesh->totpoly == 0); + return mesh->runtime.poly_normals; } if (mesh->totpoly == 0) { @@ -399,9 +403,9 @@ const float (*BKE_mesh_poly_normals_ensure(const Mesh *mesh))[3] ThreadMutex *normals_mutex = (ThreadMutex *)mesh->runtime.normals_mutex; BLI_mutex_lock(normals_mutex); if (!BKE_mesh_poly_normals_are_dirty(mesh)) { - BLI_assert(CustomData_has_layer(&mesh->pdata, CD_NORMAL)); + BLI_assert(mesh->runtime.poly_normals != nullptr); BLI_mutex_unlock(normals_mutex); - return (const float(*)[3])CustomData_get_layer(&mesh->pdata, CD_NORMAL); + return mesh->runtime.poly_normals; } float(*poly_normals)[3]; diff --git a/source/blender/blenkernel/intern/mesh_runtime.c b/source/blender/blenkernel/intern/mesh_runtime.c index 204441d5326..7bd52abeb0d 100644 --- a/source/blender/blenkernel/intern/mesh_runtime.c +++ b/source/blender/blenkernel/intern/mesh_runtime.c @@ -88,6 +88,11 @@ void BKE_mesh_runtime_reset_on_copy(Mesh *mesh, const int UNUSED(flag)) runtime->bvh_cache = NULL; runtime->shrinkwrap_data = NULL; + runtime->vert_normals_dirty = true; + runtime->poly_normals_dirty = true; + runtime->vert_normals = NULL; + runtime->poly_normals = NULL; + mesh_runtime_init_mutexes(mesh); } @@ -101,6 +106,7 @@ void BKE_mesh_runtime_clear_cache(Mesh *mesh) BKE_mesh_runtime_clear_geometry(mesh); BKE_mesh_batch_cache_free(mesh); BKE_mesh_runtime_clear_edit_data(mesh); + BKE_mesh_clear_derived_normals(mesh); } /** diff --git a/source/blender/blenkernel/intern/mesh_tessellate.c b/source/blender/blenkernel/intern/mesh_tessellate.c index 96b588779f8..ae52e31cb9b 100644 --- a/source/blender/blenkernel/intern/mesh_tessellate.c +++ b/source/blender/blenkernel/intern/mesh_tessellate.c @@ -144,8 +144,7 @@ int BKE_mesh_tessface_calc_ex(CustomData *fdata, MVert *mvert, int totface, int totloop, - int totpoly, - const bool do_face_nor_copy) + int totpoly) { #define USE_TESSFACE_SPEEDUP #define USE_TESSFACE_QUADS @@ -347,18 +346,6 @@ int BKE_mesh_tessface_calc_ex(CustomData *fdata, CustomData_add_layer(fdata, CD_ORIGINDEX, CD_ASSIGN, mface_to_poly_map, totface); CustomData_from_bmeshpoly(fdata, ldata, totface); - if (do_face_nor_copy) { - /* If polys have a normals layer, copying that to faces can help - * avoid the need to recalculate normals later. */ - if (CustomData_has_layer(pdata, CD_NORMAL)) { - float(*pnors)[3] = CustomData_get_layer(pdata, CD_NORMAL); - float(*fnors)[3] = CustomData_add_layer(fdata, CD_NORMAL, CD_CALLOC, NULL, totface); - for (mface_index = 0; mface_index < totface; mface_index++) { - copy_v3_v3(fnors[mface_index], pnors[mface_to_poly_map[mface_index]]); - } - } - } - /* NOTE: quad detection issue - fourth vertidx vs fourth loopidx: * Polygons take care of their loops ordering, hence not of their vertices ordering. * Currently, our tfaces' fourth vertex index might be 0 even for a quad. @@ -395,16 +382,13 @@ int BKE_mesh_tessface_calc_ex(CustomData *fdata, void BKE_mesh_tessface_calc(Mesh *mesh) { - mesh->totface = BKE_mesh_tessface_calc_ex( - &mesh->fdata, - &mesh->ldata, - &mesh->pdata, - mesh->mvert, - mesh->totface, - mesh->totloop, - mesh->totpoly, - /* Calculate normals right after, don't copy from polys here. */ - false); + mesh->totface = BKE_mesh_tessface_calc_ex(&mesh->fdata, + &mesh->ldata, + &mesh->pdata, + mesh->mvert, + mesh->totface, + mesh->totloop, + mesh->totpoly); BKE_mesh_update_customdata_pointers(mesh, true); } diff --git a/source/blender/blenkernel/intern/mesh_validate.c b/source/blender/blenkernel/intern/mesh_validate.cc index 53e19e6d16d..fb526354305 100644 --- a/source/blender/blenkernel/intern/mesh_validate.c +++ b/source/blender/blenkernel/intern/mesh_validate.cc @@ -5,10 +5,10 @@ * \ingroup bke */ -#include <limits.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> +#include <climits> +#include <cstdio> +#include <cstdlib> +#include <cstring> #include "CLG_log.h" @@ -41,25 +41,25 @@ static CLG_LogRef LOG = {"bke.mesh"}; /** \name Internal functions * \{ */ -typedef union { +union EdgeUUID { uint32_t verts[2]; int64_t edval; -} EdgeUUID; +}; -typedef struct SortFace { +struct SortFace { EdgeUUID es[4]; uint index; -} SortFace; +}; /* Used to detect polys (faces) using exactly the same vertices. */ /* Used to detect loops used by no (disjoint) or more than one (intersect) polys. */ -typedef struct SortPoly { +struct SortPoly { int *verts; int numverts; int loopstart; uint index; bool invalid; /* Poly index. */ -} SortPoly; +}; static void edge_store_assign(uint32_t verts[2], const uint32_t v1, const uint32_t v2) { @@ -106,7 +106,8 @@ static int int64_cmp(const void *v1, const void *v2) static int search_face_cmp(const void *v1, const void *v2) { - const SortFace *sfa = v1, *sfb = v2; + const SortFace *sfa = static_cast<const SortFace *>(v1); + const SortFace *sfb = static_cast<const SortFace *>(v2); if (sfa->es[0].edval > sfb->es[0].edval) { return 1; @@ -147,8 +148,8 @@ static int int_cmp(const void *v1, const void *v2) static int search_poly_cmp(const void *v1, const void *v2) { - const SortPoly *sp1 = v1; - const SortPoly *sp2 = v2; + const SortPoly *sp1 = static_cast<const SortPoly *>(v1); + const SortPoly *sp2 = static_cast<const SortPoly *>(v2); /* Reject all invalid polys at end of list! */ if (sp1->invalid || sp2->invalid) { @@ -168,8 +169,8 @@ static int search_poly_cmp(const void *v1, const void *v2) static int search_polyloop_cmp(const void *v1, const void *v2) { - const SortPoly *sp1 = v1; - const SortPoly *sp2 = v2; + const SortPoly *sp1 = static_cast<const SortPoly *>(v1); + const SortPoly *sp2 = static_cast<const SortPoly *>(v2); /* Reject all invalid polys at end of list! */ if (sp1->invalid || sp2->invalid) { @@ -275,7 +276,7 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, EdgeHash *edge_hash = BLI_edgehash_new_ex(__func__, totedge); - BLI_assert(!(do_fixes && mesh == NULL)); + BLI_assert(!(do_fixes && mesh == nullptr)); fix_flag.as_flag = 0; free_flag.as_flag = 0; @@ -288,7 +289,7 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, recalc_flag.edges = do_fixes; } - const float(*vert_normals)[3] = NULL; + const float(*vert_normals)[3] = nullptr; BKE_mesh_assert_normals_dirty_or_calculated(mesh); if (!BKE_mesh_vertex_normals_are_dirty(mesh)) { vert_normals = BKE_mesh_vertex_normals_ensure(mesh); @@ -394,7 +395,7 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, MFace *mf; MFace *mf_prev; - SortFace *sort_faces = MEM_callocN(sizeof(SortFace) * totface, "search faces"); + SortFace *sort_faces = (SortFace *)MEM_callocN(sizeof(SortFace) * totface, "search faces"); SortFace *sf; SortFace *sf_prev; uint totsortface = 0; @@ -547,7 +548,8 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, { BLI_bitmap *vert_tag = BLI_BITMAP_NEW(mesh->totvert, __func__); - SortPoly *sort_polys = MEM_callocN(sizeof(SortPoly) * totpoly, "mesh validate's sort_polys"); + SortPoly *sort_polys = (SortPoly *)MEM_callocN(sizeof(SortPoly) * totpoly, + "mesh validate's sort_polys"); SortPoly *prev_sp, *sp = sort_polys; int prev_end; @@ -586,7 +588,7 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, /* Poly itself is valid, for now. */ int v1, v2; /* v1 is prev loop vert idx, v2 is current loop one. */ sp->invalid = false; - sp->verts = v = MEM_mallocN(sizeof(int) * mp->totloop, "Vert idx of SortPoly"); + sp->verts = v = (int *)MEM_mallocN(sizeof(int) * mp->totloop, "Vert idx of SortPoly"); sp->numverts = mp->totloop; sp->loopstart = mp->loopstart; @@ -725,7 +727,7 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, /* Third check pass, testing loops used by none or more than one poly. */ qsort(sort_polys, totpoly, sizeof(SortPoly), search_polyloop_cmp); sp = sort_polys; - prev_sp = NULL; + prev_sp = nullptr; prev_end = 0; for (i = 0; i < totpoly; i++, sp++) { /* Free this now, we don't need it anymore, and avoid us another loop! */ @@ -733,7 +735,8 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, MEM_freeN(sp->verts); } - /* Note above prev_sp: in following code, we make sure it is always valid poly (or NULL). */ + /* Note above prev_sp: in following code, we make sure it is always valid poly (or nullptr). + */ if (sp->invalid) { if (do_fixes) { REMOVE_POLY_TAG((&mpolys[sp->index])); @@ -792,7 +795,7 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, MEM_freeN(sort_polys); } - BLI_edgehash_free(edge_hash, NULL); + BLI_edgehash_free(edge_hash, nullptr); /* fix deform verts */ if (dverts) { @@ -907,7 +910,7 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, if (free_flag.mselect) { MEM_freeN(mesh->mselect); - mesh->mselect = NULL; + mesh->mselect = nullptr; mesh->totselect = 0; } } @@ -1192,7 +1195,7 @@ void BKE_mesh_strip_loose_polysloops(Mesh *me) MLoop *l; int a, b; /* New loops idx! */ - int *new_idx = MEM_mallocN(sizeof(int) * me->totloop, __func__); + int *new_idx = (int *)MEM_mallocN(sizeof(int) * me->totloop, __func__); for (a = b = 0, p = me->mpoly; a < me->totpoly; a++, p++) { bool invalid = false; @@ -1262,7 +1265,7 @@ void BKE_mesh_strip_loose_edges(Mesh *me) MEdge *e; MLoop *l; int a, b; - uint *new_idx = MEM_mallocN(sizeof(int) * me->totedge, __func__); + uint *new_idx = (uint *)MEM_mallocN(sizeof(int) * me->totedge, __func__); for (a = b = 0, e = me->medge; a < me->totedge; a++, e++) { if (e->v1 != e->v2) { @@ -1322,7 +1325,8 @@ static void to_edgesort(struct EdgeSort *ed, uint v1, uint v2, char is_loose, sh static int vergedgesort(const void *v1, const void *v2) { - const struct EdgeSort *x1 = v1, *x2 = v2; + const struct EdgeSort *x1 = static_cast<const struct EdgeSort *>(v1); + const struct EdgeSort *x2 = static_cast<const struct EdgeSort *>(v2); if (x1->v1 > x2->v1) { return 1; @@ -1380,12 +1384,12 @@ static void mesh_calc_edges_mdata(MVert *UNUSED(allvert), if (totedge == 0) { /* flag that mesh has edges */ - (*r_medge) = MEM_callocN(0, __func__); + (*r_medge) = (MEdge *)MEM_callocN(0, __func__); (*r_totedge) = 0; return; } - ed = edsort = MEM_mallocN(totedge * sizeof(struct EdgeSort), "EdgeSort"); + ed = edsort = (EdgeSort *)MEM_mallocN(totedge * sizeof(struct EdgeSort), "EdgeSort"); for (a = totface, mface = allface; a > 0; a--, mface++) { to_edgesort(ed++, mface->v1, mface->v2, !mface->v3, mface->edcode & ME_V1V2); @@ -1411,7 +1415,7 @@ static void mesh_calc_edges_mdata(MVert *UNUSED(allvert), } totedge_final++; - medge = MEM_callocN(sizeof(MEdge) * totedge_final, __func__); + medge = (MEdge *)MEM_callocN(sizeof(MEdge) * totedge_final, __func__); for (a = totedge, med = medge, ed = edsort; a > 1; a--, ed++) { /* edge is unique when it differs from next edge, or is last */ @@ -1433,7 +1437,7 @@ static void mesh_calc_edges_mdata(MVert *UNUSED(allvert), med++; } else { - /* equal edge, we merge the drawflag */ + /* Equal edge, merge the draw-flag. */ (ed + 1)->is_draw |= ed->is_draw; } } @@ -1469,7 +1473,7 @@ static void mesh_calc_edges_mdata(MVert *UNUSED(allvert), } } - BLI_edgehash_free(hash, NULL); + BLI_edgehash_free(hash, nullptr); *r_medge = medge; *r_totedge = totedge_final; @@ -1499,7 +1503,7 @@ void BKE_mesh_calc_edges_legacy(Mesh *me, const bool use_old) return; } - medge = CustomData_add_layer(&me->edata, CD_MEDGE, CD_ASSIGN, medge, totedge); + medge = (MEdge *)CustomData_add_layer(&me->edata, CD_MEDGE, CD_ASSIGN, medge, totedge); me->medge = medge; me->totedge = totedge; @@ -1548,11 +1552,11 @@ void BKE_mesh_calc_edges_tessface(Mesh *mesh) /* write new edges into a temporary CustomData */ CustomData edgeData; CustomData_reset(&edgeData); - CustomData_add_layer(&edgeData, CD_MEDGE, CD_CALLOC, NULL, numEdges); - CustomData_add_layer(&edgeData, CD_ORIGINDEX, CD_CALLOC, NULL, numEdges); + CustomData_add_layer(&edgeData, CD_MEDGE, CD_CALLOC, nullptr, numEdges); + CustomData_add_layer(&edgeData, CD_ORIGINDEX, CD_CALLOC, nullptr, numEdges); - MEdge *med = CustomData_get_layer(&edgeData, CD_MEDGE); - int *index = CustomData_get_layer(&edgeData, CD_ORIGINDEX); + MEdge *med = (MEdge *)CustomData_get_layer(&edgeData, CD_MEDGE); + int *index = (int *)CustomData_get_layer(&edgeData, CD_ORIGINDEX); EdgeSetIterator *ehi = BLI_edgesetIterator_new(eh); for (int i = 0; BLI_edgesetIterator_isDone(ehi) == false; @@ -1569,7 +1573,7 @@ void BKE_mesh_calc_edges_tessface(Mesh *mesh) mesh->edata = edgeData; mesh->totedge = numEdges; - mesh->medge = CustomData_get_layer(&mesh->edata, CD_MEDGE); + mesh->medge = (MEdge *)CustomData_get_layer(&mesh->edata, CD_MEDGE); BLI_edgeset_free(eh); } diff --git a/source/blender/blenkernel/intern/mesh_wrapper.c b/source/blender/blenkernel/intern/mesh_wrapper.c index 267020fb675..f9fcaa0dceb 100644 --- a/source/blender/blenkernel/intern/mesh_wrapper.c +++ b/source/blender/blenkernel/intern/mesh_wrapper.c @@ -115,6 +115,16 @@ static void mesh_wrapper_ensure_mdata_isolated(void *userdata) BMEditMesh *em = me->edit_mesh; BM_mesh_bm_to_me_for_eval(em->bm, me, &me->runtime.cd_mask_extra); + /* Adding original index layers assumes that all BMesh mesh wrappers are created from + * original edit mode meshes (the only case where adding original indices makes sense). + * If that assumption is broken, the layers might be incorrect in that they might not + * actually be "original". + * + * There is also a performance aspect, where this also assumes that original indices are + * always needed when converting an edit mesh to a mesh. That might be wrong, but it's not + * harmful. */ + BKE_mesh_ensure_default_orig_index_customdata(me); + EditMeshData *edit_data = me->runtime.edit_data; if (edit_data->vertexCos) { BKE_mesh_vert_coords_apply(me, edit_data->vertexCos); diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c index 46fea5ae115..5af8dfc2b72 100644 --- a/source/blender/blenkernel/intern/modifier.c +++ b/source/blender/blenkernel/intern/modifier.c @@ -131,7 +131,7 @@ void BKE_modifier_panel_expand(ModifierData *md) /***/ -ModifierData *BKE_modifier_new(int type) +static ModifierData *modifier_allocate_and_init(int type) { const ModifierTypeInfo *mti = BKE_modifier_get_info(type); ModifierData *md = MEM_callocN(mti->structSize, mti->structName); @@ -152,6 +152,13 @@ ModifierData *BKE_modifier_new(int type) mti->initData(md); } + return md; +} + +ModifierData *BKE_modifier_new(int type) +{ + ModifierData *md = modifier_allocate_and_init(type); + BKE_modifier_session_uuid_generate(md); return md; @@ -315,6 +322,16 @@ void BKE_modifiers_foreach_tex_link(Object *ob, TexWalkFunc walk, void *userData } } +ModifierData *BKE_modifier_copy_ex(const ModifierData *md, int flag) +{ + ModifierData *md_dst = modifier_allocate_and_init(md->type); + + BLI_strncpy(md_dst->name, md->name, sizeof(md_dst->name)); + BKE_modifier_copydata_ex(md, md_dst, flag); + + return md_dst; +} + void BKE_modifier_copydata_generic(const ModifierData *md_src, ModifierData *md_dst, const int UNUSED(flag)) @@ -348,7 +365,7 @@ static void modifier_copy_data_id_us_cb(void *UNUSED(userData), } } -void BKE_modifier_copydata_ex(ModifierData *md, ModifierData *target, const int flag) +void BKE_modifier_copydata_ex(const ModifierData *md, ModifierData *target, const int flag) { const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); @@ -378,7 +395,7 @@ void BKE_modifier_copydata_ex(ModifierData *md, ModifierData *target, const int } } -void BKE_modifier_copydata(ModifierData *md, ModifierData *target) +void BKE_modifier_copydata(const ModifierData *md, ModifierData *target) { BKE_modifier_copydata_ex(md, target, 0); } @@ -651,7 +668,7 @@ ModifierData *BKE_modifiers_get_virtual_modifierlist(const Object *ob, virtualModifierData->amd.deformflag = ((bArmature *)(ob->parent->data))->deformflag; md = &virtualModifierData->amd.modifier; } - else if (ob->parent->type == OB_CURVE && ob->partype == PARSKEL) { + else if (ob->parent->type == OB_CURVES_LEGACY && ob->partype == PARSKEL) { virtualModifierData->cmd.object = ob->parent; virtualModifierData->cmd.defaxis = ob->trackflag + 1; virtualModifierData->cmd.modifier.next = md; diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index c8e82302787..96bfcb0311b 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -1562,13 +1562,15 @@ static void socket_id_user_increment(bNodeSocket *sock) } } -static void socket_id_user_decrement(bNodeSocket *sock) +/** \return True if the socket had an ID default value. */ +static bool socket_id_user_decrement(bNodeSocket *sock) { switch ((eNodeSocketDatatype)sock->type) { case SOCK_OBJECT: { bNodeSocketValueObject *default_value = (bNodeSocketValueObject *)sock->default_value; if (default_value->value != nullptr) { id_us_min(&default_value->value->id); + return true; } break; } @@ -1576,6 +1578,7 @@ static void socket_id_user_decrement(bNodeSocket *sock) bNodeSocketValueImage *default_value = (bNodeSocketValueImage *)sock->default_value; if (default_value->value != nullptr) { id_us_min(&default_value->value->id); + return true; } break; } @@ -1584,6 +1587,7 @@ static void socket_id_user_decrement(bNodeSocket *sock) sock->default_value; if (default_value->value != nullptr) { id_us_min(&default_value->value->id); + return true; } break; } @@ -1591,6 +1595,7 @@ static void socket_id_user_decrement(bNodeSocket *sock) bNodeSocketValueTexture *default_value = (bNodeSocketValueTexture *)sock->default_value; if (default_value->value != nullptr) { id_us_min(&default_value->value->id); + return true; } break; } @@ -1598,6 +1603,7 @@ static void socket_id_user_decrement(bNodeSocket *sock) bNodeSocketValueMaterial *default_value = (bNodeSocketValueMaterial *)sock->default_value; if (default_value->value != nullptr) { id_us_min(&default_value->value->id); + return true; } break; } @@ -1613,6 +1619,7 @@ static void socket_id_user_decrement(bNodeSocket *sock) case SOCK_GEOMETRY: break; } + return false; } void nodeModifySocketType(bNodeTree *ntree, @@ -1945,6 +1952,8 @@ void nodeRemoveAllSockets(bNodeTree *ntree, bNode *node) } } + BLI_freelistN(&node->internal_links); + LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, &node->inputs) { node_socket_free(sock, true); MEM_freeN(sock); @@ -2431,6 +2440,11 @@ bool nodeLinkIsHidden(const bNodeLink *link) return nodeSocketIsHidden(link->fromsock) || nodeSocketIsHidden(link->tosock); } +bool nodeLinkIsSelected(const bNodeLink *link) +{ + return (link->fromnode->flag & NODE_SELECT) || (link->tonode->flag & NODE_SELECT); +} + /* Adjust the indices of links connected to the given multi input socket after deleting the link at * `deleted_index`. This function also works if the link has not yet been deleted. */ static void adjust_multi_input_indices_after_removed_link(bNodeTree *ntree, @@ -2972,6 +2986,8 @@ void nodeRemoveNode(Main *bmain, bNodeTree *ntree, bNode *node, bool do_id_user) * do to ID user refcounting and removal of animdation data then. */ BLI_assert((ntree->id.tag & LIB_TAG_LOCALIZED) == 0); + bool node_has_id = false; + if (do_id_user) { /* Free callback for NodeCustomGroup. */ if (node->typeinfo->freefunc_api) { @@ -2984,13 +3000,14 @@ void nodeRemoveNode(Main *bmain, bNodeTree *ntree, bNode *node, bool do_id_user) /* Do user counting. */ if (node->id) { id_us_min(node->id); + node_has_id = true; } LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { - socket_id_user_decrement(sock); + node_has_id |= socket_id_user_decrement(sock); } LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { - socket_id_user_decrement(sock); + node_has_id |= socket_id_user_decrement(sock); } } @@ -3007,6 +3024,12 @@ void nodeRemoveNode(Main *bmain, bNodeTree *ntree, bNode *node, bool do_id_user) } } + if (node_has_id) { + if (bmain != nullptr) { + DEG_relations_tag_update(bmain); + } + } + nodeUnlinkNode(ntree, node); node_unlink_attached(ntree, node); @@ -4735,6 +4758,7 @@ static void registerGeometryNodes() register_node_type_geo_curve_to_points(); register_node_type_geo_curve_trim(); register_node_type_geo_delete_geometry(); + register_node_type_geo_duplicate_elements(); register_node_type_geo_distribute_points_on_faces(); register_node_type_geo_dual_mesh(); register_node_type_geo_edge_split(); @@ -4753,6 +4777,7 @@ static void registerGeometryNodes() register_node_type_geo_input_mesh_edge_neighbors(); register_node_type_geo_input_mesh_edge_vertices(); register_node_type_geo_input_mesh_face_area(); + register_node_type_geo_input_mesh_face_is_planar(); register_node_type_geo_input_mesh_face_neighbors(); register_node_type_geo_input_mesh_island(); register_node_type_geo_input_mesh_vertex_neighbors(); diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc index 579e61750f0..985c9edac1a 100644 --- a/source/blender/blenkernel/intern/object.cc +++ b/source/blender/blenkernel/intern/object.cc @@ -73,7 +73,7 @@ #include "BKE_constraint.h" #include "BKE_crazyspace.h" #include "BKE_curve.h" -#include "BKE_curves.h" +#include "BKE_curves.hh" #include "BKE_deform.h" #include "BKE_displist.h" #include "BKE_duplilist.h" @@ -1385,8 +1385,14 @@ ModifierData *BKE_object_active_modifier(const Object *ob) bool BKE_object_supports_modifiers(const Object *ob) { - return ( - ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE, OB_POINTCLOUD, OB_VOLUME)); + return (ELEM(ob->type, + OB_MESH, + OB_CURVES_LEGACY, + OB_SURF, + OB_FONT, + OB_LATTICE, + OB_POINTCLOUD, + OB_VOLUME)); } bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type) @@ -1402,7 +1408,7 @@ bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type) if (ELEM(ob->type, OB_POINTCLOUD, OB_VOLUME, OB_CURVES)) { return (mti->modifyGeometrySet != nullptr); } - if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) { + if (ELEM(ob->type, OB_MESH, OB_CURVES_LEGACY, OB_SURF, OB_FONT, OB_LATTICE)) { if (ob->type == OB_LATTICE && (mti->flags & eModifierTypeFlag_AcceptsVertexCosOnly) == 0) { return false; } @@ -1599,9 +1605,7 @@ bool BKE_object_modifier_stack_copy(Object *ob_dst, continue; } - ModifierData *md_dst = BKE_modifier_new(md_src->type); - BLI_strncpy(md_dst->name, md_src->name, sizeof(md_dst->name)); - BKE_modifier_copydata_ex(md_src, md_dst, flag_subdata); + ModifierData *md_dst = BKE_modifier_copy_ex(md_src, flag_subdata); BLI_addtail(&ob_dst->modifiers, md_dst); } @@ -1873,7 +1877,7 @@ bool BKE_object_is_in_editmode(const Object *ob) case OB_LATTICE: return ((Lattice *)ob->data)->editlatt != nullptr; case OB_SURF: - case OB_CURVE: + case OB_CURVES_LEGACY: return ((Curve *)ob->data)->editnurb != nullptr; case OB_GPENCIL: /* Grease Pencil object has no edit mode data. */ @@ -1895,7 +1899,7 @@ bool BKE_object_data_is_in_editmode(const ID *id) switch (type) { case ID_ME: return ((const Mesh *)id)->edit_mesh != nullptr; - case ID_CU: + case ID_CU_LEGACY: return ((((const Curve *)id)->editnurb != nullptr) || (((const Curve *)id)->editfont != nullptr)); case ID_MB: @@ -1921,7 +1925,7 @@ char *BKE_object_data_editmode_flush_ptr_get(struct ID *id) } break; } - case ID_CU: { + case ID_CU_LEGACY: { if (((Curve *)id)->vfont != nullptr) { EditFont *ef = ((Curve *)id)->editfont; if (ef != nullptr) { @@ -2064,7 +2068,7 @@ static const char *get_obdata_defname(int type) switch (type) { case OB_MESH: return DATA_("Mesh"); - case OB_CURVE: + case OB_CURVES_LEGACY: return DATA_("Curve"); case OB_SURF: return DATA_("Surf"); @@ -2135,8 +2139,8 @@ void *BKE_object_obdata_add_from_type(Main *bmain, int type, const char *name) switch (type) { case OB_MESH: return BKE_mesh_add(bmain, name); - case OB_CURVE: - return BKE_curve_add(bmain, name, OB_CURVE); + case OB_CURVES_LEGACY: + return BKE_curve_add(bmain, name, OB_CURVES_LEGACY); case OB_SURF: return BKE_curve_add(bmain, name, OB_SURF); case OB_FONT: @@ -2177,7 +2181,7 @@ int BKE_object_obdata_to_type(const ID *id) switch (GS(id->name)) { case ID_ME: return OB_MESH; - case ID_CU: + case ID_CU_LEGACY: return BKE_curve_type_get((const Curve *)id); case ID_MB: return OB_MBALL; @@ -2658,7 +2662,7 @@ Object *BKE_object_duplicate(Main *bmain, Object *ob, uint dupflag, uint duplica id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; - case OB_CURVE: + case OB_CURVES_LEGACY: if (dupflag & USER_DUP_CURVE) { id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } @@ -3222,7 +3226,7 @@ static void give_parvert(Object *par, int nr, float vec[3]) "object position can be wrong now"); } } - else if (ELEM(par->type, OB_CURVE, OB_SURF)) { + else if (ELEM(par->type, OB_CURVES_LEGACY, OB_SURF)) { ListBase *nurb; /* Unless there's some weird depsgraph failure the cache should exist. */ @@ -3295,7 +3299,7 @@ void BKE_object_get_parent_matrix(Object *ob, Object *par, float r_parentmat[4][ switch (ob->partype & PARTYPE) { case PAROBJECT: { bool ok = false; - if (par->type == OB_CURVE) { + if (par->type == OB_CURVES_LEGACY) { if ((((Curve *)par->data)->flag & CU_PATH) && (ob_parcurve(ob, par, tmat))) { ok = true; } @@ -3591,7 +3595,7 @@ BoundBox *BKE_object_boundbox_get(Object *ob) case OB_MESH: bb = BKE_mesh_boundbox_get(ob); break; - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: case OB_FONT: bb = BKE_curve_boundbox_get(ob); @@ -3760,7 +3764,7 @@ void BKE_object_minmax(Object *ob, float r_min[3], float r_max[3], const bool us bool changed = false; switch (ob->type) { - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_FONT: case OB_SURF: { BoundBox bb = *BKE_curve_boundbox_get(ob); @@ -3938,7 +3942,7 @@ bool BKE_object_minmax_dupli(Depsgraph *depsgraph, ListBase *lb = object_duplilist(depsgraph, scene, ob); LISTBASE_FOREACH (DupliObject *, dob, lb) { - if ((use_hidden == false) && (dob->no_draw != 0)) { + if (((use_hidden == false) && (dob->no_draw != 0)) || dob->ob_data == nullptr) { /* pass */ } else { @@ -4189,7 +4193,7 @@ bool BKE_object_obdata_texspace_get(Object *ob, char **r_texflag, float **r_loc, BKE_mesh_texspace_get_reference((Mesh *)ob->data, r_texflag, r_loc, r_size); break; } - case ID_CU: { + case ID_CU_LEGACY: { Curve *cu = (Curve *)ob->data; BKE_curve_texspace_ensure(cu); if (r_texflag) { @@ -4557,7 +4561,7 @@ KeyBlock *BKE_object_shapekey_insert(Main *bmain, case OB_MESH: key = insert_meshkey(bmain, ob, name, from_mix); break; - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: key = insert_curvekey(bmain, ob, name, from_mix); break; @@ -4629,7 +4633,7 @@ bool BKE_object_shapekey_remove(Main *bmain, Object *ob, KeyBlock *kb) case OB_MESH: BKE_keyblock_convert_to_mesh(key->refkey, (Mesh *)ob->data); break; - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: BKE_keyblock_convert_to_curve( key->refkey, (Curve *)ob->data, BKE_curve_nurbs_get((Curve *)ob->data)); @@ -4844,7 +4848,7 @@ int BKE_object_is_deform_modified(Scene *scene, Object *ob) flag |= eModifierMode_Realtime | eModifierMode_Render; } - if (ob->type == OB_CURVE) { + if (ob->type == OB_CURVES_LEGACY) { Curve *cu = (Curve *)ob->data; if (cu->taperobj != nullptr && object_deforms_in_time(cu->taperobj)) { flag |= eModifierMode_Realtime | eModifierMode_Render; @@ -4921,7 +4925,7 @@ bool BKE_object_supports_material_slots(struct Object *ob) { return ELEM(ob->type, OB_MESH, - OB_CURVE, + OB_CURVES_LEGACY, OB_SURF, OB_FONT, OB_MBALL, @@ -5155,7 +5159,7 @@ KDTree_3d *BKE_object_as_kdtree(Object *ob, int *r_tot) BLI_kdtree_3d_balance(tree); break; } - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: { /* TODO: take deformation into account */ Curve *cu = (Curve *)ob->data; @@ -5462,7 +5466,7 @@ bool BKE_object_modifier_update_subframe(Depsgraph *depsgraph, } /* for curve following objects, parented curve has to be updated too */ - if (ob->type == OB_CURVE) { + if (ob->type == OB_CURVES_LEGACY) { Curve *cu = (Curve *)ob->data; BKE_animsys_evaluate_animdata( &cu->id, cu->adt, &anim_eval_context, ADT_RECALC_ANIM, flush_to_original); diff --git a/source/blender/blenkernel/intern/object_dupli.cc b/source/blender/blenkernel/intern/object_dupli.cc index 18dd61004f5..009a7bd70be 100644 --- a/source/blender/blenkernel/intern/object_dupli.cc +++ b/source/blender/blenkernel/intern/object_dupli.cc @@ -851,7 +851,7 @@ static void make_duplis_geometry_set_impl(const DupliContext *ctx, dupli->ob_data = (ID *)volume; } } - if (!ELEM(ctx->object->type, OB_CURVE, OB_FONT) || geometry_set_is_instance) { + if (!ELEM(ctx->object->type, OB_CURVES_LEGACY, OB_FONT) || geometry_set_is_instance) { const CurveComponent *curve_component = geometry_set.get_component_for_read<CurveComponent>(); if (curve_component != nullptr) { const Curve *curve = curve_component->get_curve_for_render(); diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c index 2b0e04d0bd0..3bc2139ca0c 100644 --- a/source/blender/blenkernel/intern/object_update.c +++ b/source/blender/blenkernel/intern/object_update.c @@ -181,7 +181,7 @@ void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *o BKE_displist_make_mball(depsgraph, scene, ob); break; - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: case OB_FONT: { bool for_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER); @@ -300,7 +300,7 @@ void BKE_object_data_batch_cache_dirty_tag(ID *object_data) BKE_lattice_batch_cache_dirty_tag((struct Lattice *)object_data, BKE_LATTICE_BATCH_DIRTY_ALL); break; - case ID_CU: + case ID_CU_LEGACY: BKE_curve_batch_cache_dirty_tag((struct Curve *)object_data, BKE_CURVE_BATCH_DIRTY_ALL); break; case ID_MB: @@ -364,7 +364,7 @@ void BKE_object_data_select_update(Depsgraph *depsgraph, ID *object_data) case ID_ME: BKE_mesh_batch_cache_dirty_tag((Mesh *)object_data, BKE_MESH_BATCH_DIRTY_SELECT); break; - case ID_CU: + case ID_CU_LEGACY: BKE_curve_batch_cache_dirty_tag((Curve *)object_data, BKE_CURVE_BATCH_DIRTY_SELECT); break; case ID_LT: diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index ffd0a03fc51..d42c8ea37d5 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -327,6 +327,9 @@ bool BKE_paint_ensure_from_paintmode(Scene *sce, ePaintMode mode) case PAINT_MODE_WEIGHT_GPENCIL: paint_ptr = (Paint **)&ts->gp_weightpaint; break; + case PAINT_MODE_SCULPT_CURVES: + paint_ptr = (Paint **)&ts->curves_sculpt; + break; case PAINT_MODE_INVALID: break; } @@ -362,6 +365,8 @@ Paint *BKE_paint_get_active_from_paintmode(Scene *sce, ePaintMode mode) return &ts->gp_sculptpaint->paint; case PAINT_MODE_WEIGHT_GPENCIL: return &ts->gp_weightpaint->paint; + case PAINT_MODE_SCULPT_CURVES: + return &ts->curves_sculpt->paint; case PAINT_MODE_INVALID: return NULL; default: @@ -394,6 +399,8 @@ const EnumPropertyItem *BKE_paint_get_tool_enum_from_paintmode(ePaintMode mode) return rna_enum_brush_gpencil_sculpt_types_items; case PAINT_MODE_WEIGHT_GPENCIL: return rna_enum_brush_gpencil_weight_types_items; + case PAINT_MODE_SCULPT_CURVES: + return rna_enum_brush_curves_sculpt_tool_items; case PAINT_MODE_INVALID: break; } @@ -422,6 +429,8 @@ const char *BKE_paint_get_tool_prop_id_from_paintmode(ePaintMode mode) return "gpencil_sculpt_tool"; case PAINT_MODE_WEIGHT_GPENCIL: return "gpencil_weight_tool"; + case PAINT_MODE_SCULPT_CURVES: + return "curves_sculpt_tool"; case PAINT_MODE_INVALID: break; } @@ -453,6 +462,8 @@ Paint *BKE_paint_get_active(Scene *sce, ViewLayer *view_layer) return &ts->gp_sculptpaint->paint; case OB_MODE_WEIGHT_GPENCIL: return &ts->gp_weightpaint->paint; + case OB_MODE_SCULPT_CURVES: + return &ts->curves_sculpt->paint; case OB_MODE_EDIT: return ts->uvsculpt ? &ts->uvsculpt->paint : NULL; default: @@ -573,6 +584,8 @@ ePaintMode BKE_paintmode_get_from_tool(const struct bToolRef *tref) return PAINT_MODE_SCULPT_GPENCIL; case CTX_MODE_WEIGHT_GPENCIL: return PAINT_MODE_WEIGHT_GPENCIL; + case CTX_MODE_SCULPT_CURVES: + return PAINT_MODE_SCULPT_CURVES; } } else if (tref->space_type == SPACE_IMAGE) { @@ -641,6 +654,10 @@ void BKE_paint_runtime_init(const ToolSettings *ts, Paint *paint) paint->runtime.tool_offset = offsetof(Brush, gpencil_weight_tool); paint->runtime.ob_mode = OB_MODE_WEIGHT_GPENCIL; } + else if (ts->curves_sculpt && paint == &ts->curves_sculpt->paint) { + paint->runtime.tool_offset = offsetof(Brush, curves_sculpt_tool); + paint->runtime.ob_mode = OB_MODE_SCULPT_CURVES; + } else { BLI_assert_unreachable(); } @@ -668,6 +685,8 @@ uint BKE_paint_get_brush_tool_offset_from_paintmode(const ePaintMode mode) return offsetof(Brush, gpencil_sculpt_tool); case PAINT_MODE_WEIGHT_GPENCIL: return offsetof(Brush, gpencil_weight_tool); + case PAINT_MODE_SCULPT_CURVES: + return offsetof(Brush, curves_sculpt_tool); case PAINT_MODE_INVALID: break; /* We don't use these yet. */ } @@ -1028,6 +1047,7 @@ bool BKE_paint_ensure(ToolSettings *ts, struct Paint **r_paint) (Paint *)ts->vpaint, (Paint *)ts->wpaint, (Paint *)ts->uvsculpt, + (Paint *)ts->curves_sculpt, (Paint *)&ts->imapaint)); #ifdef DEBUG struct Paint paint_test = **r_paint; @@ -1075,6 +1095,10 @@ bool BKE_paint_ensure(ToolSettings *ts, struct Paint **r_paint) UvSculpt *data = MEM_callocN(sizeof(*data), __func__); paint = &data->paint; } + else if ((CurvesSculpt **)r_paint == &ts->curves_sculpt) { + CurvesSculpt *data = MEM_callocN(sizeof(*data), __func__); + paint = &data->paint; + } else if (*r_paint == &ts->imapaint.paint) { paint = &ts->imapaint.paint; } diff --git a/source/blender/blenkernel/intern/paint_toolslots.c b/source/blender/blenkernel/intern/paint_toolslots.c index 04b70aae199..f35755021d2 100644 --- a/source/blender/blenkernel/intern/paint_toolslots.c +++ b/source/blender/blenkernel/intern/paint_toolslots.c @@ -98,6 +98,9 @@ void BKE_paint_toolslots_init_from_main(struct Main *bmain) if (ts->gp_weightpaint) { paint_toolslots_init_with_runtime(bmain, ts, &ts->gp_weightpaint->paint); } + if (ts->curves_sculpt) { + paint_toolslots_init_with_runtime(bmain, ts, &ts->curves_sculpt->paint); + } } } diff --git a/source/blender/blenkernel/intern/pointcloud.cc b/source/blender/blenkernel/intern/pointcloud.cc index 2a4e0715293..3ee46fc4f15 100644 --- a/source/blender/blenkernel/intern/pointcloud.cc +++ b/source/blender/blenkernel/intern/pointcloud.cc @@ -11,6 +11,7 @@ #include "DNA_object_types.h" #include "DNA_pointcloud_types.h" +#include "BLI_bounds.hh" #include "BLI_index_range.hh" #include "BLI_listbase.h" #include "BLI_math_vec_types.hh" @@ -254,68 +255,28 @@ PointCloud *BKE_pointcloud_new_nomain(const int totpoint) return pointcloud; } -struct MinMaxResult { - float3 min; - float3 max; -}; - -static MinMaxResult min_max_no_radii(Span<float3> positions) +static std::optional<blender::bounds::MinMaxResult<float3>> point_cloud_bounds( + const PointCloud &pointcloud) { - using namespace blender::math; - - return blender::threading::parallel_reduce( - positions.index_range(), - 1024, - MinMaxResult{float3(FLT_MAX), float3(-FLT_MAX)}, - [&](IndexRange range, const MinMaxResult &init) { - MinMaxResult result = init; - for (const int i : range) { - min_max(positions[i], result.min, result.max); - } - return result; - }, - [](const MinMaxResult &a, const MinMaxResult &b) { - return MinMaxResult{min(a.min, b.min), max(a.max, b.max)}; - }); -} - -static MinMaxResult min_max_with_radii(Span<float3> positions, Span<float> radii) -{ - using namespace blender::math; - - return blender::threading::parallel_reduce( - positions.index_range(), - 1024, - MinMaxResult{float3(FLT_MAX), float3(-FLT_MAX)}, - [&](IndexRange range, const MinMaxResult &init) { - MinMaxResult result = init; - for (const int i : range) { - result.min = min(positions[i] - radii[i], result.min); - result.max = max(positions[i] + radii[i], result.max); - } - return result; - }, - [](const MinMaxResult &a, const MinMaxResult &b) { - return MinMaxResult{min(a.min, b.min), max(a.max, b.max)}; - }); + Span<float3> positions{reinterpret_cast<float3 *>(pointcloud.co), pointcloud.totpoint}; + if (pointcloud.radius) { + Span<float> radii{pointcloud.radius, pointcloud.totpoint}; + return blender::bounds::min_max_with_radii(positions, radii); + } + return blender::bounds::min_max(positions); } bool BKE_pointcloud_minmax(const PointCloud *pointcloud, float r_min[3], float r_max[3]) { - using namespace blender::math; + using namespace blender; - if (!pointcloud->totpoint) { + const std::optional<bounds::MinMaxResult<float3>> min_max = point_cloud_bounds(*pointcloud); + if (!min_max) { return false; } - Span<float3> positions{reinterpret_cast<float3 *>(pointcloud->co), pointcloud->totpoint}; - const MinMaxResult min_max = (pointcloud->radius) ? - min_max_with_radii(positions, - {pointcloud->radius, pointcloud->totpoint}) : - min_max_no_radii(positions); - - copy_v3_v3(r_min, min(min_max.min, float3(r_min))); - copy_v3_v3(r_max, max(min_max.max, float3(r_max))); + copy_v3_v3(r_min, math::min(min_max->min, float3(r_min))); + copy_v3_v3(r_max, math::max(min_max->max, float3(r_max))); return true; } diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 7827c40e2c2..baf2f0bac8a 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -317,12 +317,6 @@ static void scene_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int scene_dst->r.avicodecdata->lpParms = MEM_dupallocN(scene_dst->r.avicodecdata->lpParms); } - if (scene_src->r.ffcodecdata.properties) { - /* intentionally check sce_dst not sce_src. */ /* XXX ??? comment outdated... */ - scene_dst->r.ffcodecdata.properties = IDP_CopyProperty_ex(scene_src->r.ffcodecdata.properties, - flag_subdata); - } - if (scene_src->display.shading.prop) { scene_dst->display.shading.prop = IDP_CopyProperty(scene_src->display.shading.prop); } @@ -393,10 +387,6 @@ static void scene_free_data(ID *id) MEM_freeN(scene->r.avicodecdata); scene->r.avicodecdata = NULL; } - if (scene->r.ffcodecdata.properties) { - IDP_FreeProperty(scene->r.ffcodecdata.properties); - scene->r.ffcodecdata.properties = NULL; - } scene_free_markers(scene, do_id_user); BLI_freelistN(&scene->transform_spaces); @@ -718,6 +708,16 @@ static void scene_foreach_toolsettings(LibraryForeachIDData *data, reader, &toolsett_old->gp_weightpaint->paint)); } + if (toolsett->curves_sculpt) { + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL( + data, + do_undo_restore, + scene_foreach_paint(data, + &toolsett->curves_sculpt->paint, + do_undo_restore, + reader, + &toolsett_old->curves_sculpt->paint)); + } BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, toolsett->gp_sculpt.guide.reference_object, @@ -972,6 +972,10 @@ static void scene_blend_write(BlendWriter *writer, ID *id, const void *id_addres BLO_write_struct(writer, GpWeightPaint, tos->gp_weightpaint); BKE_paint_blend_write(writer, &tos->gp_weightpaint->paint); } + if (tos->curves_sculpt) { + BLO_write_struct(writer, CurvesSculpt, tos->curves_sculpt); + BKE_paint_blend_write(writer, &tos->curves_sculpt->paint); + } /* write grease-pencil custom ipo curve to file */ if (tos->gp_interpolate.custom_ipo) { BKE_curvemapping_blend_write(writer, tos->gp_interpolate.custom_ipo); @@ -1014,9 +1018,6 @@ static void scene_blend_write(BlendWriter *writer, ID *id, const void *id_addres BLO_write_raw(writer, (size_t)sce->r.avicodecdata->cbParms, sce->r.avicodecdata->lpParms); } } - if (sce->r.ffcodecdata.properties) { - IDP_BlendWrite(writer, sce->r.ffcodecdata.properties); - } /* writing dynamic list of TimeMarkers to the blend file */ LISTBASE_FOREACH (TimeMarker *, marker, &sce->markers) { @@ -1148,6 +1149,7 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id) direct_link_paint_helper(reader, sce, (Paint **)&sce->toolsettings->gp_vertexpaint); direct_link_paint_helper(reader, sce, (Paint **)&sce->toolsettings->gp_sculptpaint); direct_link_paint_helper(reader, sce, (Paint **)&sce->toolsettings->gp_weightpaint); + direct_link_paint_helper(reader, sce, (Paint **)&sce->toolsettings->curves_sculpt); BKE_paint_blend_read_data(reader, sce, &sce->toolsettings->imapaint.paint); @@ -1256,11 +1258,6 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id) BLO_read_data_address(reader, &sce->r.avicodecdata->lpFormat); BLO_read_data_address(reader, &sce->r.avicodecdata->lpParms); } - if (sce->r.ffcodecdata.properties) { - BLO_read_data_address(reader, &sce->r.ffcodecdata.properties); - IDP_BlendDataRead(reader, &sce->r.ffcodecdata.properties); - } - BLO_read_list(reader, &(sce->markers)); LISTBASE_FOREACH (TimeMarker *, marker, &sce->markers) { BLO_read_data_address(reader, &marker->prop); @@ -1406,6 +1403,9 @@ static void scene_blend_read_lib(BlendLibReader *reader, ID *id) if (sce->toolsettings->gp_weightpaint) { BKE_paint_blend_read_lib(reader, sce, &sce->toolsettings->gp_weightpaint->paint); } + if (sce->toolsettings->curves_sculpt) { + BKE_paint_blend_read_lib(reader, sce, &sce->toolsettings->curves_sculpt->paint); + } if (sce->toolsettings->sculpt) { BLO_read_id_address(reader, sce->id.lib, &sce->toolsettings->sculpt->gravity_object); @@ -1726,6 +1726,10 @@ ToolSettings *BKE_toolsettings_copy(ToolSettings *toolsettings, const int flag) ts->gp_weightpaint = MEM_dupallocN(ts->gp_weightpaint); BKE_paint_copy(&ts->gp_weightpaint->paint, &ts->gp_weightpaint->paint, flag); } + if (ts->curves_sculpt) { + ts->curves_sculpt = MEM_dupallocN(ts->curves_sculpt); + BKE_paint_copy(&ts->curves_sculpt->paint, &ts->curves_sculpt->paint, flag); + } BKE_paint_copy(&ts->imapaint.paint, &ts->imapaint.paint, flag); ts->particle.paintcursor = NULL; @@ -1781,6 +1785,10 @@ void BKE_toolsettings_free(ToolSettings *toolsettings) BKE_paint_free(&toolsettings->gp_weightpaint->paint); MEM_freeN(toolsettings->gp_weightpaint); } + if (toolsettings->curves_sculpt) { + BKE_paint_free(&toolsettings->curves_sculpt->paint); + MEM_freeN(toolsettings->curves_sculpt); + } BKE_paint_free(&toolsettings->imapaint.paint); /* free Grease Pencil interpolation curve */ @@ -1873,10 +1881,6 @@ Scene *BKE_scene_duplicate(Main *bmain, Scene *sce, eSceneCopyMethod type) sce_copy->r.avicodecdata->lpParms = MEM_dupallocN(sce_copy->r.avicodecdata->lpParms); } - if (sce->r.ffcodecdata.properties) { /* intentionally check scen not sce. */ - sce_copy->r.ffcodecdata.properties = IDP_CopyProperty(sce->r.ffcodecdata.properties); - } - BKE_sound_reset_scene_runtime(sce_copy); /* grease pencil */ diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c index 7b8e5a1409a..38066f95084 100644 --- a/source/blender/blenkernel/intern/softbody.c +++ b/source/blender/blenkernel/intern/softbody.c @@ -2967,7 +2967,7 @@ static void curve_surf_to_softbody(Object *ob) totvert = BKE_nurbList_verts_count(&cu->nurb); if (ob->softflag & OB_SB_EDGES) { - if (ob->type == OB_CURVE) { + if (ob->type == OB_CURVES_LEGACY) { totspring = totvert - BLI_listbase_count(&cu->nurb); } } @@ -3320,7 +3320,7 @@ static void softbody_reset(Object *ob, SoftBody *sb, float (*vertexCos)[3], int break; case OB_LATTICE: break; - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: break; default: @@ -3537,7 +3537,7 @@ void sbObjectStep(struct Depsgraph *depsgraph, case OB_LATTICE: lattice_to_softbody(ob); break; - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: curve_surf_to_softbody(ob); break; diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc index d1c4756a3b9..dc5b1d28539 100644 --- a/source/blender/blenkernel/intern/spline_base.cc +++ b/source/blender/blenkernel/intern/spline_base.cc @@ -23,7 +23,7 @@ using blender::fn::GMutableSpan; using blender::fn::GSpan; using blender::fn::GVArray; -Spline::Type Spline::type() const +CurveType Spline::type() const { return type_; } @@ -34,15 +34,18 @@ void Spline::copy_base_settings(const Spline &src, Spline &dst) dst.is_cyclic_ = src.is_cyclic_; } -static SplinePtr create_spline(const Spline::Type type) +static SplinePtr create_spline(const CurveType type) { switch (type) { - case Spline::Type::Poly: + case CURVE_TYPE_POLY: return std::make_unique<PolySpline>(); - case Spline::Type::Bezier: + case CURVE_TYPE_BEZIER: return std::make_unique<BezierSpline>(); - case Spline::Type::NURBS: + case CURVE_TYPE_NURBS: return std::make_unique<NURBSpline>(); + case CURVE_TYPE_CATMULL_ROM: + BLI_assert_unreachable(); + return {}; } BLI_assert_unreachable(); return {}; diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc index 57f1d73d55e..3c2ac1dae9c 100644 --- a/source/blender/blenkernel/intern/spline_bezier.cc +++ b/source/blender/blenkernel/intern/spline_bezier.cc @@ -93,11 +93,11 @@ Span<float> BezierSpline::tilts() const { return tilts_; } -Span<BezierSpline::HandleType> BezierSpline::handle_types_left() const +Span<int8_t> BezierSpline::handle_types_left() const { return handle_types_left_; } -MutableSpan<BezierSpline::HandleType> BezierSpline::handle_types_left() +MutableSpan<int8_t> BezierSpline::handle_types_left() { return handle_types_left_; } @@ -114,11 +114,11 @@ MutableSpan<float3> BezierSpline::handle_positions_left(const bool write_only) return handle_positions_left_; } -Span<BezierSpline::HandleType> BezierSpline::handle_types_right() const +Span<int8_t> BezierSpline::handle_types_right() const { return handle_types_right_; } -MutableSpan<BezierSpline::HandleType> BezierSpline::handle_types_right() +MutableSpan<int8_t> BezierSpline::handle_types_right() { return handle_types_right_; } @@ -187,7 +187,7 @@ void BezierSpline::ensure_auto_handles() const for (const int i : IndexRange(this->size())) { using namespace blender; - if (ELEM(HandleType::Auto, handle_types_left_[i], handle_types_right_[i])) { + if (ELEM(BEZIER_HANDLE_AUTO, handle_types_left_[i], handle_types_right_[i])) { const float3 prev_diff = positions_[i] - previous_position(positions_, is_cyclic_, i); const float3 next_diff = next_position(positions_, is_cyclic_, i) - positions_[i]; float prev_len = math::length(prev_diff); @@ -203,23 +203,23 @@ void BezierSpline::ensure_auto_handles() const /* This magic number is unfortunate, but comes from elsewhere in Blender. */ const float len = math::length(dir) * 2.5614f; if (len != 0.0f) { - if (handle_types_left_[i] == HandleType::Auto) { + if (handle_types_left_[i] == BEZIER_HANDLE_AUTO) { const float prev_len_clamped = std::min(prev_len, next_len * 5.0f); handle_positions_left_[i] = positions_[i] + dir * -(prev_len_clamped / len); } - if (handle_types_right_[i] == HandleType::Auto) { + if (handle_types_right_[i] == BEZIER_HANDLE_AUTO) { const float next_len_clamped = std::min(next_len, prev_len * 5.0f); handle_positions_right_[i] = positions_[i] + dir * (next_len_clamped / len); } } } - if (handle_types_left_[i] == HandleType::Vector) { + if (handle_types_left_[i] == BEZIER_HANDLE_VECTOR) { const float3 prev = previous_position(positions_, is_cyclic_, i); handle_positions_left_[i] = math::interpolate(positions_[i], prev, 1.0f / 3.0f); } - if (handle_types_right_[i] == HandleType::Vector) { + if (handle_types_right_[i] == BEZIER_HANDLE_VECTOR) { const float3 next = next_position(positions_, is_cyclic_, i); handle_positions_right_[i] = math::interpolate(positions_[i], next, 1.0f / 3.0f); } @@ -257,8 +257,8 @@ void BezierSpline::transform(const blender::float4x4 &matrix) } static void set_handle_position(const float3 &position, - const BezierSpline::HandleType type, - const BezierSpline::HandleType type_other, + const HandleType type, + const HandleType type_other, const float3 &new_value, float3 &handle, float3 &handle_other) @@ -266,12 +266,12 @@ static void set_handle_position(const float3 &position, using namespace blender::math; /* Don't bother when the handle positions are calculated automatically anyway. */ - if (ELEM(type, BezierSpline::HandleType::Auto, BezierSpline::HandleType::Vector)) { + if (ELEM(type, BEZIER_HANDLE_AUTO, BEZIER_HANDLE_VECTOR)) { return; } handle = new_value; - if (type_other == BezierSpline::HandleType::Align) { + if (type_other == BEZIER_HANDLE_ALIGN) { /* Keep track of the old length of the opposite handle. */ const float length = distance(handle_other, position); /* Set the other handle to directly opposite from the current handle. */ @@ -283,8 +283,8 @@ static void set_handle_position(const float3 &position, void BezierSpline::set_handle_position_right(const int index, const blender::float3 &value) { set_handle_position(positions_[index], - handle_types_right_[index], - handle_types_left_[index], + static_cast<HandleType>(handle_types_right_[index]), + static_cast<HandleType>(handle_types_left_[index]), value, handle_positions_right_[index], handle_positions_left_[index]); @@ -293,8 +293,8 @@ void BezierSpline::set_handle_position_right(const int index, const blender::flo void BezierSpline::set_handle_position_left(const int index, const blender::float3 &value) { set_handle_position(positions_[index], - handle_types_left_[index], - handle_types_right_[index], + static_cast<HandleType>(handle_types_right_[index]), + static_cast<HandleType>(handle_types_left_[index]), value, handle_positions_left_[index], handle_positions_right_[index]); @@ -302,8 +302,8 @@ void BezierSpline::set_handle_position_left(const int index, const blender::floa bool BezierSpline::point_is_sharp(const int index) const { - return ELEM(handle_types_left_[index], HandleType::Vector, HandleType::Free) || - ELEM(handle_types_right_[index], HandleType::Vector, HandleType::Free); + return ELEM(handle_types_left_[index], BEZIER_HANDLE_VECTOR, BEZIER_HANDLE_FREE) || + ELEM(handle_types_right_[index], BEZIER_HANDLE_VECTOR, BEZIER_HANDLE_FREE); } bool BezierSpline::segment_is_vector(const int index) const @@ -313,15 +313,15 @@ bool BezierSpline::segment_is_vector(const int index) const if (index == this->size() - 1) { if (is_cyclic_) { - return handle_types_right_.last() == HandleType::Vector && - handle_types_left_.first() == HandleType::Vector; + return handle_types_right_.last() == BEZIER_HANDLE_VECTOR && + handle_types_left_.first() == BEZIER_HANDLE_VECTOR; } /* There is actually no segment in this case, but it's nice to avoid * having a special case for the last segment in calling code. */ return true; } - return handle_types_right_[index] == HandleType::Vector && - handle_types_left_[index + 1] == HandleType::Vector; + return handle_types_right_[index] == BEZIER_HANDLE_VECTOR && + handle_types_left_[index + 1] == BEZIER_HANDLE_VECTOR; } void BezierSpline::mark_cache_invalid() diff --git a/source/blender/blenkernel/intern/subdiv_mesh.c b/source/blender/blenkernel/intern/subdiv_mesh.c index fd56582f5e3..50135110a64 100644 --- a/source/blender/blenkernel/intern/subdiv_mesh.c +++ b/source/blender/blenkernel/intern/subdiv_mesh.c @@ -72,6 +72,9 @@ static void subdiv_mesh_ctx_cache_custom_data_layers(SubdivMeshContext *ctx) static void subdiv_mesh_prepare_accumulator(SubdivMeshContext *ctx, int num_vertices) { + if (!ctx->have_displacement) { + return; + } ctx->accumulated_counters = MEM_calloc_arrayN( num_vertices, sizeof(*ctx->accumulated_counters), "subdiv accumulated counters"); } @@ -424,18 +427,17 @@ static void subdiv_accumulate_vertex_displacement(SubdivMeshContext *ctx, const float v, MVert *subdiv_vert) { + /* Accumulate displacement. */ Subdiv *subdiv = ctx->subdiv; const int subdiv_vertex_index = subdiv_vert - ctx->subdiv_mesh->mvert; float dummy_P[3], dPdu[3], dPdv[3], D[3]; BKE_subdiv_eval_limit_point_and_derivatives(subdiv, ptex_face_index, u, v, dummy_P, dPdu, dPdv); - /* Accumulate displacement if needed. */ - if (ctx->have_displacement) { - /* NOTE: The subdivided mesh is allocated in this module, and its vertices are kept at zero - * locations as a default calloc(). */ - BKE_subdiv_eval_displacement(subdiv, ptex_face_index, u, v, dPdu, dPdv, D); - add_v3_v3(subdiv_vert->co, D); - } + /* NOTE: The subdivided mesh is allocated in this module, and its vertices are kept at zero + * locations as a default calloc(). */ + BKE_subdiv_eval_displacement(subdiv, ptex_face_index, u, v, dPdu, dPdv, D); + add_v3_v3(subdiv_vert->co, D); + if (ctx->accumulated_counters) { ++ctx->accumulated_counters[subdiv_vertex_index]; } @@ -554,12 +556,13 @@ static void evaluate_vertex_and_apply_displacement_interpolate( add_v3_v3(subdiv_vert->co, D); } -static void subdiv_mesh_vertex_every_corner_or_edge(const SubdivForeachContext *foreach_context, - void *UNUSED(tls), - const int ptex_face_index, - const float u, - const float v, - const int subdiv_vertex_index) +static void subdiv_mesh_vertex_displacement_every_corner_or_edge( + const SubdivForeachContext *foreach_context, + void *UNUSED(tls), + const int ptex_face_index, + const float u, + const float v, + const int subdiv_vertex_index) { SubdivMeshContext *ctx = foreach_context->user_data; Mesh *subdiv_mesh = ctx->subdiv_mesh; @@ -568,31 +571,32 @@ static void subdiv_mesh_vertex_every_corner_or_edge(const SubdivForeachContext * subdiv_accumulate_vertex_displacement(ctx, ptex_face_index, u, v, subdiv_vert); } -static void subdiv_mesh_vertex_every_corner(const SubdivForeachContext *foreach_context, - void *tls, - const int ptex_face_index, - const float u, - const float v, - const int UNUSED(coarse_vertex_index), - const int UNUSED(coarse_poly_index), - const int UNUSED(coarse_corner), - const int subdiv_vertex_index) +static void subdiv_mesh_vertex_displacement_every_corner( + const SubdivForeachContext *foreach_context, + void *tls, + const int ptex_face_index, + const float u, + const float v, + const int UNUSED(coarse_vertex_index), + const int UNUSED(coarse_poly_index), + const int UNUSED(coarse_corner), + const int subdiv_vertex_index) { - subdiv_mesh_vertex_every_corner_or_edge( + subdiv_mesh_vertex_displacement_every_corner_or_edge( foreach_context, tls, ptex_face_index, u, v, subdiv_vertex_index); } -static void subdiv_mesh_vertex_every_edge(const SubdivForeachContext *foreach_context, - void *tls, - const int ptex_face_index, - const float u, - const float v, - const int UNUSED(coarse_edge_index), - const int UNUSED(coarse_poly_index), - const int UNUSED(coarse_corner), - const int subdiv_vertex_index) +static void subdiv_mesh_vertex_displacement_every_edge(const SubdivForeachContext *foreach_context, + void *tls, + const int ptex_face_index, + const float u, + const float v, + const int UNUSED(coarse_edge_index), + const int UNUSED(coarse_poly_index), + const int UNUSED(coarse_corner), + const int subdiv_vertex_index) { - subdiv_mesh_vertex_every_corner_or_edge( + subdiv_mesh_vertex_displacement_every_corner_or_edge( foreach_context, tls, ptex_face_index, u, v, subdiv_vertex_index); } @@ -1078,12 +1082,8 @@ static void setup_foreach_callbacks(const SubdivMeshContext *subdiv_context, foreach_context->topology_info = subdiv_mesh_topology_info; /* Every boundary geometry. Used for displacement averaging. */ if (subdiv_context->have_displacement) { - foreach_context->vertex_every_corner = subdiv_mesh_vertex_every_corner; - foreach_context->vertex_every_edge = subdiv_mesh_vertex_every_edge; - } - else { - foreach_context->vertex_every_corner = subdiv_mesh_vertex_every_corner; - foreach_context->vertex_every_edge = subdiv_mesh_vertex_every_edge; + foreach_context->vertex_every_corner = subdiv_mesh_vertex_displacement_every_corner; + foreach_context->vertex_every_edge = subdiv_mesh_vertex_displacement_every_edge; } foreach_context->vertex_corner = subdiv_mesh_vertex_corner; foreach_context->vertex_edge = subdiv_mesh_vertex_edge; diff --git a/source/blender/blenkernel/intern/subdiv_modifier.c b/source/blender/blenkernel/intern/subdiv_modifier.c index dd35388f230..34dfdaf7595 100644 --- a/source/blender/blenkernel/intern/subdiv_modifier.c +++ b/source/blender/blenkernel/intern/subdiv_modifier.c @@ -54,23 +54,20 @@ static ModifierData *modifier_get_last_enabled_for_mode(const Scene *scene, return md; } -bool BKE_subsurf_modifier_can_do_gpu_subdiv_ex(const Scene *scene, - const Object *ob, - const SubsurfModifierData *smd, - int required_mode, - bool skip_check_is_last) +bool BKE_subsurf_modifier_use_custom_loop_normals(const SubsurfModifierData *smd, const Mesh *mesh) { - if ((U.gpu_flag & USER_GPU_FLAG_SUBDIVISION_EVALUATION) == 0) { - return false; - } + return (smd->flags & eSubsurfModifierFlag_UseCustomNormals) && (mesh->flag & ME_AUTOSMOOTH) && + CustomData_has_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL); +} - if (!skip_check_is_last) { - ModifierData *md = modifier_get_last_enabled_for_mode(scene, ob, required_mode); - if (md != (const ModifierData *)smd) { - return false; - } - } +static bool subsurf_modifier_use_autosmooth_or_split_normals(const SubsurfModifierData *smd, + const Mesh *mesh) +{ + return (mesh->flag & ME_AUTOSMOOTH) || BKE_subsurf_modifier_use_custom_loop_normals(smd, mesh); +} +static bool is_subdivision_evaluation_possible_on_gpu(void) +{ /* Only OpenGL is supported for OpenSubdiv evaluation for now. */ if (GPU_backend_get_type() != GPU_BACKEND_OPENGL) { return false; @@ -88,8 +85,52 @@ bool BKE_subsurf_modifier_can_do_gpu_subdiv_ex(const Scene *scene, return true; } +bool BKE_subsurf_modifier_force_disable_gpu_evaluation_for_mesh(const SubsurfModifierData *smd, + const Mesh *mesh) +{ + if ((U.gpu_flag & USER_GPU_FLAG_SUBDIVISION_EVALUATION) == 0) { + /* GPU subdivision is explicitly disabled, so we don't force it. */ + return false; + } + + if (!is_subdivision_evaluation_possible_on_gpu()) { + /* The GPU type is not compatible with the subdivision. */ + return false; + } + + return subsurf_modifier_use_autosmooth_or_split_normals(smd, mesh); +} + +bool BKE_subsurf_modifier_can_do_gpu_subdiv_ex(const Scene *scene, + const Object *ob, + const Mesh *mesh, + const SubsurfModifierData *smd, + int required_mode, + bool skip_check_is_last) +{ + if ((U.gpu_flag & USER_GPU_FLAG_SUBDIVISION_EVALUATION) == 0) { + return false; + } + + /* Deactivate GPU subdivision if autosmooth or custom split normals are used as those are + * complicated to support on GPU, and should really be separate workflows. */ + if (subsurf_modifier_use_autosmooth_or_split_normals(smd, mesh)) { + return false; + } + + if (!skip_check_is_last) { + ModifierData *md = modifier_get_last_enabled_for_mode(scene, ob, required_mode); + if (md != (const ModifierData *)smd) { + return false; + } + } + + return is_subdivision_evaluation_possible_on_gpu(); +} + bool BKE_subsurf_modifier_can_do_gpu_subdiv(const Scene *scene, const Object *ob, + const Mesh *mesh, int required_mode) { ModifierData *md = modifier_get_last_enabled_for_mode(scene, ob, required_mode); @@ -103,7 +144,7 @@ bool BKE_subsurf_modifier_can_do_gpu_subdiv(const Scene *scene, } return BKE_subsurf_modifier_can_do_gpu_subdiv_ex( - scene, ob, (SubsurfModifierData *)md, required_mode, true); + scene, ob, mesh, (SubsurfModifierData *)md, required_mode, true); } void (*BKE_subsurf_modifier_free_gpu_cache_cb)(Subdiv *subdiv) = NULL; diff --git a/source/blender/blenkernel/intern/vfont.c b/source/blender/blenkernel/intern/vfont.c index cb5e351e78a..5f751da1ee1 100644 --- a/source/blender/blenkernel/intern/vfont.c +++ b/source/blender/blenkernel/intern/vfont.c @@ -811,8 +811,8 @@ static bool vfont_to_curve(Object *ob, #define MARGIN_X_MIN (xof_scale + tb_scale.x) #define MARGIN_Y_MIN (yof_scale + tb_scale.y) - /* remark: do calculations including the trailing '\0' of a string - * because the cursor can be at that location */ + /* NOTE: do calculations including the trailing '\0' of a string + * because the cursor can be at that location. */ BLI_assert(ob == NULL || ob->type == OB_FONT); @@ -905,8 +905,8 @@ static bool vfont_to_curve(Object *ob, custrinfo[i].flag &= ~(CU_CHINFO_WRAP | CU_CHINFO_SMALLCAPS_CHECK | CU_CHINFO_OVERFLOW); } - for (i = 0; i <= slen; i++) { - makebreak: + i = 0; + while (i <= slen) { /* Characters in the list */ info = &custrinfo[i]; ascii = mem[i]; @@ -941,19 +941,16 @@ static bool vfont_to_curve(Object *ob, che = find_vfont_char(vfd, ascii); BLI_rw_mutex_unlock(&vfont_rwlock); - /* - * The character wasn't in the current curve base so load it + /* The character wasn't in the current curve base so load it. * But if the font is built-in then do not try loading since - * whole font is in the memory already - */ + * whole font is in the memory already. */ if (che == NULL && BKE_vfont_is_builtin(vfont) == false) { BLI_rw_mutex_lock(&vfont_rwlock, THREAD_LOCK_WRITE); /* Check it once again, char might have been already load - * between previous BLI_rw_mutex_unlock() and this BLI_rw_mutex_lock(). + * between previous #BLI_rw_mutex_unlock() and this #BLI_rw_mutex_lock(). * * Such a check should not be a bottleneck since it wouldn't - * happen often once all the chars are load. - */ + * happen often once all the chars are load. */ if ((che = find_vfont_char(vfd, ascii)) == NULL) { che = BKE_vfontdata_char_from_freetypefont(vfont, ascii); } @@ -985,8 +982,23 @@ static bool vfont_to_curve(Object *ob, } 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; + bool dobreak = false; + for (j = i; (mem[j] != '\n') && (chartransdata[j].dobreak == 0); j--) { + + /* Special case when there are no breaks possible. */ + if (UNLIKELY(j == 0)) { + if (i == slen) { + /* Use the behavior of zero a height text-box when a break cannot be inserted. + * + * Typically when a text-box has any height and overflow is set to scale + * the text will wrap to fit the width as necessary. When wrapping isn't + * possible it's important to use the same code-path as zero-height lines. + * Without this exception a single word will not scale-to-fit (see: T95116). */ + tb_scale.h = 0.0f; + } + break; + } + if (ELEM(mem[j], ' ', '-')) { ct -= (i - (j - 1)); cnr -= (i - (j - 1)); @@ -1001,24 +1013,18 @@ static bool vfont_to_curve(Object *ob, ct[1].dobreak = 1; custrinfo[i + 1].flag |= CU_CHINFO_WRAP; dobreak = true; + break; } - 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; - } - goto makebreak; + BLI_assert(chartransdata[j].dobreak == 0); + } + + 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; } + /* Since a break was added, re-run this loop with `i` at it's new value. */ + continue; } } } @@ -1063,22 +1069,12 @@ static bool vfont_to_curve(Object *ob, current_line_length = 0.0f; } - /* XXX(campbell): has been unused for years, need to check if this is useful, r4613 r5282. */ -#if 0 - if (ascii == '\n') { - xof = xof_scale; - } - else { - xof = MARGIN_X_MIN; - } -#else xof = MARGIN_X_MIN; -#endif lnr++; cnr = 0; wsnr = 0; } - else if (ascii == 9) { /* TAB */ + else if (ascii == '\t') { /* Tab character. */ float tabfac; ct->xof = xof; @@ -1106,7 +1102,7 @@ static bool vfont_to_curve(Object *ob, sb->w = xof * font_size; } - if (ascii == 32) { + if (ascii == ' ') { /* Space character. */ wsfac = cu->wordspace; wsnr++; } @@ -1114,7 +1110,7 @@ static bool vfont_to_curve(Object *ob, wsfac = 1.0f; } - /* Set the width of the character */ + /* Set the width of the character. */ twidth = char_width(cu, che, info); xof += (twidth * wsfac * (1.0f + (info->kern / 40.0f))) + xtrax; @@ -1124,7 +1120,9 @@ static bool vfont_to_curve(Object *ob, } } ct++; + i++; } + current_line_length += xof + twidth - MARGIN_X_MIN; longest_line_length = MAX2(current_line_length, longest_line_length); @@ -1213,7 +1211,7 @@ static bool vfont_to_curve(Object *ob, } } - /* top-baseline is default, in this case, do nothing */ + /* Top-baseline is default, in this case, do nothing. */ if (cu->align_y != CU_ALIGN_Y_TOP_BASELINE) { if (tb_scale.h != 0.0f) { /* We need to loop all the text-boxes even the "full" ones. @@ -1238,7 +1236,7 @@ static bool vfont_to_curve(Object *ob, } textbox_scale(&tb_scale, &cu->tb[tb_index], 1.0f / font_size); - /* The initial Y origin of the textbox is hardcoded to 1.0f * text scale. */ + /* The initial Y origin of the text-box is hard-coded to 1.0f * text scale. */ const float textbox_y_origin = 1.0f; float yoff = 0.0f; @@ -1302,8 +1300,8 @@ static bool vfont_to_curve(Object *ob, MEM_freeN(i_textbox_array); /* TEXT ON CURVE */ - /* NOTE: Only OB_CURVE objects could have a path. */ - if (cu->textoncurve && cu->textoncurve->type == OB_CURVE) { + /* NOTE: Only #OB_CURVES_LEGACY objects could have a path. */ + if (cu->textoncurve && cu->textoncurve->type == OB_CURVES_LEGACY) { BLI_assert(cu->textoncurve->runtime.curve_cache != NULL); if (cu->textoncurve->runtime.curve_cache != NULL && cu->textoncurve->runtime.curve_cache->anim_path_accum_length != NULL) { @@ -1393,8 +1391,8 @@ static bool vfont_to_curve(Object *ob, ctime = timeofs + distfac * (ct->xof - minx); CLAMP(ctime, 0.0f, 1.0f); - /* calc the right loc AND the right rot separately */ - /* vec, tvec need 4 items */ + /* Calculate the right loc AND the right rot separately. */ + /* `vec`, `tvec` need 4 items. */ BKE_where_on_path(cu->textoncurve, ctime, vec, tvec, NULL, NULL, NULL); BKE_where_on_path(cu->textoncurve, ctime + dtime, tvec, rotvec, NULL, NULL, NULL); @@ -1455,7 +1453,7 @@ static bool vfont_to_curve(Object *ob, break; } cnr = ct->charnr; - /* seek for char with lnr en cnr */ + /* Seek for char with `lnr` & `cnr`. */ ef->pos = 0; ct = chartransdata; for (i = 0; i < slen; i++) { @@ -1473,7 +1471,7 @@ static bool vfont_to_curve(Object *ob, } } - /* cursor first */ + /* Cursor first. */ if (ef) { float si, co; @@ -1519,13 +1517,13 @@ static bool vfont_to_curve(Object *ob, } /* Only do that check in case we do have an object, otherwise all materials get erased every - * time that code is called without an object... */ + * time that code is called without an object. */ if (ob != NULL && (info->mat_nr > (ob->totcol))) { // CLOG_ERROR( // &LOG, "Illegal material index (%d) in text object, setting to 0", info->mat_nr); info->mat_nr = 0; } - /* We do not want to see any character for \n or \r */ + /* We don't want to see any character for '\n'. */ if (cha != '\n') { BKE_vfont_build_char(cu, r_nubase, cha, info, ct->xof, ct->yof, ct->rot, i, font_size); } @@ -1601,8 +1599,7 @@ static bool vfont_to_curve(Object *ob, * * Keep in mind that there is no single number that will make all fit to the end. * In a way, our ultimate goal is to get the highest scale that still leads to the - * number of extra lines to zero. - */ + * number of extra lines to zero. */ if (iter_data->status == VFONT_TO_CURVE_INIT) { bool valid = true; @@ -1635,7 +1632,7 @@ static bool vfont_to_curve(Object *ob, iter_data->bisect.max = iter_data->scale_to_fit; } else { - /* It fits inside the textbox, scale it up. */ + /* It fits inside the text-box, scale it up. */ iter_data->bisect.min = iter_data->scale_to_fit; valid = true; } @@ -1697,7 +1694,7 @@ finally: } } - /* Store the effective scale, to use for the textbox lines. */ + /* Store the effective scale, to use for the text-box lines. */ cu->fsize_realtime = font_size; return ok; diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc index ff3f20479f8..07db0328f56 100644 --- a/source/blender/blenkernel/intern/volume.cc +++ b/source/blender/blenkernel/intern/volume.cc @@ -1331,9 +1331,6 @@ VolumeGridType BKE_volume_grid_type_openvdb(const openvdb::GridBase &grid) if (grid.isType<openvdb::Vec3dGrid>()) { return VOLUME_GRID_VECTOR_DOUBLE; } - if (grid.isType<openvdb::StringGrid>()) { - return VOLUME_GRID_STRING; - } if (grid.isType<openvdb::MaskGrid>()) { return VOLUME_GRID_MASK; } @@ -1369,7 +1366,6 @@ int BKE_volume_grid_channels(const VolumeGrid *grid) case VOLUME_GRID_VECTOR_DOUBLE: case VOLUME_GRID_VECTOR_INT: return 3; - case VOLUME_GRID_STRING: case VOLUME_GRID_POINTS: case VOLUME_GRID_UNKNOWN: return 0; @@ -1610,13 +1606,8 @@ struct CreateGridWithChangedResolutionOp { template<typename GridType> typename openvdb::GridBase::Ptr operator()() { - if constexpr (std::is_same_v<GridType, openvdb::StringGrid>) { - return {}; - } - else { - return create_grid_with_changed_resolution(static_cast<const GridType &>(grid), - resolution_factor); - } + return create_grid_with_changed_resolution(static_cast<const GridType &>(grid), + resolution_factor); } }; diff --git a/source/blender/blenkernel/intern/volume_render.cc b/source/blender/blenkernel/intern/volume_render.cc index ca18d6c3446..e7620be6401 100644 --- a/source/blender/blenkernel/intern/volume_render.cc +++ b/source/blender/blenkernel/intern/volume_render.cc @@ -63,7 +63,6 @@ static void extract_dense_float_voxels(const VolumeGridType grid_type, case VOLUME_GRID_VECTOR_INT: return extract_dense_voxels<openvdb::Vec3IGrid, openvdb::Vec3f>( grid, bbox, reinterpret_cast<openvdb::Vec3f *>(r_voxels)); - case VOLUME_GRID_STRING: case VOLUME_GRID_POINTS: case VOLUME_GRID_UNKNOWN: /* Zero channels to copy. */ diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index 3ee75ca21ce..b9d013d4756 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -42,6 +42,7 @@ * like M_SQRT1_2 leading to warnings with MSVC */ # include <libavcodec/avcodec.h> # include <libavformat/avformat.h> +# include <libavutil/channel_layout.h> # include <libavutil/imgutils.h> # include <libavutil/opt.h> # include <libavutil/rational.h> @@ -101,8 +102,6 @@ typedef struct FFMpegContext { printf static void ffmpeg_dict_set_int(AVDictionary **dict, const char *key, int value); -static void ffmpeg_dict_set_float(AVDictionary **dict, const char *key, float value); -static void ffmpeg_set_expert_options(RenderData *rd); static void ffmpeg_filepath_get(FFMpegContext *context, char *string, const struct RenderData *rd, @@ -414,99 +413,6 @@ static AVFrame *generate_video_frame(FFMpegContext *context, const uint8_t *pixe return context->current_frame; } -static void set_ffmpeg_property_option(IDProperty *prop, AVDictionary **dictionary) -{ - char name[128]; - char *param; - - PRINT("FFMPEG expert option: %s: ", prop->name); - - BLI_strncpy(name, prop->name, sizeof(name)); - - param = strchr(name, ':'); - - if (param) { - *param++ = '\0'; - } - - switch (prop->type) { - case IDP_STRING: - PRINT("%s.\n", IDP_String(prop)); - av_dict_set(dictionary, name, IDP_String(prop), 0); - break; - case IDP_FLOAT: - PRINT("%g.\n", IDP_Float(prop)); - ffmpeg_dict_set_float(dictionary, prop->name, IDP_Float(prop)); - break; - case IDP_INT: - PRINT("%d.\n", IDP_Int(prop)); - - if (param) { - if (IDP_Int(prop)) { - av_dict_set(dictionary, name, param, 0); - } - else { - return; - } - } - else { - ffmpeg_dict_set_int(dictionary, prop->name, IDP_Int(prop)); - } - break; - } -} - -static int ffmpeg_proprty_valid(AVCodecContext *c, const char *prop_name, IDProperty *curr) -{ - int valid = 1; - - if (STREQ(prop_name, "video")) { - if (STREQ(curr->name, "bf")) { - /* flash codec doesn't support b frames */ - valid &= c->codec_id != AV_CODEC_ID_FLV1; - } - } - - return valid; -} - -static void set_ffmpeg_properties(RenderData *rd, - AVCodecContext *c, - const char *prop_name, - AVDictionary **dictionary) -{ - IDProperty *prop; - IDProperty *curr; - - /* TODO(sergey): This is actually rather stupid, because changing - * codec settings in render panel would also set expert options. - * - * But we need ti here in order to get rid of deprecated settings - * when opening old files in new blender. - * - * For as long we don't allow editing properties in the interface - * it's all good. bug if we allow editing them, we'll need to - * replace it with some smarter code which would port settings - * from deprecated to new one. - */ - ffmpeg_set_expert_options(rd); - - if (!rd->ffcodecdata.properties) { - return; - } - - prop = IDP_GetPropertyFromGroup(rd->ffcodecdata.properties, prop_name); - if (!prop) { - return; - } - - for (curr = prop->data.group.first; curr; curr = curr->next) { - if (ffmpeg_proprty_valid(c, prop_name, curr)) { - set_ffmpeg_property_option(curr, dictionary); - } - } -} - static AVRational calc_time_base(uint den, double num, int codec_id) { /* Convert the input 'num' to an integer. Simply shift the decimal places until we get an integer @@ -561,7 +467,7 @@ static AVStream *alloc_video_stream(FFMpegContext *context, int error_size) { AVStream *st; - AVCodec *codec; + const AVCodec *codec; AVDictionary *opts = NULL; error[0] = '\0'; @@ -574,21 +480,15 @@ static AVStream *alloc_video_stream(FFMpegContext *context, /* Set up the codec context */ - context->video_codec = avcodec_alloc_context3(NULL); - AVCodecContext *c = context->video_codec; - c->codec_id = codec_id; - c->codec_type = AVMEDIA_TYPE_VIDEO; - - codec = avcodec_find_encoder(c->codec_id); + codec = avcodec_find_encoder(codec_id); if (!codec) { fprintf(stderr, "Couldn't find valid video codec\n"); - avcodec_free_context(&c); context->video_codec = NULL; return NULL; } - /* Load codec defaults into 'c'. */ - avcodec_get_context_defaults3(c, codec); + context->video_codec = avcodec_alloc_context3(codec); + AVCodecContext *c = context->video_codec; /* Get some values from the current render settings */ @@ -702,6 +602,13 @@ static AVStream *alloc_video_stream(FFMpegContext *context, } } + if (codec_id == AV_CODEC_ID_DNXHD) { + if (rd->ffcodecdata.flags & FFMPEG_LOSSLESS_OUTPUT) { + /* Set the block decision algorithm to be of the highest quality ("rd" == 2). */ + c->mb_decision = 2; + } + } + if (codec_id == AV_CODEC_ID_FFV1) { c->pix_fmt = AV_PIX_FMT_RGB32; } @@ -738,8 +645,6 @@ static AVStream *alloc_video_stream(FFMpegContext *context, 255); st->avg_frame_rate = av_inv_q(c->time_base); - set_ffmpeg_properties(rd, c, "video", &opts); - if (codec->capabilities & AV_CODEC_CAP_AUTO_THREADS) { c->thread_count = 0; } @@ -804,8 +709,7 @@ static AVStream *alloc_audio_stream(FFMpegContext *context, int error_size) { AVStream *st; - AVCodec *codec; - AVDictionary *opts = NULL; + const AVCodec *codec; error[0] = '\0'; @@ -815,24 +719,17 @@ static AVStream *alloc_audio_stream(FFMpegContext *context, } st->id = 1; - context->audio_codec = avcodec_alloc_context3(NULL); - AVCodecContext *c = context->audio_codec; - c->thread_count = BLI_system_thread_count(); - c->thread_type = FF_THREAD_SLICE; - - c->codec_id = codec_id; - c->codec_type = AVMEDIA_TYPE_AUDIO; - - codec = avcodec_find_encoder(c->codec_id); + codec = avcodec_find_encoder(codec_id); if (!codec) { fprintf(stderr, "Couldn't find valid audio codec\n"); - avcodec_free_context(&c); context->audio_codec = NULL; return NULL; } - /* Load codec defaults into 'c'. */ - avcodec_get_context_defaults3(c, codec); + context->audio_codec = avcodec_alloc_context3(codec); + AVCodecContext *c = context->audio_codec; + c->thread_count = BLI_system_thread_count(); + c->thread_type = FF_THREAD_SLICE; c->sample_rate = rd->ffcodecdata.audio_mixrate; c->bit_rate = context->ffmpeg_audio_bitrate * 1000; @@ -900,19 +797,15 @@ static AVStream *alloc_audio_stream(FFMpegContext *context, c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; } - set_ffmpeg_properties(rd, c, "audio", &opts); - - int ret = avcodec_open2(c, codec, &opts); + int ret = avcodec_open2(c, codec, NULL); if (ret < 0) { fprintf(stderr, "Couldn't initialize audio codec: %s\n", av_err2str(ret)); BLI_strncpy(error, IMB_ffmpeg_last_error(), error_size); - av_dict_free(&opts); avcodec_free_context(&c); context->audio_codec = NULL; return NULL; } - av_dict_free(&opts); /* need to prevent floating point exception when using vorbis audio codec, * initialize this value in the same way as it's done in FFmpeg itself (sergey) */ @@ -958,15 +851,6 @@ static void ffmpeg_dict_set_int(AVDictionary **dict, const char *key, int value) av_dict_set(dict, key, buffer, 0); } -static void ffmpeg_dict_set_float(AVDictionary **dict, const char *key, float value) -{ - char buffer[32]; - - BLI_snprintf(buffer, sizeof(buffer), "%.8f", value); - - av_dict_set(dict, key, buffer, 0); -} - static void ffmpeg_add_metadata_callback(void *data, const char *propname, char *propvalue, @@ -985,8 +869,7 @@ static int start_ffmpeg_impl(FFMpegContext *context, { /* Handle to the output file */ AVFormatContext *of; - AVOutputFormat *fmt; - AVDictionary *opts = NULL; + const AVOutputFormat *fmt; char name[FILE_MAX], error[1024]; const char **exts; @@ -1023,11 +906,13 @@ static int start_ffmpeg_impl(FFMpegContext *context, rectx, recty); + /* Sanity checks for the output file extensions. */ exts = get_file_extensions(context->ffmpeg_type); if (!exts) { BKE_report(reports, RPT_ERROR, "No valid formats found"); return 0; } + fmt = av_guess_format(NULL, exts[0], NULL); if (!fmt) { BKE_report(reports, RPT_ERROR, "No valid formats found"); @@ -1036,66 +921,55 @@ static int start_ffmpeg_impl(FFMpegContext *context, of = avformat_alloc_context(); if (!of) { - BKE_report(reports, RPT_ERROR, "Error opening output file"); + BKE_report(reports, RPT_ERROR, "Can't allocate ffmpeg format context"); return 0; } - /* Returns after this must 'goto fail;' */ - - of->oformat = fmt; - - /* Only bother with setting packet size & mux rate when CRF is not used. */ - if (context->ffmpeg_crf == 0) { - of->packet_size = rd->ffcodecdata.mux_packet_size; - if (context->ffmpeg_audio_codec != AV_CODEC_ID_NONE) { - ffmpeg_dict_set_int(&opts, "muxrate", rd->ffcodecdata.mux_rate); - } - else { - av_dict_set(&opts, "muxrate", "0", 0); - } - } - - ffmpeg_dict_set_int(&opts, "preload", (int)(0.5 * AV_TIME_BASE)); - - of->max_delay = (int)(0.7 * AV_TIME_BASE); - - fmt->audio_codec = context->ffmpeg_audio_codec; + enum AVCodecID audio_codec = context->ffmpeg_audio_codec; + enum AVCodecID video_codec = context->ffmpeg_codec; of->url = av_strdup(name); - /* set the codec to the user's selection */ + /* Check if we need to force change the codec because of file type codec restrictions */ switch (context->ffmpeg_type) { - case FFMPEG_AVI: - case FFMPEG_MOV: - case FFMPEG_MKV: - fmt->video_codec = context->ffmpeg_codec; - break; case FFMPEG_OGG: - fmt->video_codec = AV_CODEC_ID_THEORA; + video_codec = AV_CODEC_ID_THEORA; break; case FFMPEG_DV: - fmt->video_codec = AV_CODEC_ID_DVVIDEO; + video_codec = AV_CODEC_ID_DVVIDEO; break; case FFMPEG_MPEG1: - fmt->video_codec = AV_CODEC_ID_MPEG1VIDEO; + video_codec = AV_CODEC_ID_MPEG1VIDEO; break; case FFMPEG_MPEG2: - fmt->video_codec = AV_CODEC_ID_MPEG2VIDEO; + video_codec = AV_CODEC_ID_MPEG2VIDEO; break; case FFMPEG_H264: - fmt->video_codec = AV_CODEC_ID_H264; + video_codec = AV_CODEC_ID_H264; break; case FFMPEG_XVID: - fmt->video_codec = AV_CODEC_ID_MPEG4; + video_codec = AV_CODEC_ID_MPEG4; break; case FFMPEG_FLV: - fmt->video_codec = AV_CODEC_ID_FLV1; + video_codec = AV_CODEC_ID_FLV1; break; - case FFMPEG_MPEG4: default: - fmt->video_codec = context->ffmpeg_codec; + /* These containers are not restricted to any specific codec types. + * Currently we expect these to be .avi, .mov, .mkv, and .mp4. + */ + video_codec = context->ffmpeg_codec; break; } - if (fmt->video_codec == AV_CODEC_ID_DVVIDEO) { + + /* Returns after this must 'goto fail;' */ + +# if LIBAVFORMAT_VERSION_MAJOR >= 59 + of->oformat = fmt; +# else + /* *DEPRECATED* 2022/08/01 For FFMPEG (<5.0) remove this else branch and the `ifdef` above. */ + of->oformat = (AVOutputFormat *)fmt; +# endif + + if (video_codec == AV_CODEC_ID_DVVIDEO) { if (rectx != 720) { BKE_report(reports, RPT_ERROR, "Render width has to be 720 pixels for DV!"); goto fail; @@ -1111,7 +985,7 @@ static int start_ffmpeg_impl(FFMpegContext *context, } if (context->ffmpeg_type == FFMPEG_DV) { - fmt->audio_codec = AV_CODEC_ID_PCM_S16LE; + audio_codec = AV_CODEC_ID_PCM_S16LE; if (context->ffmpeg_audio_codec != AV_CODEC_ID_NONE && rd->ffcodecdata.audio_mixrate != 48000 && rd->ffcodecdata.audio_channels != 2) { BKE_report(reports, RPT_ERROR, "FFMPEG only supports 48khz / stereo audio for DV!"); @@ -1119,9 +993,9 @@ static int start_ffmpeg_impl(FFMpegContext *context, } } - if (fmt->video_codec != AV_CODEC_ID_NONE) { + if (video_codec != AV_CODEC_ID_NONE) { context->video_stream = alloc_video_stream( - context, rd, fmt->video_codec, of, rectx, recty, error, sizeof(error)); + context, rd, video_codec, of, rectx, recty, error, sizeof(error)); PRINT("alloc video stream %p\n", context->video_stream); if (!context->video_stream) { if (error[0]) { @@ -1137,8 +1011,7 @@ static int start_ffmpeg_impl(FFMpegContext *context, } if (context->ffmpeg_audio_codec != AV_CODEC_ID_NONE) { - context->audio_stream = alloc_audio_stream( - context, rd, fmt->audio_codec, of, error, sizeof(error)); + context->audio_stream = alloc_audio_stream(context, rd, audio_codec, of, error, sizeof(error)); if (!context->audio_stream) { if (error[0]) { BKE_report(reports, RPT_ERROR, error); @@ -1175,7 +1048,6 @@ static int start_ffmpeg_impl(FFMpegContext *context, context->outfile = of; av_dump_format(of, 0, name, 1); - av_dict_free(&opts); return 1; @@ -1192,7 +1064,6 @@ fail: context->audio_stream = NULL; } - av_dict_free(&opts); avformat_free_context(of); return 0; } @@ -1526,198 +1397,17 @@ void BKE_ffmpeg_end(void *context_v) end_ffmpeg_impl(context, false); } -/* properties */ - -void BKE_ffmpeg_property_del(RenderData *rd, void *type, void *prop_) -{ - struct IDProperty *prop = (struct IDProperty *)prop_; - IDProperty *group; - - if (!rd->ffcodecdata.properties) { - return; - } - - group = IDP_GetPropertyFromGroup(rd->ffcodecdata.properties, type); - if (group && prop) { - IDP_FreeFromGroup(group, prop); - } -} - -static IDProperty *BKE_ffmpeg_property_add(RenderData *rd, - const char *type, - const AVOption *o, - const AVOption *parent) -{ - AVCodecContext c; - IDProperty *group; - IDProperty *prop; - IDPropertyTemplate val; - int idp_type; - char name[256]; - - val.i = 0; - - avcodec_get_context_defaults3(&c, NULL); - - if (!rd->ffcodecdata.properties) { - rd->ffcodecdata.properties = IDP_New(IDP_GROUP, &val, "ffmpeg"); - } - - group = IDP_GetPropertyFromGroup(rd->ffcodecdata.properties, type); - - if (!group) { - group = IDP_New(IDP_GROUP, &val, type); - IDP_AddToGroup(rd->ffcodecdata.properties, group); - } - - if (parent) { - BLI_snprintf(name, sizeof(name), "%s:%s", parent->name, o->name); - } - else { - BLI_strncpy(name, o->name, sizeof(name)); - } - - PRINT("ffmpeg_property_add: %s %s\n", type, name); - - prop = IDP_GetPropertyFromGroup(group, name); - if (prop) { - return prop; - } - - switch (o->type) { - case AV_OPT_TYPE_INT: - case AV_OPT_TYPE_INT64: - val.i = o->default_val.i64; - idp_type = IDP_INT; - break; - case AV_OPT_TYPE_DOUBLE: - case AV_OPT_TYPE_FLOAT: - val.f = o->default_val.dbl; - idp_type = IDP_FLOAT; - break; - case AV_OPT_TYPE_STRING: - val.string.str = - (char - *)" "; - val.string.len = 80; - idp_type = IDP_STRING; - break; - case AV_OPT_TYPE_CONST: - val.i = 1; - idp_type = IDP_INT; - break; - default: - return NULL; - } - prop = IDP_New(idp_type, &val, name); - IDP_AddToGroup(group, prop); - return prop; -} - -/* not all versions of ffmpeg include that, so here we go ... */ - -int BKE_ffmpeg_property_add_string(RenderData *rd, const char *type, const char *str) -{ - AVCodecContext c; - const AVOption *o = NULL; - const AVOption *p = NULL; - char name_[128]; - char *name; - char *param; - IDProperty *prop = NULL; - - avcodec_get_context_defaults3(&c, NULL); - - BLI_strncpy(name_, str, sizeof(name_)); - - name = name_; - while (*name == ' ') { - name++; - } - - param = strchr(name, ':'); - - if (!param) { - param = strchr(name, ' '); - } - if (param) { - *param++ = '\0'; - while (*param == ' ') { - param++; - } - } - - o = av_opt_find(&c, name, NULL, 0, AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ); - if (!o) { - PRINT("Ignoring unknown expert option %s\n", str); - return 0; - } - if (param && o->type == AV_OPT_TYPE_CONST) { - return 0; - } - if (param && o->type != AV_OPT_TYPE_CONST && o->unit) { - p = av_opt_find(&c, param, o->unit, 0, AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ); - if (p) { - prop = BKE_ffmpeg_property_add(rd, (char *)type, p, o); - } - else { - PRINT("Ignoring unknown expert option %s\n", str); - } - } - else { - prop = BKE_ffmpeg_property_add(rd, (char *)type, o, NULL); - } - - if (!prop) { - return 0; - } - - if (param && !p) { - switch (prop->type) { - case IDP_INT: - IDP_Int(prop) = atoi(param); - break; - case IDP_FLOAT: - IDP_Float(prop) = atof(param); - break; - case IDP_STRING: - strncpy(IDP_String(prop), param, prop->len); - break; - } - } - return 1; -} - -static void ffmpeg_set_expert_options(RenderData *rd) -{ - int codec_id = rd->ffcodecdata.codec; - - if (rd->ffcodecdata.properties) { - IDP_FreePropertyContent(rd->ffcodecdata.properties); - } - - if (codec_id == AV_CODEC_ID_DNXHD) { - if (rd->ffcodecdata.flags & FFMPEG_LOSSLESS_OUTPUT) { - BKE_ffmpeg_property_add_string(rd, "video", "mbd:rd"); - } - } -} - void BKE_ffmpeg_preset_set(RenderData *rd, int preset) { - int isntsc = (rd->frs_sec != 25); - - if (rd->ffcodecdata.properties) { - IDP_FreePropertyContent(rd->ffcodecdata.properties); - } + bool is_ntsc = (rd->frs_sec != 25); switch (preset) { case FFMPEG_PRESET_VCD: rd->ffcodecdata.type = FFMPEG_MPEG1; rd->ffcodecdata.video_bitrate = 1150; rd->xsch = 352; - rd->ysch = isntsc ? 240 : 288; - rd->ffcodecdata.gop_size = isntsc ? 18 : 15; + rd->ysch = is_ntsc ? 240 : 288; + rd->ffcodecdata.gop_size = is_ntsc ? 18 : 15; rd->ffcodecdata.rc_max_rate = 1150; rd->ffcodecdata.rc_min_rate = 1150; rd->ffcodecdata.rc_buffer_size = 40 * 8; @@ -1729,8 +1419,8 @@ void BKE_ffmpeg_preset_set(RenderData *rd, int preset) rd->ffcodecdata.type = FFMPEG_MPEG2; rd->ffcodecdata.video_bitrate = 2040; rd->xsch = 480; - rd->ysch = isntsc ? 480 : 576; - rd->ffcodecdata.gop_size = isntsc ? 18 : 15; + rd->ysch = is_ntsc ? 480 : 576; + rd->ffcodecdata.gop_size = is_ntsc ? 18 : 15; rd->ffcodecdata.rc_max_rate = 2516; rd->ffcodecdata.rc_min_rate = 0; rd->ffcodecdata.rc_buffer_size = 224 * 8; @@ -1747,7 +1437,7 @@ void BKE_ffmpeg_preset_set(RenderData *rd, int preset) rd->ysch = isntsc ? 480 : 576; # endif - rd->ffcodecdata.gop_size = isntsc ? 18 : 15; + rd->ffcodecdata.gop_size = is_ntsc ? 18 : 15; rd->ffcodecdata.rc_max_rate = 9000; rd->ffcodecdata.rc_min_rate = 0; rd->ffcodecdata.rc_buffer_size = 224 * 8; @@ -1758,14 +1448,14 @@ void BKE_ffmpeg_preset_set(RenderData *rd, int preset) case FFMPEG_PRESET_DV: rd->ffcodecdata.type = FFMPEG_DV; rd->xsch = 720; - rd->ysch = isntsc ? 480 : 576; + rd->ysch = is_ntsc ? 480 : 576; break; case FFMPEG_PRESET_H264: rd->ffcodecdata.type = FFMPEG_AVI; rd->ffcodecdata.codec = AV_CODEC_ID_H264; rd->ffcodecdata.video_bitrate = 6000; - rd->ffcodecdata.gop_size = isntsc ? 18 : 15; + rd->ffcodecdata.gop_size = is_ntsc ? 18 : 15; rd->ffcodecdata.rc_max_rate = 9000; rd->ffcodecdata.rc_min_rate = 0; rd->ffcodecdata.rc_buffer_size = 224 * 8; @@ -1786,7 +1476,7 @@ void BKE_ffmpeg_preset_set(RenderData *rd, int preset) } rd->ffcodecdata.video_bitrate = 6000; - rd->ffcodecdata.gop_size = isntsc ? 18 : 15; + rd->ffcodecdata.gop_size = is_ntsc ? 18 : 15; rd->ffcodecdata.rc_max_rate = 9000; rd->ffcodecdata.rc_min_rate = 0; rd->ffcodecdata.rc_buffer_size = 224 * 8; @@ -1794,8 +1484,6 @@ void BKE_ffmpeg_preset_set(RenderData *rd, int preset) rd->ffcodecdata.mux_rate = 10080000; break; } - - ffmpeg_set_expert_options(rd); } void BKE_ffmpeg_image_type_verify(RenderData *rd, ImageFormatData *imf) @@ -1841,11 +1529,6 @@ void BKE_ffmpeg_image_type_verify(RenderData *rd, ImageFormatData *imf) } } -void BKE_ffmpeg_codec_settings_verify(RenderData *rd) -{ - ffmpeg_set_expert_options(rd); -} - bool BKE_ffmpeg_alpha_channel_is_supported(const RenderData *rd) { int codec = rd->ffcodecdata.codec; diff --git a/source/blender/blenlib/BLI_array.h b/source/blender/blenlib/BLI_array.h index d3bb3401d7e..80e865ded62 100644 --- a/source/blender/blenlib/BLI_array.h +++ b/source/blender/blenlib/BLI_array.h @@ -146,6 +146,17 @@ void _bli_array_grow_func(void **arr_p, */ #define BLI_array_fake_user(arr) ((void)_##arr##_len, (void)_##arr##_static) +/** + * Trim excess items from the array (when they exist). + */ +#define BLI_array_trim(arr) \ + { \ + if (_bli_array_totalsize_dynamic(arr) != _##arr##_len) { \ + arr = MEM_reallocN(arr, sizeof(*arr) * _##arr##_len); \ + } \ + } \ + ((void)0) + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenlib/BLI_array.hh b/source/blender/blenlib/BLI_array.hh index a580f12851e..91dfc81ae27 100644 --- a/source/blender/blenlib/BLI_array.hh +++ b/source/blender/blenlib/BLI_array.hh @@ -278,18 +278,20 @@ class Array { } /** - * Return a reference to the last element in the array. - * This invokes undefined behavior when the array is empty. + * Return a reference to the nth last element. + * This invokes undefined behavior when the array is too short. */ - const T &last() const + const T &last(const int64_t n = 0) const { - BLI_assert(size_ > 0); - return *(data_ + size_ - 1); + BLI_assert(n >= 0); + BLI_assert(n < size_); + return *(data_ + size_ - 1 - n); } - T &last() + T &last(const int64_t n = 0) { - BLI_assert(size_ > 0); - return *(data_ + size_ - 1); + BLI_assert(n >= 0); + BLI_assert(n < size_); + return *(data_ + size_ - 1 - n); } /** diff --git a/source/blender/blenlib/BLI_bounds.hh b/source/blender/blenlib/BLI_bounds.hh new file mode 100644 index 00000000000..d20382ed500 --- /dev/null +++ b/source/blender/blenlib/BLI_bounds.hh @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/** \file + * \ingroup bli + * + * Generic algorithms for finding the largest and smallest elements in a span. + */ + +#include <optional> + +#include "BLI_math_vector.hh" +#include "BLI_task.hh" + +namespace blender::bounds { + +template<typename T> struct MinMaxResult { + T min; + T max; +}; + +/** + * Find the smallest and largest values element-wise in the span. + */ +template<typename T> static std::optional<MinMaxResult<T>> min_max(Span<T> values) +{ + if (values.is_empty()) { + return std::nullopt; + } + return threading::parallel_reduce( + values.index_range(), + 1024, + MinMaxResult<T>(), + [&](IndexRange range, const MinMaxResult<T> &init) { + MinMaxResult<T> result = init; + for (const int i : range) { + math::min_max(values[i], result.min, result.max); + } + return result; + }, + [](const MinMaxResult<T> &a, const MinMaxResult<T> &b) { + return MinMaxResult<T>{math::min(a.min, b.min), math::max(a.max, b.max)}; + }); +} + +/** + * Find the smallest and largest values element-wise in the span, adding the radius to each element + * first. The template type T is expected to have an addition operator implemented with RadiusT. + */ +template<typename T, typename RadiusT> +static std::optional<MinMaxResult<T>> min_max_with_radii(Span<T> values, Span<RadiusT> radii) +{ + BLI_assert(values.size() == radii.size()); + if (values.is_empty()) { + return std::nullopt; + } + return threading::parallel_reduce( + values.index_range(), + 1024, + MinMaxResult<T>(), + [&](IndexRange range, const MinMaxResult<T> &init) { + MinMaxResult<T> result = init; + for (const int i : range) { + result.min = math::min(values[i] - radii[i], result.min); + result.max = math::max(values[i] + radii[i], result.max); + } + return result; + }, + [](const MinMaxResult<T> &a, const MinMaxResult<T> &b) { + return MinMaxResult<T>{math::min(a.min, b.min), math::max(a.max, b.max)}; + }); +} + +} // namespace blender::bounds diff --git a/source/blender/blenlib/BLI_enumerable_thread_specific.hh b/source/blender/blenlib/BLI_enumerable_thread_specific.hh index 339f02dce0f..51bf8d06cf1 100644 --- a/source/blender/blenlib/BLI_enumerable_thread_specific.hh +++ b/source/blender/blenlib/BLI_enumerable_thread_specific.hh @@ -3,7 +3,25 @@ #pragma once #ifdef WITH_TBB -# include <tbb/enumerable_thread_specific.h> + +# ifdef WITH_TBB +/* Quiet top level deprecation message, unrelated to API usage here. */ +# if defined(WIN32) && !defined(NOMINMAX) +/* TBB includes Windows.h which will define min/max macros causing issues + * when we try to use std::min and std::max later on. */ +# define NOMINMAX +# define TBB_MIN_MAX_CLEANUP +# endif +# include <tbb/enumerable_thread_specific.h> +# ifdef WIN32 +/* We cannot keep this defined, since other parts of the code deal with this on their own, leading + * to multiple define warnings unless we un-define this, however we can only undefine this if we + * were the ones that made the definition earlier. */ +# ifdef TBB_MIN_MAX_CLEANUP +# undef NOMINMAX +# endif +# endif +# endif #endif #include <atomic> diff --git a/source/blender/blenlib/BLI_hash_tables.hh b/source/blender/blenlib/BLI_hash_tables.hh index 40b20dbd84f..334634613a2 100644 --- a/source/blender/blenlib/BLI_hash_tables.hh +++ b/source/blender/blenlib/BLI_hash_tables.hh @@ -8,6 +8,7 @@ * This file contains code that can be shared between different hash table implementations. */ +#include <algorithm> #include <cmath> #include "BLI_allocator.hh" diff --git a/source/blender/blenlib/BLI_index_mask.hh b/source/blender/blenlib/BLI_index_mask.hh index 7f4e7be543b..3decd8b9441 100644 --- a/source/blender/blenlib/BLI_index_mask.hh +++ b/source/blender/blenlib/BLI_index_mask.hh @@ -209,6 +209,18 @@ class IndexMask { return indices_.is_empty(); } + bool contained_in(const IndexRange range) const + { + if (indices_.is_empty()) { + return true; + } + if (range.size() < indices_.size()) { + return false; + } + return indices_.first() >= range.first() && indices_.last() <= range.last(); + } + + IndexMask slice(int64_t start, int64_t size) const; IndexMask slice(IndexRange slice) const; /** * Create a sub-mask that is also shifted to the beginning. @@ -229,6 +241,30 @@ class IndexMask { * so that the first index in the output is zero. */ IndexMask slice_and_offset(IndexRange slice, Vector<int64_t> &r_new_indices) const; + + /** + * Get a new mask that contains all the indices that are not in the current mask. + * If necessary, the indices referenced by the new mask are inserted in #r_new_indices. + */ + IndexMask invert(const IndexRange full_range, Vector<int64_t> &r_new_indices) const; + + /** + * Get all contiguous index ranges within the mask. + */ + Vector<IndexRange> extract_ranges() const; + + /** + * Similar to #extract ranges, but works on the inverted mask. So the returned ranges are + * in-between the indices in the mask. + * + * Using this method is generally more efficient than first inverting the index mask and then + * extracting the ranges. + * + * If #r_skip_amounts is passed in, it will contain the number of indices that have been skipped + * before each range in the return value starts. + */ + Vector<IndexRange> extract_ranges_invert(const IndexRange full_range, + Vector<int64_t> *r_skip_amounts) const; }; } // namespace blender diff --git a/source/blender/blenlib/BLI_index_mask_ops.hh b/source/blender/blenlib/BLI_index_mask_ops.hh new file mode 100644 index 00000000000..48a1f27a2fa --- /dev/null +++ b/source/blender/blenlib/BLI_index_mask_ops.hh @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/** \file + * \ingroup bli + * + * This is separate from `BLI_index_mask.hh` because it includes headers just `IndexMask` shouldn't + * depend on. + */ + +#include "BLI_enumerable_thread_specific.hh" +#include "BLI_index_mask.hh" +#include "BLI_task.hh" +#include "BLI_vector.hh" + +namespace blender::index_mask_ops { + +namespace detail { +IndexMask find_indices_based_on_predicate__merge( + IndexMask indices_to_check, + threading::EnumerableThreadSpecific<Vector<Vector<int64_t>>> &sub_masks, + Vector<int64_t> &r_indices); +} // namespace detail + +/** + * Evaluate the #predicate for all indices in #indices_to_check and return a mask that contains all + * indices where the predicate was true. + * + * #r_indices indices is only used if necessary. + */ +template<typename Predicate> +inline IndexMask find_indices_based_on_predicate(const IndexMask indices_to_check, + const int64_t parallel_grain_size, + Vector<int64_t> &r_indices, + const Predicate &predicate) +{ + /* Evaluate predicate in parallel. Since the size of the final mask is not known yet, many + * smaller vectors have to be filled with all indices where the predicate is true. Those smaller + * vectors are joined afterwards. */ + threading::EnumerableThreadSpecific<Vector<Vector<int64_t>>> sub_masks; + threading::parallel_for( + indices_to_check.index_range(), parallel_grain_size, [&](const IndexRange range) { + const IndexMask sub_mask = indices_to_check.slice(range); + Vector<int64_t> masked_indices; + for (const int64_t i : sub_mask) { + if (predicate(i)) { + masked_indices.append(i); + } + } + if (!masked_indices.is_empty()) { + sub_masks.local().append(std::move(masked_indices)); + } + }); + + /* This part doesn't have to be in the header. */ + return detail::find_indices_based_on_predicate__merge(indices_to_check, sub_masks, r_indices); +} + +} // namespace blender::index_mask_ops diff --git a/source/blender/blenlib/BLI_math_base.hh b/source/blender/blenlib/BLI_math_base.hh new file mode 100644 index 00000000000..6a988eda8a9 --- /dev/null +++ b/source/blender/blenlib/BLI_math_base.hh @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. */ + +#pragma once + +/** \file + * \ingroup bli + */ + +#include <algorithm> +#include <cmath> +#include <type_traits> + +#include "BLI_math_base_safe.h" +#include "BLI_math_vec_types.hh" +#include "BLI_utildefines.h" + +#ifdef WITH_GMP +# include "BLI_math_mpq.hh" +#endif + +namespace blender::math { + +template<typename T> inline bool is_zero(const T &a) +{ + return a == T(0); +} + +template<typename T> inline bool is_any_zero(const T &a) +{ + return is_zero(a); +} + +template<typename T> inline T abs(const T &a) +{ + return std::abs(a); +} + +template<typename T> inline T min(const T &a, const T &b) +{ + return std::min(a, b); +} + +template<typename T> inline T max(const T &a, const T &b) +{ + return std::max(a, b); +} + +template<typename T> inline T clamp(const T &a, const T &min, const T &max) +{ + return std::clamp(a, min, max); +} + +template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))> inline T mod(const T &a, const T &b) +{ + return std::fmod(a, b); +} + +template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))> +inline T safe_mod(const T &a, const T &b) +{ + return (b != 0) ? std::fmod(a, b) : 0; +} + +template<typename T> inline void min_max(const T &value, T &min, T &max) +{ + min = math::min(value, min); + max = math::max(value, max); +} + +template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))> +inline T safe_divide(const T &a, const T &b) +{ + return (b != 0) ? a / b : T(0.0f); +} + +template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))> inline T floor(const T &a) +{ + return std::floor(a); +} + +template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))> inline T ceil(const T &a) +{ + return std::ceil(a); +} + +template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))> inline T fract(const T &a) +{ + return a - std::floor(a); +} + +template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))> +inline T interpolate(const T &a, const T &b, const T &t) +{ + return a * (1 - t) + b * t; +} + +template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))> +inline T midpoint(const T &a, const T &b) +{ + return (a + b) * T(0.5); +} + +} // namespace blender::math diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h index 3d2ac5688ff..4bba84f2e29 100644 --- a/source/blender/blenlib/BLI_math_geom.h +++ b/source/blender/blenlib/BLI_math_geom.h @@ -303,6 +303,9 @@ float dist_squared_to_projected_aabb_simple(const float projmat[4][4], const float bbmin[3], const float bbmax[3]); +/** Returns the distance between two 2D line segments. */ +float dist_seg_seg_v2(const float a1[3], const float a2[3], const float b1[3], const float b2[3]); + float closest_to_ray_v3(float r_close[3], const float p[3], const float ray_orig[3], diff --git a/source/blender/blenlib/BLI_math_vec_types.hh b/source/blender/blenlib/BLI_math_vec_types.hh index 8e897870098..389307e331d 100644 --- a/source/blender/blenlib/BLI_math_vec_types.hh +++ b/source/blender/blenlib/BLI_math_vec_types.hh @@ -14,6 +14,10 @@ #include "BLI_utildefines.h" +#ifdef WITH_GMP +# include "BLI_math_mpq.hh" +#endif + namespace blender { /* clang-format off */ @@ -60,16 +64,6 @@ template<typename T> uint64_t vector_hash(const T &vec) return result; } -template<typename T> inline bool is_any_zero(const T &a) -{ - for (int i = 0; i < T::type_length; i++) { - if (a[i] == T::base_type(0)) { - return true; - } - } - return false; -} - } // namespace math template<typename T, int Size> struct vec_base : public vec_struct_base<T, Size> { @@ -349,7 +343,9 @@ template<typename T, int Size> struct vec_base : public vec_struct_base<T, Size> friend vec_base operator/(const vec_base &a, const vec_base &b) { - BLI_assert(!math::is_any_zero(b)); + for (int i = 0; i < Size; i++) { + BLI_assert(b[i] != T(0)); + } BLI_VEC_OP_IMPL(ret, i, ret[i] = a[i] / b[i]); } @@ -361,7 +357,9 @@ template<typename T, int Size> struct vec_base : public vec_struct_base<T, Size> friend vec_base operator/(T a, const vec_base &b) { - BLI_assert(!math::is_any_zero(b)); + for (int i = 0; i < Size; i++) { + BLI_assert(b[i] != T(0)); + } BLI_VEC_OP_IMPL(ret, i, ret[i] = a / b[i]); } @@ -505,7 +503,9 @@ template<typename T, int Size> struct vec_base : public vec_struct_base<T, Size> BLI_INT_OP(T) friend vec_base operator%(const vec_base &a, const vec_base &b) { - BLI_assert(!math::is_any_zero(b)); + for (int i = 0; i < Size; i++) { + BLI_assert(b[i] != T(0)); + } BLI_VEC_OP_IMPL(ret, i, ret[i] = a[i] % b[i]); } @@ -579,4 +579,13 @@ using double2 = vec_base<double, 2>; using double3 = vec_base<double, 3>; using double4 = vec_base<double, 4>; +template<typename T> +inline constexpr bool is_math_float_type = (std::is_floating_point_v<T> +#ifdef WITH_GMP + || std::is_same_v<T, mpq_class> +#endif +); + +template<typename T> inline constexpr bool is_math_integral_type = std::is_integral_v<T>; + } // namespace blender diff --git a/source/blender/blenlib/BLI_math_vector.hh b/source/blender/blenlib/BLI_math_vector.hh index d2ef2a1c5c8..7c848eeb145 100644 --- a/source/blender/blenlib/BLI_math_vector.hh +++ b/source/blender/blenlib/BLI_math_vector.hh @@ -15,10 +15,6 @@ #include "BLI_span.hh" #include "BLI_utildefines.h" -#ifdef WITH_GMP -# include "BLI_math_mpq.hh" -#endif - namespace blender::math { #ifndef NDEBUG @@ -33,277 +29,303 @@ namespace blender::math { # define BLI_ASSERT_UNIT(v) (void)(v) #endif -#define bT typename T::base_type - -#ifdef WITH_GMP -# define BLI_ENABLE_IF_FLT_VEC(T) \ - BLI_ENABLE_IF((std::is_floating_point_v<typename T::base_type> || \ - std::is_same_v<typename T::base_type, mpq_class>)) -#else -# define BLI_ENABLE_IF_FLT_VEC(T) BLI_ENABLE_IF((std::is_floating_point_v<typename T::base_type>)) -#endif - -#define BLI_ENABLE_IF_INT_VEC(T) BLI_ENABLE_IF((std::is_integral_v<typename T::base_type>)) - -template<typename T> inline bool is_zero(const T &a) +template<typename T, int Size> inline bool is_zero(const vec_base<T, Size> &a) { - for (int i = 0; i < T::type_length; i++) { - if (a[i] != bT(0)) { + for (int i = 0; i < Size; i++) { + if (a[i] != T(0)) { return false; } } return true; } -template<typename T> inline T abs(const T &a) +template<typename T, int Size> inline bool is_any_zero(const vec_base<T, Size> &a) { - T result; - for (int i = 0; i < T::type_length; i++) { + for (int i = 0; i < Size; i++) { + if (a[i] == T(0)) { + return true; + } + } + return false; +} + +template<typename T, int Size> inline vec_base<T, Size> abs(const vec_base<T, Size> &a) +{ + vec_base<T, Size> result; + for (int i = 0; i < Size; i++) { result[i] = a[i] >= 0 ? a[i] : -a[i]; } return result; } -template<typename T> inline T min(const T &a, const T &b) +template<typename T, int Size> +inline vec_base<T, Size> min(const vec_base<T, Size> &a, const vec_base<T, Size> &b) { - T result; - for (int i = 0; i < T::type_length; i++) { + vec_base<T, Size> result; + for (int i = 0; i < Size; i++) { result[i] = a[i] < b[i] ? a[i] : b[i]; } return result; } -template<typename T> inline T max(const T &a, const T &b) +template<typename T, int Size> +inline vec_base<T, Size> max(const vec_base<T, Size> &a, const vec_base<T, Size> &b) { - T result; - for (int i = 0; i < T::type_length; i++) { + vec_base<T, Size> result; + for (int i = 0; i < Size; i++) { result[i] = a[i] > b[i] ? a[i] : b[i]; } return result; } -template<typename T> inline T clamp(const T &a, const T &min_v, const T &max_v) +template<typename T, int Size> +inline T clamp(const vec_base<T, Size> &a, + const vec_base<T, Size> &min, + const vec_base<T, Size> &max) { - T result = a; - for (int i = 0; i < T::type_length; i++) { - CLAMP(result[i], min_v[i], max_v[i]); + vec_base<T, Size> result = a; + for (int i = 0; i < Size; i++) { + std::clamp(result[i], min[i], max[i]); } return result; } -template<typename T> inline T clamp(const T &a, const bT &min_v, const bT &max_v) +template<typename T, int Size> +inline vec_base<T, Size> clamp(const vec_base<T, Size> &a, const T &min, const T &max) { - T result = a; - for (int i = 0; i < T::type_length; i++) { - CLAMP(result[i], min_v, max_v); + vec_base<T, Size> result = a; + for (int i = 0; i < Size; i++) { + std::clamp(result[i], min, max); } return result; } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T mod(const T &a, const T &b) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline vec_base<T, Size> mod(const vec_base<T, Size> &a, const vec_base<T, Size> &b) { - T result; - for (int i = 0; i < T::type_length; i++) { + vec_base<T, Size> result; + for (int i = 0; i < Size; i++) { BLI_assert(b[i] != 0); result[i] = std::fmod(a[i], b[i]); } return result; } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T mod(const T &a, bT b) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline vec_base<T, Size> mod(const vec_base<T, Size> &a, const T &b) { BLI_assert(b != 0); - T result; - for (int i = 0; i < T::type_length; i++) { + vec_base<T, Size> result; + for (int i = 0; i < Size; i++) { result[i] = std::fmod(a[i], b); } return result; } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T safe_mod(const T &a, const T &b) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline T safe_mod(const vec_base<T, Size> &a, const vec_base<T, Size> &b) { - T result; - for (int i = 0; i < T::type_length; i++) { + vec_base<T, Size> result; + for (int i = 0; i < Size; i++) { result[i] = (b[i] != 0) ? std::fmod(a[i], b[i]) : 0; } return result; } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T safe_mod(const T &a, bT b) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline T safe_mod(const vec_base<T, Size> &a, const T &b) { if (b == 0) { - return T(0.0f); + return vec_base<T, Size>(0); } - T result; - for (int i = 0; i < T::type_length; i++) { + vec_base<T, Size> result; + for (int i = 0; i < Size; i++) { result[i] = std::fmod(a[i], b); } return result; } -template<typename T> inline void min_max(const T &vector, T &min_vec, T &max_vec) +template<typename T, int Size> +inline void min_max(const vec_base<T, Size> &vector, + vec_base<T, Size> &min, + vec_base<T, Size> &max) { - min_vec = min(vector, min_vec); - max_vec = max(vector, max_vec); + min = math::min(vector, min); + max = math::max(vector, max); } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T safe_divide(const T &a, const T &b) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline vec_base<T, Size> safe_divide(const vec_base<T, Size> &a, const vec_base<T, Size> &b) { - T result; - for (int i = 0; i < T::type_length; i++) { + vec_base<T, Size> result; + for (int i = 0; i < Size; i++) { result[i] = (b[i] == 0) ? 0 : a[i] / b[i]; } return result; } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T safe_divide(const T &a, const bT b) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline vec_base<T, Size> safe_divide(const vec_base<T, Size> &a, const T &b) { - return (b != 0) ? a / b : T(0.0f); + return (b != 0) ? a / b : vec_base<T, Size>(0.0f); } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T floor(const T &a) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline vec_base<T, Size> floor(const vec_base<T, Size> &a) { - T result; - for (int i = 0; i < T::type_length; i++) { + vec_base<T, Size> result; + for (int i = 0; i < Size; i++) { result[i] = std::floor(a[i]); } return result; } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T ceil(const T &a) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline vec_base<T, Size> ceil(const vec_base<T, Size> &a) { - T result; - for (int i = 0; i < T::type_length; i++) { + vec_base<T, Size> result; + for (int i = 0; i < Size; i++) { result[i] = std::ceil(a[i]); } return result; } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T fract(const T &a) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline vec_base<T, Size> fract(const vec_base<T, Size> &a) { - T result; - for (int i = 0; i < T::type_length; i++) { + vec_base<T, Size> result; + for (int i = 0; i < Size; i++) { result[i] = a[i] - std::floor(a[i]); } return result; } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline bT dot(const T &a, const T &b) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline T dot(const vec_base<T, Size> &a, const vec_base<T, Size> &b) { - bT result = a[0] * b[0]; - for (int i = 1; i < T::type_length; i++) { + T result = a[0] * b[0]; + for (int i = 1; i < Size; i++) { result += a[i] * b[i]; } return result; } -template<typename T> inline bT length_manhattan(const T &a) +template<typename T, int Size> inline T length_manhattan(const vec_base<T, Size> &a) { - bT result = std::abs(a[0]); - for (int i = 1; i < T::type_length; i++) { + T result = std::abs(a[0]); + for (int i = 1; i < Size; i++) { result += std::abs(a[i]); } return result; } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline bT length_squared(const T &a) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline T length_squared(const vec_base<T, Size> &a) { return dot(a, a); } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline bT length(const T &a) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline T length(const vec_base<T, Size> &a) { return std::sqrt(length_squared(a)); } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline bT distance_manhattan(const T &a, const T &b) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline T distance_manhattan(const vec_base<T, Size> &a, const vec_base<T, Size> &b) { return length_manhattan(a - b); } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline bT distance_squared(const T &a, const T &b) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline T distance_squared(const vec_base<T, Size> &a, const vec_base<T, Size> &b) { return length_squared(a - b); } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline bT distance(const T &a, const T &b) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline T distance(const vec_base<T, Size> &a, const vec_base<T, Size> &b) { return length(a - b); } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T reflect(const T &incident, const T &normal) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline vec_base<T, Size> reflect(const vec_base<T, Size> &incident, + const vec_base<T, Size> &normal) { BLI_ASSERT_UNIT(normal); return incident - 2.0 * dot(normal, incident) * normal; } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> -inline T refract(const T &incident, const T &normal, const bT eta) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline vec_base<T, Size> refract(const vec_base<T, Size> &incident, + const vec_base<T, Size> &normal, + const T &eta) { float dot_ni = dot(normal, incident); float k = 1.0f - eta * eta * (1.0f - dot_ni * dot_ni); if (k < 0.0f) { - return T(0.0f); + return vec_base<T, Size>(0.0f); } return eta * incident - (eta * dot_ni + sqrt(k)) * normal; } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T project(const T &p, const T &v_proj) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline vec_base<T, Size> project(const vec_base<T, Size> &p, const vec_base<T, Size> &v_proj) { if (UNLIKELY(is_zero(v_proj))) { - return T(0.0f); + return vec_base<T, Size>(0.0f); } return v_proj * (dot(p, v_proj) / dot(v_proj, v_proj)); } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> -inline T normalize_and_get_length(const T &v, bT &out_length) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline vec_base<T, Size> normalize_and_get_length(const vec_base<T, Size> &v, T &out_length) { out_length = length_squared(v); /* A larger value causes normalize errors in a scaled down models with camera extreme close. */ - constexpr bT threshold = std::is_same_v<bT, double> ? 1.0e-70 : 1.0e-35f; + constexpr T threshold = std::is_same_v<T, double> ? 1.0e-70 : 1.0e-35f; if (out_length > threshold) { out_length = sqrt(out_length); return v / out_length; } /* Either the vector is small or one of it's values contained `nan`. */ out_length = 0.0; - return T(0.0); + return vec_base<T, Size>(0.0); } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T normalize(const T &v) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline vec_base<T, Size> normalize(const vec_base<T, Size> &v) { - bT len; + T len; return normalize_and_get_length(v, len); } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T), BLI_ENABLE_IF((T::type_length == 3))> -inline T cross(const T &a, const T &b) +template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))> +inline vec_base<T, 3> cross(const vec_base<T, 3> &a, const vec_base<T, 3> &b) { return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x}; } -template<typename T, - BLI_ENABLE_IF((std::is_same_v<bT, float>)), - BLI_ENABLE_IF((T::type_length == 3))> -inline T cross_high_precision(const T &a, const T &b) +inline vec_base<float, 3> cross_high_precision(const vec_base<float, 3> &a, + const vec_base<float, 3> &b) { return {(float)((double)a.y * b.z - (double)a.z * b.y), (float)((double)a.z * b.x - (double)a.x * b.z), (float)((double)a.x * b.y - (double)a.y * b.x)}; } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T), BLI_ENABLE_IF((T::type_length == 3))> -inline T cross_poly(Span<T> poly) +template<typename T, BLI_ENABLE_IF((is_math_float_type<T>))> +inline vec_base<T, 3> cross_poly(Span<vec_base<T, 3>> poly) { /* Newell's Method. */ int nv = static_cast<int>(poly.size()); if (nv < 3) { - return T(0, 0, 0); + return vec_base<T, 3>(0, 0, 0); } - const T *v_prev = &poly[nv - 1]; - const T *v_curr = &poly[0]; - T n(0, 0, 0); + const vec_base<T, 3> *v_prev = &poly[nv - 1]; + const vec_base<T, 3> *v_curr = &poly[0]; + vec_base<T, 3> n(0, 0, 0); for (int i = 0; i < nv;) { n[0] = n[0] + ((*v_prev)[1] - (*v_curr)[1]) * ((*v_prev)[2] + (*v_curr)[2]); n[1] = n[1] + ((*v_prev)[2] - (*v_curr)[2]) * ((*v_prev)[0] + (*v_curr)[0]); @@ -317,25 +339,31 @@ inline T cross_poly(Span<T> poly) return n; } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T interpolate(const T &a, const T &b, bT t) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline vec_base<T, Size> interpolate(const vec_base<T, Size> &a, + const vec_base<T, Size> &b, + const T &t) { return a * (1 - t) + b * t; } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> inline T midpoint(const T &a, const T &b) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline vec_base<T, Size> midpoint(const vec_base<T, Size> &a, const vec_base<T, Size> &b) { return (a + b) * 0.5; } -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> -inline T faceforward(const T &vector, const T &incident, const T &reference) +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +inline vec_base<T, Size> faceforward(const vec_base<T, Size> &vector, + const vec_base<T, Size> &incident, + const vec_base<T, Size> &reference) { return (dot(reference, incident) < 0) ? vector : -vector; } -template<typename T> inline int dominant_axis(const T &a) +template<typename T> inline int dominant_axis(const vec_base<T, 3> &a) { - T b = abs(a); + vec_base<T, 3> b = abs(a); return ((b.x > b.y) ? ((b.x > b.z) ? 0 : 2) : ((b.y > b.z) ? 1 : 2)); } @@ -348,14 +376,13 @@ template<typename T> struct isect_result { LINE_LINE_EXACT = 1, LINE_LINE_CROSS = 2, } kind; - bT lambda; + typename T::base_type lambda; }; -template<typename T, BLI_ENABLE_IF_FLT_VEC(T)> -isect_result<T> isect_seg_seg(const T &v1, const T &v2, const T &v3, const T &v4); - -#undef BLI_ENABLE_IF_FLT_VEC -#undef BLI_ENABLE_IF_INT_VEC -#undef bT +template<typename T, int Size, BLI_ENABLE_IF((is_math_float_type<T>))> +isect_result<vec_base<T, Size>> isect_seg_seg(const vec_base<T, Size> &v1, + const vec_base<T, Size> &v2, + const vec_base<T, Size> &v3, + const vec_base<T, Size> &v4); } // namespace blender::math diff --git a/source/blender/blenlib/BLI_memory_utils.hh b/source/blender/blenlib/BLI_memory_utils.hh index dd6b8463d84..a7cad5461b4 100644 --- a/source/blender/blenlib/BLI_memory_utils.hh +++ b/source/blender/blenlib/BLI_memory_utils.hh @@ -544,3 +544,31 @@ Container &move_assign_container(Container &dst, Container &&src) noexcept( } } // namespace blender + +namespace blender::detail { + +template<typename Func> struct ScopedDeferHelper { + Func func; + + ~ScopedDeferHelper() + { + func(); + } +}; + +} // namespace blender::detail + +#define BLI_SCOPED_DEFER_NAME1(a, b) a##b +#define BLI_SCOPED_DEFER_NAME2(a, b) BLI_SCOPED_DEFER_NAME1(a, b) +#define BLI_SCOPED_DEFER_NAME(a) BLI_SCOPED_DEFER_NAME2(_scoped_defer_##a##_, __LINE__) + +/** + * Execute the given function when the current scope ends. This can be used to cheaply implement + * some RAII-like behavior for C types that don't support it. Long term, the types we want to use + * this with should either be converted to C++ or get a proper C++ API. Until then, this function + * can help avoid common resource leakages. + */ +#define BLI_SCOPED_DEFER(function_to_defer) \ + auto BLI_SCOPED_DEFER_NAME(func) = (function_to_defer); \ + blender::detail::ScopedDeferHelper<decltype(BLI_SCOPED_DEFER_NAME(func))> \ + BLI_SCOPED_DEFER_NAME(helper){std::move(BLI_SCOPED_DEFER_NAME(func))}; diff --git a/source/blender/blenlib/BLI_path_util.h b/source/blender/blenlib/BLI_path_util.h index b4427b1dc2a..7b3e3e983f0 100644 --- a/source/blender/blenlib/BLI_path_util.h +++ b/source/blender/blenlib/BLI_path_util.h @@ -298,7 +298,7 @@ bool BLI_path_parent_dir_until_exists(char *path) ATTR_NONNULL(); bool BLI_path_abs(char *path, const char *basepath) ATTR_NONNULL(); /** * Replaces "#" character sequence in last slash-separated component of `path` - * with frame as decimal integer, with leading zeroes as necessary, to make digits digits. + * with frame as decimal integer, with leading zeroes as necessary, to make digits. */ bool BLI_path_frame(char *path, int frame, int digits) ATTR_NONNULL(); /** diff --git a/source/blender/blenlib/BLI_span.hh b/source/blender/blenlib/BLI_span.hh index d82f21a57ff..9ab096094de 100644 --- a/source/blender/blenlib/BLI_span.hh +++ b/source/blender/blenlib/BLI_span.hh @@ -307,13 +307,14 @@ template<typename T> class Span { } /** - * Returns a reference to the last element in the array. This invokes undefined behavior when the - * array is empty. + * Returns a reference to the nth last element. This invokes undefined behavior when the span is + * too short. */ - constexpr const T &last() const + constexpr const T &last(const int64_t n = 0) const { - BLI_assert(size_ > 0); - return data_[size_ - 1]; + BLI_assert(n >= 0); + BLI_assert(n < size_); + return data_[size_ - 1 - n]; } /** @@ -673,13 +674,14 @@ template<typename T> class MutableSpan { } /** - * Returns a reference to the last element. This invokes undefined behavior when the array is - * empty. + * Returns a reference to the nth last element. This invokes undefined behavior when the span is + * too short. */ - constexpr T &last() const + constexpr T &last(const int64_t n = 0) const { - BLI_assert(size_ > 0); - return data_[size_ - 1]; + BLI_assert(n >= 0); + BLI_assert(n < size_); + return data_[size_ - 1 - n]; } /** diff --git a/source/blender/blenlib/BLI_vector.hh b/source/blender/blenlib/BLI_vector.hh index d5d33b8a000..da9ab9c313e 100644 --- a/source/blender/blenlib/BLI_vector.hh +++ b/source/blender/blenlib/BLI_vector.hh @@ -639,18 +639,20 @@ class Vector { } /** - * Return a reference to the last element in the vector. - * This invokes undefined behavior when the vector is empty. + * Return a reference to the nth last element. + * This invokes undefined behavior when the vector is too short. */ - const T &last() const + const T &last(const int64_t n = 0) const { - BLI_assert(this->size() > 0); - return *(end_ - 1); + BLI_assert(n >= 0); + BLI_assert(n < this->size()); + return *(end_ - 1 - n); } - T &last() + T &last(const int64_t n = 0) { - BLI_assert(this->size() > 0); - return *(end_ - 1); + BLI_assert(n >= 0); + BLI_assert(n < this->size()); + return *(end_ - 1 - n); } /** diff --git a/source/blender/blenlib/BLI_virtual_array.hh b/source/blender/blenlib/BLI_virtual_array.hh index d697590b946..16fd706c99d 100644 --- a/source/blender/blenlib/BLI_virtual_array.hh +++ b/source/blender/blenlib/BLI_virtual_array.hh @@ -667,7 +667,7 @@ template<typename T> class VArrayCommon { } /** - * Returns the internally used span of the virtual array. This invokes undefined behavior is the + * Returns the internally used span of the virtual array. This invokes undefined behavior if the * virtual array is not stored as a span internally. */ Span<T> get_internal_span() const diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 29015084679..6e3e84f6495 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -162,6 +162,7 @@ set(SRC BLI_bitmap_draw_2d.h BLI_blenlib.h BLI_boxpack_2d.h + BLI_bounds.hh BLI_buffer.h BLI_color.hh BLI_compiler_attrs.h @@ -202,6 +203,7 @@ set(SRC BLI_heap.h BLI_heap_simple.h BLI_index_mask.hh + BLI_index_mask_ops.hh BLI_index_range.hh BLI_inplace_priority_queue.hh BLI_iterator.h @@ -220,6 +222,7 @@ set(SRC BLI_map.hh BLI_map_slots.hh BLI_math.h + BLI_math_base.hh BLI_math_base.h BLI_math_base_safe.h BLI_math_bits.h @@ -238,6 +241,7 @@ set(SRC BLI_math_vec_mpq_types.hh BLI_math_vec_types.hh BLI_math_vector.h + BLI_math_vector.hh BLI_memarena.h BLI_memblock.h BLI_memiter.h @@ -306,6 +310,9 @@ set(SRC BLI_winstuff.h PIL_time.h PIL_time_utildefines.h + + # Without these files listed, they aren't known to CMake. + ../../../extern/json/include/json.hpp ) set(LIB @@ -394,6 +401,7 @@ if(WITH_GTESTS) tests/BLI_array_store_test.cc tests/BLI_array_test.cc tests/BLI_array_utils_test.cc + tests/BLI_bounds_test.cc tests/BLI_color_test.cc tests/BLI_delaunay_2d_test.cc tests/BLI_disjoint_set_test.cc diff --git a/source/blender/blenlib/intern/delaunay_2d.cc b/source/blender/blenlib/intern/delaunay_2d.cc index cb0ba763c94..b7dbd7d679c 100644 --- a/source/blender/blenlib/intern/delaunay_2d.cc +++ b/source/blender/blenlib/intern/delaunay_2d.cc @@ -1691,7 +1691,7 @@ void fill_crossdata_for_intersect(const FatCo<T> &curco, BLI_assert(se_vcva->vert == vc && se_vcva->next->vert == va); BLI_assert(se_vcvb->vert == vc && se_vcvb->next->vert == vb); UNUSED_VARS_NDEBUG(vc); - auto isect = isect_seg_seg<vec2<T>>(va->co.exact, vb->co.exact, curco.exact, v2->co.exact); + auto isect = isect_seg_seg(va->co.exact, vb->co.exact, curco.exact, v2->co.exact); T &lambda = isect.lambda; switch (isect.kind) { case isect_result<vec2<T>>::LINE_LINE_CROSS: { @@ -2556,10 +2556,10 @@ template<typename T> void detect_holes(CDT_state<T> *cdt_state) if (e->symedges[0].face->visit_index == e->symedges[1].face->visit_index) { continue; /* Don't count hits on edges between faces in same region. */ } - auto isect = isect_seg_seg<vec2<T>>(ray_end.exact, - mid.exact, - e->symedges[0].vert->co.exact, - e->symedges[1].vert->co.exact); + auto isect = isect_seg_seg(ray_end.exact, + mid.exact, + e->symedges[0].vert->co.exact, + e->symedges[1].vert->co.exact); switch (isect.kind) { case isect_result<vec2<T>>::LINE_LINE_CROSS: { hits++; diff --git a/source/blender/blenlib/intern/index_mask.cc b/source/blender/blenlib/intern/index_mask.cc index b55de6a9264..1e301bc5fb9 100644 --- a/source/blender/blenlib/intern/index_mask.cc +++ b/source/blender/blenlib/intern/index_mask.cc @@ -1,9 +1,15 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #include "BLI_index_mask.hh" +#include "BLI_index_mask_ops.hh" namespace blender { +IndexMask IndexMask::slice(int64_t start, int64_t size) const +{ + return this->slice(IndexRange(start, size)); +} + IndexMask IndexMask::slice(IndexRange slice) const { return IndexMask(indices_.slice(slice)); @@ -30,4 +36,161 @@ IndexMask IndexMask::slice_and_offset(const IndexRange slice, Vector<int64_t> &r return IndexMask(r_new_indices.as_span()); } +IndexMask IndexMask::invert(const IndexRange full_range, Vector<int64_t> &r_new_indices) const +{ + BLI_assert(this->contained_in(full_range)); + if (full_range.size() == indices_.size()) { + return {}; + } + if (indices_.is_empty()) { + return full_range; + } + r_new_indices.clear(); + + const Vector<IndexRange> ranges = this->extract_ranges_invert(full_range, nullptr); + for (const IndexRange &range : ranges) { + for (const int64_t index : range) { + r_new_indices.append(index); + } + } + return r_new_indices.as_span(); +} + +Vector<IndexRange> IndexMask::extract_ranges() const +{ + Vector<IndexRange> ranges; + int64_t range_start = 0; + while (range_start < indices_.size()) { + int64_t current_range_end = range_start + 1; + int64_t step_size = 1; + + while (true) { + const int64_t possible_range_end = current_range_end + step_size; + if (possible_range_end > indices_.size()) { + break; + } + if (!this->slice(range_start, possible_range_end - range_start).is_range()) { + break; + } + current_range_end = possible_range_end; + step_size *= 2; + } + + /* This step size was tried already, no need to try it again. */ + step_size /= 2; + + while (step_size > 0) { + const int64_t possible_range_end = current_range_end + step_size; + step_size /= 2; + if (possible_range_end > indices_.size()) { + continue; + } + if (!this->slice(range_start, possible_range_end - range_start).is_range()) { + continue; + } + current_range_end = possible_range_end; + } + + ranges.append(IndexRange{indices_[range_start], current_range_end - range_start}); + range_start = current_range_end; + } + return ranges; +} + +Vector<IndexRange> IndexMask::extract_ranges_invert(const IndexRange full_range, + Vector<int64_t> *r_skip_amounts) const +{ + BLI_assert(this->contained_in(full_range)); + const Vector<IndexRange> ranges = this->extract_ranges(); + Vector<IndexRange> inverted_ranges; + + int64_t skip_amount = 0; + int64_t next_start = full_range.start(); + for (const int64_t i : ranges.index_range()) { + const IndexRange range = ranges[i]; + if (range.start() > next_start) { + inverted_ranges.append({next_start, range.start() - next_start}); + if (r_skip_amounts != nullptr) { + r_skip_amounts->append(skip_amount); + } + } + next_start = range.one_after_last(); + skip_amount += range.size(); + } + if (next_start < full_range.one_after_last()) { + inverted_ranges.append({next_start, full_range.one_after_last() - next_start}); + if (r_skip_amounts != nullptr) { + r_skip_amounts->append(skip_amount); + } + } + return inverted_ranges; +} + } // namespace blender + +namespace blender::index_mask_ops::detail { + +IndexMask find_indices_based_on_predicate__merge( + IndexMask indices_to_check, + threading::EnumerableThreadSpecific<Vector<Vector<int64_t>>> &sub_masks, + Vector<int64_t> &r_indices) +{ + /* Gather vectors that have been generated by possibly multiple threads. */ + Vector<Vector<int64_t> *> all_vectors; + int64_t result_mask_size = 0; + for (Vector<Vector<int64_t>> &local_sub_masks : sub_masks) { + for (Vector<int64_t> &sub_mask : local_sub_masks) { + all_vectors.append(&sub_mask); + result_mask_size += sub_mask.size(); + } + } + + if (all_vectors.is_empty()) { + /* Special case when the predicate was false for all elements. */ + return {}; + } + if (result_mask_size == indices_to_check.size()) { + /* Special case when the predicate was true for all elements. */ + return indices_to_check; + } + if (all_vectors.size() == 1) { + /* Special case when all indices for which the predicate is true happen to be in a single + * vector. */ + r_indices = std::move(*all_vectors[0]); + return r_indices.as_span(); + } + + /* Indices in separate vectors don't overlap. So it is ok to sort the vectors just by looking at + * the first element. */ + std::sort(all_vectors.begin(), + all_vectors.end(), + [](const Vector<int64_t> *a, const Vector<int64_t> *b) { return (*a)[0] < (*b)[0]; }); + + /* Precompute the offsets for the individual vectors, so that the indices can be copied into the + * final vector in parallel. */ + Vector<int64_t> offsets; + offsets.reserve(all_vectors.size() + 1); + offsets.append(0); + for (Vector<int64_t> *vector : all_vectors) { + offsets.append(offsets.last() + vector->size()); + } + + r_indices.resize(result_mask_size); + + /* Fill the final index mask in parallel again. */ + threading::parallel_for(all_vectors.index_range(), 100, [&](const IndexRange all_vectors_range) { + for (const int64_t vector_index : all_vectors_range) { + Vector<int64_t> &vector = *all_vectors[vector_index]; + const int64_t offset = offsets[vector_index]; + threading::parallel_for(vector.index_range(), 1024, [&](const IndexRange range) { + initialized_copy_n(vector.data() + range.start(), + range.size(), + r_indices.data() + offset + range.start()); + }); + } + }); + + return r_indices.as_span(); +} + +} // namespace blender::index_mask_ops::detail diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index f96c80185b1..bc3ed099fd5 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -903,6 +903,18 @@ float dist_squared_to_projected_aabb_simple(const float projmat[4][4], /** \} */ +float dist_seg_seg_v2(const float a1[3], const float a2[3], const float b1[3], const float b2[3]) +{ + if (isect_seg_seg_v2_simple(a1, a2, b1, b2)) { + return 0.0f; + } + const float d1 = dist_squared_to_line_segment_v2(a1, b1, b2); + const float d2 = dist_squared_to_line_segment_v2(a2, b1, b2); + const float d3 = dist_squared_to_line_segment_v2(b1, a1, a2); + const float d4 = dist_squared_to_line_segment_v2(b2, a1, a2); + return sqrtf(min_ffff(d1, d2, d3, d4)); +} + void closest_on_tri_to_point_v3( float r[3], const float p[3], const float v1[3], const float v2[3], const float v3[3]) { diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc index 6e2e9787ebe..70030fc2bdf 100644 --- a/source/blender/blenlib/intern/mesh_boolean.cc +++ b/source/blender/blenlib/intern/mesh_boolean.cc @@ -2185,7 +2185,7 @@ static void finish_patch_cell_graph(const IMesh &tm, * There will be a vector of \a nshapes winding numbers in each cell, one per * input shape. * As one crosses a patch into a new cell, the original shape (mesh part) - * that that patch was part of dictates which winding number changes. + * that patch was part of dictates which winding number changes. * The shape_fn(triangle_number) function should return the shape that the * triangle is part of. * Also, as soon as the winding numbers for a cell are set, use bool_optype diff --git a/source/blender/blenlib/intern/string.c b/source/blender/blenlib/intern/string.c index 976c1b0226f..75fa628e701 100644 --- a/source/blender/blenlib/intern/string.c +++ b/source/blender/blenlib/intern/string.c @@ -307,8 +307,9 @@ size_t BLI_str_unescape_ex(char *__restrict dst, { size_t len = 0; bool is_complete = true; + const size_t max_strlen = dst_maxncpy - 1; /* Account for trailing zero byte. */ for (const char *src_end = src + src_maxncpy; (src < src_end) && *src; src++) { - if (UNLIKELY(len == dst_maxncpy)) { + if (UNLIKELY(len == max_strlen)) { is_complete = false; break; } diff --git a/source/blender/blenlib/tests/BLI_array_test.cc b/source/blender/blenlib/tests/BLI_array_test.cc index 6d12b54099a..74eeb5e4e5e 100644 --- a/source/blender/blenlib/tests/BLI_array_test.cc +++ b/source/blender/blenlib/tests/BLI_array_test.cc @@ -231,9 +231,11 @@ TEST(array, Last) { Array<int> array = {5, 7, 8, 9}; EXPECT_EQ(array.last(), 9); + EXPECT_EQ(array.last(1), 8); array.last() = 1; EXPECT_EQ(array[3], 1); EXPECT_EQ(const_cast<const Array<int> &>(array).last(), 1); + EXPECT_EQ(const_cast<const Array<int> &>(array).last(2), 7); } TEST(array, Reinitialize) diff --git a/source/blender/blenlib/tests/BLI_bounds_test.cc b/source/blender/blenlib/tests/BLI_bounds_test.cc new file mode 100644 index 00000000000..9c123d4705c --- /dev/null +++ b/source/blender/blenlib/tests/BLI_bounds_test.cc @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "testing/testing.h" + +#include "BLI_math_base.hh" + +#include "BLI_array.hh" +#include "BLI_bounds.hh" + +namespace blender::tests { + +TEST(bounds, Empty) +{ + Span<float2> empty_span{}; + EXPECT_TRUE(empty_span.is_empty()); + auto result = bounds::min_max(empty_span); + EXPECT_EQ(result, std::nullopt); +} + +TEST(bounds, MinMax) +{ + Array<float2> data = {float2(0, 1), float2(3, -1), float2(0, -2), float2(-1, 1)}; + auto result = bounds::min_max(data.as_span()); + EXPECT_EQ(result->min, float2(-1, -2)); + EXPECT_EQ(result->max, float2(3, 1)); +} + +TEST(bounds, MinMaxFloat) +{ + Array<float> data = {1.0f, 3.0f, 0.0f, -1.0f}; + auto result = bounds::min_max(data.as_span()); + EXPECT_EQ(result->min, -1.0f); + EXPECT_EQ(result->max, 3.0f); +} + +TEST(bounds, MinMaxRadii) +{ + Array<int2> data = {int2(0, 1), int2(3, -1), int2(0, -2), int2(-1, 1)}; + Array<int> radii = {5, 1, 1, 4}; + auto result = bounds::min_max_with_radii(data.as_span(), radii.as_span()); + EXPECT_EQ(result->min, int2(-5, -4)); + EXPECT_EQ(result->max, int2(5, 6)); +} + +TEST(bounds, Large) +{ + Array<int2> data(10000); + for (const int64_t i : data.index_range()) { + data[i] = int2(i, i); + } + + auto result = bounds::min_max(data.as_span()); + EXPECT_EQ(result->min, int2(0, 0)); + EXPECT_EQ(result->max, int2(9999, 9999)); +} + +} // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_index_mask_test.cc b/source/blender/blenlib/tests/BLI_index_mask_test.cc index 179c1c58cc4..86ae31cedcc 100644 --- a/source/blender/blenlib/tests/BLI_index_mask_test.cc +++ b/source/blender/blenlib/tests/BLI_index_mask_test.cc @@ -64,4 +64,153 @@ TEST(index_mask, SliceAndOffset) } } +TEST(index_mask, ExtractRanges) +{ + { + Vector<int64_t> indices = {1, 2, 3, 5, 7, 8}; + Vector<IndexRange> ranges = IndexMask(indices).extract_ranges(); + EXPECT_EQ(ranges.size(), 3); + EXPECT_EQ(ranges[0], IndexRange(1, 3)); + EXPECT_EQ(ranges[1], IndexRange(5, 1)); + EXPECT_EQ(ranges[2], IndexRange(7, 2)); + } + { + Vector<int64_t> indices; + Vector<IndexRange> ranges = IndexMask(indices).extract_ranges(); + EXPECT_EQ(ranges.size(), 0); + } + { + Vector<int64_t> indices = {5, 6, 7, 8, 9, 10}; + Vector<IndexRange> ranges = IndexMask(indices).extract_ranges(); + EXPECT_EQ(ranges.size(), 1); + EXPECT_EQ(ranges[0], IndexRange(5, 6)); + } + { + Vector<int64_t> indices = {1, 3, 6, 8}; + Vector<IndexRange> ranges = IndexMask(indices).extract_ranges(); + EXPECT_EQ(ranges.size(), 4); + EXPECT_EQ(ranges[0], IndexRange(1, 1)); + EXPECT_EQ(ranges[1], IndexRange(3, 1)); + EXPECT_EQ(ranges[2], IndexRange(6, 1)); + EXPECT_EQ(ranges[3], IndexRange(8, 1)); + } + { + Vector<int64_t> indices; + IndexRange range1{4, 10}; + IndexRange range2{20, 30}; + IndexRange range3{100, 1}; + IndexRange range4{150, 100}; + for (const IndexRange &range : {range1, range2, range3, range4}) { + for (const int64_t i : range) { + indices.append(i); + } + } + Vector<IndexRange> ranges = IndexMask(indices).extract_ranges(); + EXPECT_EQ(ranges.size(), 4); + EXPECT_EQ(ranges[0], range1); + EXPECT_EQ(ranges[1], range2); + EXPECT_EQ(ranges[2], range3); + EXPECT_EQ(ranges[3], range4); + } + { + const int64_t max_test_range_size = 50; + Vector<int64_t> indices; + int64_t offset = 0; + for (const int64_t range_size : IndexRange(1, max_test_range_size)) { + for (const int i : IndexRange(range_size)) { + indices.append(offset + i); + } + offset += range_size + 1; + } + Vector<IndexRange> ranges = IndexMask(indices).extract_ranges(); + EXPECT_EQ(ranges.size(), max_test_range_size); + for (const int64_t range_size : IndexRange(1, max_test_range_size)) { + const IndexRange range = ranges[range_size - 1]; + EXPECT_EQ(range.size(), range_size); + } + } +} + +TEST(index_mask, Invert) +{ + { + Vector<int64_t> indices; + Vector<int64_t> new_indices; + IndexMask inverted_mask = IndexMask(indices).invert(IndexRange(10), new_indices); + EXPECT_EQ(inverted_mask.size(), 10); + EXPECT_TRUE(new_indices.is_empty()); + } + { + Vector<int64_t> indices = {3, 4, 5, 6}; + Vector<int64_t> new_indices; + IndexMask inverted_mask = IndexMask(indices).invert(IndexRange(3, 4), new_indices); + EXPECT_TRUE(inverted_mask.is_empty()); + } + { + Vector<int64_t> indices = {5}; + Vector<int64_t> new_indices; + IndexMask inverted_mask = IndexMask(indices).invert(IndexRange(10), new_indices); + EXPECT_EQ(inverted_mask.size(), 9); + EXPECT_EQ(inverted_mask.indices(), Span<int64_t>({0, 1, 2, 3, 4, 6, 7, 8, 9})); + } + { + Vector<int64_t> indices = {0, 1, 2, 6, 7, 9}; + Vector<int64_t> new_indices; + IndexMask inverted_mask = IndexMask(indices).invert(IndexRange(10), new_indices); + EXPECT_EQ(inverted_mask.size(), 4); + EXPECT_EQ(inverted_mask.indices(), Span<int64_t>({3, 4, 5, 8})); + } +} + +TEST(index_mask, ExtractRangesInvert) +{ + { + Vector<int64_t> indices; + Vector<IndexRange> ranges = IndexMask(indices).extract_ranges_invert(IndexRange(10), nullptr); + EXPECT_EQ(ranges.size(), 1); + EXPECT_EQ(ranges[0], IndexRange(10)); + } + { + Vector<int64_t> indices = {1, 2, 3, 6, 7}; + Vector<int64_t> skip_amounts; + Vector<IndexRange> ranges = IndexMask(indices).extract_ranges_invert(IndexRange(10), + &skip_amounts); + EXPECT_EQ(ranges.size(), 3); + EXPECT_EQ(ranges[0], IndexRange(0, 1)); + EXPECT_EQ(ranges[1], IndexRange(4, 2)); + EXPECT_EQ(ranges[2], IndexRange(8, 2)); + EXPECT_EQ(skip_amounts[0], 0); + EXPECT_EQ(skip_amounts[1], 3); + EXPECT_EQ(skip_amounts[2], 5); + } + { + Vector<int64_t> indices = {0, 1, 2, 3, 4}; + Vector<int64_t> skip_amounts; + Vector<IndexRange> ranges = IndexMask(indices).extract_ranges_invert(IndexRange(5), + &skip_amounts); + EXPECT_TRUE(ranges.is_empty()); + EXPECT_TRUE(skip_amounts.is_empty()); + } + { + Vector<int64_t> indices = {5, 6, 7, 10, 11}; + Vector<int64_t> skip_amounts; + Vector<IndexRange> ranges = IndexMask(indices).extract_ranges_invert(IndexRange(5, 20), + &skip_amounts); + EXPECT_EQ(ranges.size(), 2); + EXPECT_EQ(ranges[0], IndexRange(8, 2)); + EXPECT_EQ(ranges[1], IndexRange(12, 13)); + EXPECT_EQ(skip_amounts[0], 3); + EXPECT_EQ(skip_amounts[1], 5); + } +} + +TEST(index_mask, ContainedIn) +{ + EXPECT_TRUE(IndexMask({3, 4, 5}).contained_in(IndexRange(10))); + EXPECT_TRUE(IndexMask().contained_in(IndexRange(5, 0))); + EXPECT_FALSE(IndexMask({3}).contained_in(IndexRange(3))); + EXPECT_FALSE(IndexMask({4, 5, 6}).contained_in(IndexRange(5, 10))); + EXPECT_FALSE(IndexMask({5, 6}).contained_in(IndexRange())); +} + } // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_math_base_test.cc b/source/blender/blenlib/tests/BLI_math_base_test.cc index 33acefeeac2..62f2b2775d0 100644 --- a/source/blender/blenlib/tests/BLI_math_base_test.cc +++ b/source/blender/blenlib/tests/BLI_math_base_test.cc @@ -3,6 +3,10 @@ #include "testing/testing.h" #include "BLI_math.h" +#include "BLI_math_base.hh" +#include "BLI_math_vector.hh" + +namespace blender::tests { /* In tests below, when we are using -1.0f as max_diff value, we actually turn the function into a * pure-ULP one. */ @@ -131,3 +135,20 @@ TEST(math_base, FloorPowerOf10) EXPECT_NEAR(floor_power_of_10(100.1f), 100.0f, 1e-4f); EXPECT_NEAR(floor_power_of_10(99.9f), 10.0f, 1e-4f); } + +TEST(math_base, MinVectorAndFloat) +{ + EXPECT_EQ(math::min(1.0f, 2.0f), 1.0f); +} + +TEST(math_base, ClampInt) +{ + EXPECT_EQ(math::clamp(111, -50, 101), 101); +} + +TEST(math_base, Midpoint) +{ + EXPECT_NEAR(math::midpoint(100.0f, 200.0f), 150.0f, 1e-4f); +} + +} // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_math_vec_types_test.cc b/source/blender/blenlib/tests/BLI_math_vec_types_test.cc index 07eb6b29a20..7590d77525b 100644 --- a/source/blender/blenlib/tests/BLI_math_vec_types_test.cc +++ b/source/blender/blenlib/tests/BLI_math_vec_types_test.cc @@ -146,4 +146,29 @@ TEST(math_vec_types, VectorTypeConversion) EXPECT_EQ(d[1], -1.0); } +TEST(math_vec_types, Divide) +{ + float2 a(1.0f, 2.0f); + float2 b(0.5f, 2.0f); + float2 result = a / b; + EXPECT_FLOAT_EQ(result.x, 2.0f); + EXPECT_FLOAT_EQ(result.y, 1.0f); +} + +TEST(math_vec_types, DivideFloatByVector) +{ + float a = 2.0f; + float2 b(0.5f, 2.0f); + float2 result = a / b; + EXPECT_FLOAT_EQ(result.x, 4.0f); + EXPECT_FLOAT_EQ(result.y, 1.0f); +} + +TEST(math_vec_types, DivideFloatByVectorSmall) +{ + float2 result = 2.0f / float2(2.0f); + EXPECT_FLOAT_EQ(result.x, 1.0f); + EXPECT_FLOAT_EQ(result.y, 1.0f); +} + } // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_memory_utils_test.cc b/source/blender/blenlib/tests/BLI_memory_utils_test.cc index 993434ddeba..ab716e5d011 100644 --- a/source/blender/blenlib/tests/BLI_memory_utils_test.cc +++ b/source/blender/blenlib/tests/BLI_memory_utils_test.cc @@ -176,4 +176,29 @@ static_assert(!is_same_any_v<int, float, bool>); static_assert(!is_same_any_v<int, float>); static_assert(!is_same_any_v<int>); +TEST(memory_utils, ScopedDefer1) +{ + int a = 0; + { + BLI_SCOPED_DEFER([&]() { a -= 5; }); + { + BLI_SCOPED_DEFER([&]() { a *= 10; }); + a = 5; + } + } + EXPECT_EQ(a, 45); +} + +TEST(memory_utils, ScopedDefer2) +{ + std::string s; + { + BLI_SCOPED_DEFER([&]() { s += "A"; }); + BLI_SCOPED_DEFER([&]() { s += "B"; }); + BLI_SCOPED_DEFER([&]() { s += "C"; }); + BLI_SCOPED_DEFER([&]() { s += "D"; }); + } + EXPECT_EQ(s, "DCBA"); +} + } // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_span_test.cc b/source/blender/blenlib/tests/BLI_span_test.cc index 35fb22b3257..0bd34250deb 100644 --- a/source/blender/blenlib/tests/BLI_span_test.cc +++ b/source/blender/blenlib/tests/BLI_span_test.cc @@ -247,6 +247,8 @@ TEST(span, FirstLast) Span<int> a_span(a); EXPECT_EQ(a_span.first(), 6); EXPECT_EQ(a_span.last(), 9); + EXPECT_EQ(a_span.last(1), 8); + EXPECT_EQ(a_span.last(2), 7); } TEST(span, FirstLast_OneElement) @@ -255,6 +257,7 @@ TEST(span, FirstLast_OneElement) Span<int> a_span(&a, 1); EXPECT_EQ(a_span.first(), 3); EXPECT_EQ(a_span.last(), 3); + EXPECT_EQ(a_span.last(0), 3); } TEST(span, Get) diff --git a/source/blender/blenlib/tests/BLI_vector_test.cc b/source/blender/blenlib/tests/BLI_vector_test.cc index 40cda20c395..29b6d2b41fe 100644 --- a/source/blender/blenlib/tests/BLI_vector_test.cc +++ b/source/blender/blenlib/tests/BLI_vector_test.cc @@ -447,6 +447,9 @@ TEST(vector, Last) { Vector<int> a{3, 5, 7}; EXPECT_EQ(a.last(), 7); + EXPECT_EQ(a.last(0), 7); + EXPECT_EQ(a.last(1), 5); + EXPECT_EQ(a.last(2), 3); } TEST(vector, AppendNTimes) diff --git a/source/blender/blenloader/intern/blend_validate.c b/source/blender/blenloader/intern/blend_validate.c index 10150f56098..e1527201e22 100644 --- a/source/blender/blenloader/intern/blend_validate.c +++ b/source/blender/blenloader/intern/blend_validate.c @@ -181,5 +181,20 @@ bool BLO_main_validate_shapekeys(Main *bmain, ReportList *reports) BKE_main_unlock(bmain); + /* NOTE: #BKE_id_delete also locks `bmain`, so we need to do this loop outside of the lock here. + */ + LISTBASE_FOREACH_MUTABLE (Key *, shapekey, &bmain->shapekeys) { + if (shapekey->from != NULL) { + continue; + } + + BKE_reportf(reports, + RPT_ERROR, + "Shapekey %s has an invalid 'from' pointer (%p), it will be deleted", + shapekey->id.name, + shapekey->from); + BKE_id_delete(bmain, shapekey); + } + return is_valid; } diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 84625fea6fc..9539436cf69 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -320,15 +320,22 @@ static void oldnewmap_increase_size(OldNewMap *onm) /* Public OldNewMap API */ -static OldNewMap *oldnewmap_new(void) +static void oldnewmap_init_data(OldNewMap *onm, const int capacity_exp) { - OldNewMap *onm = MEM_callocN(sizeof(*onm), "OldNewMap"); + memset(onm, 0x0, sizeof(*onm)); - onm->capacity_exp = DEFAULT_SIZE_EXP; + onm->capacity_exp = capacity_exp; onm->entries = MEM_malloc_arrayN( ENTRIES_CAPACITY(onm), sizeof(*onm->entries), "OldNewMap.entries"); onm->map = MEM_malloc_arrayN(MAP_CAPACITY(onm), sizeof(*onm->map), "OldNewMap.map"); oldnewmap_clear_map(onm); +} + +static OldNewMap *oldnewmap_new(void) +{ + OldNewMap *onm = MEM_mallocN(sizeof(*onm), "OldNewMap"); + + oldnewmap_init_data(onm, DEFAULT_SIZE_EXP); return onm; } @@ -395,9 +402,10 @@ static void oldnewmap_clear(OldNewMap *onm) } } - onm->capacity_exp = DEFAULT_SIZE_EXP; - oldnewmap_clear_map(onm); - onm->nentries = 0; + MEM_freeN(onm->entries); + MEM_freeN(onm->map); + + oldnewmap_init_data(onm, DEFAULT_SIZE_EXP); } static void oldnewmap_free(OldNewMap *onm) @@ -2913,7 +2921,7 @@ static const char *dataname(short id_code) return "Data from MA"; case ID_TE: return "Data from TE"; - case ID_CU: + case ID_CU_LEGACY: return "Data from CU"; case ID_GR: return "Data from GR"; diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c index 6bf3402eafe..ac59e3efc72 100644 --- a/source/blender/blenloader/intern/versioning_250.c +++ b/source/blender/blenloader/intern/versioning_250.c @@ -1644,7 +1644,7 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain) BLI_addtail((ListBase *)&ob->modifiers, lmd); ob->partype = PAROBJECT; } - else if (parent->type == OB_CURVE && ob->partype == PARCURVE) { + else if (parent->type == OB_CURVES_LEGACY && ob->partype == PARCURVE) { CurveModifierData *cmd; cmd = (CurveModifierData *)BKE_modifier_new(eModifierType_Curve); diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index f14029d6555..2a840ea585a 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -2544,6 +2544,21 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } + if (!MAIN_VERSION_ATLEAST(bmain, 301, 7) || + (bmain->versionfile == 302 && !MAIN_VERSION_ATLEAST(bmain, 302, 4))) { + /* Duplicate value for two flags that mistakenly had the same numeric value. */ + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + if (md->type == eModifierType_WeightVGProximity) { + WeightVGProximityModifierData *wpmd = (WeightVGProximityModifierData *)md; + if (wpmd->proximity_flags & MOD_WVG_PROXIMITY_INVERT_VGROUP_MASK) { + wpmd->proximity_flags |= MOD_WVG_PROXIMITY_WEIGHTS_NORMALIZE; + } + } + } + } + } + if (!MAIN_VERSION_ATLEAST(bmain, 302, 2)) { LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { if (scene->ed != NULL) { diff --git a/source/blender/blenloader/intern/versioning_common.cc b/source/blender/blenloader/intern/versioning_common.cc index 6aac76642d5..281769410bd 100644 --- a/source/blender/blenloader/intern/versioning_common.cc +++ b/source/blender/blenloader/intern/versioning_common.cc @@ -56,7 +56,7 @@ ID *do_versions_rename_id(Main *bmain, ListBase *lb = which_libbase(bmain, id_type); ID *id = nullptr; LISTBASE_FOREACH (ID *, idtest, lb) { - if (idtest->lib == nullptr) { + if (!ID_IS_LINKED(idtest)) { if (STREQ(idtest->name + 2, name_src)) { id = idtest; } diff --git a/source/blender/blenloader/intern/versioning_legacy.c b/source/blender/blenloader/intern/versioning_legacy.c index cd4efa95d5e..2908b2b151b 100644 --- a/source/blender/blenloader/intern/versioning_legacy.c +++ b/source/blender/blenloader/intern/versioning_legacy.c @@ -1356,7 +1356,7 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain) bFollowPathConstraint *data = con->data; Object *obc = blo_do_versions_newlibadr(fd, lib, data->tar); - if (obc && obc->type == OB_CURVE) { + if (obc && obc->type == OB_CURVES_LEGACY) { Curve *cu = blo_do_versions_newlibadr(fd, lib, obc->data); if (cu) { cu->flag |= CU_PATH; diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index 8685a0fa62d..e933964221b 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -33,6 +33,7 @@ #include "readfile.h" /* Own include. */ +#include "WM_types.h" #include "wm_event_types.h" /* Don't use translation strings in versioning! @@ -363,10 +364,12 @@ static void do_version_select_mouse(UserDef *userdef, wmKeyMapItem *kmi) kmi->type = (left) ? RIGHTMOUSE : LEFTMOUSE; break; case EVT_TWEAK_S: - kmi->type = (left) ? EVT_TWEAK_L : EVT_TWEAK_R; + kmi->type = (left) ? LEFTMOUSE : RIGHTMOUSE; + kmi->val = KM_CLICK_DRAG; break; case EVT_TWEAK_A: - kmi->type = (left) ? EVT_TWEAK_R : EVT_TWEAK_L; + kmi->type = (left) ? RIGHTMOUSE : LEFTMOUSE; + kmi->val = KM_CLICK_DRAG; break; default: break; @@ -385,6 +388,39 @@ static bool keymap_item_has_invalid_wm_context_data_path(wmKeyMapItem *kmi, return false; } +static bool keymap_item_update_tweak_event(wmKeyMapItem *kmi, void *UNUSED(user_data)) +{ + /* Tweak events for L M R mouse-buttons. */ + enum { + EVT_TWEAK_L = 0x5002, + EVT_TWEAK_M = 0x5003, + EVT_TWEAK_R = 0x5004, + }; + switch (kmi->type) { + case EVT_TWEAK_L: + kmi->type = LEFTMOUSE; + break; + case EVT_TWEAK_M: + kmi->type = MIDDLEMOUSE; + break; + case EVT_TWEAK_R: + kmi->type = RIGHTMOUSE; + break; + default: + kmi->direction = KM_ANY; + return false; + } + + if (kmi->val >= KM_DIRECTION_N && kmi->val <= KM_DIRECTION_NW) { + kmi->direction = kmi->val; + } + else { + kmi->direction = KM_ANY; + } + kmi->val = KM_CLICK_DRAG; + return false; +} + void blo_do_versions_userdef(UserDef *userdef) { /* #UserDef & #Main happen to have the same struct member. */ @@ -948,6 +984,20 @@ void blo_do_versions_userdef(UserDef *userdef) } } + if (!USER_VERSION_ATLEAST(300, 43)) { + userdef->ndof_flag |= NDOF_CAMERA_PAN_ZOOM; + } + + if (!USER_VERSION_ATLEAST(302, 5)) { + BKE_keyconfig_pref_filter_items(userdef, + &((struct wmKeyConfigFilterItemParams){ + .check_item = true, + .check_diff_item_add = true, + }), + keymap_item_update_tweak_event, + NULL); + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 495054923f9..490328106ca 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -580,7 +580,7 @@ static WriteData *mywrite_begin(WriteWrap *ww, MemFile *compare, MemFile *curren /** * END the mywrite wrapper - * \return 1 if write failed + * \return True if write failed * \return unknown global variable otherwise * \warning Talks to other functions with global parameters */ @@ -1256,12 +1256,12 @@ static bool do_history(const char *name, ReportList *reports) int hisnr = U.versions; if (U.versions == 0) { - return 0; + return false; } if (strlen(name) < 2) { BKE_report(reports, RPT_ERROR, "Unable to make version backup: filename too short"); - return 1; + return true; } while (hisnr > 1) { @@ -1287,7 +1287,7 @@ static bool do_history(const char *name, ReportList *reports) } } - return 0; + return false; } /** \} */ @@ -1334,7 +1334,7 @@ bool BLO_write_file(Main *mainvar, if (ww.open(&ww, tempname) == false) { BKE_reportf( reports, RPT_ERROR, "Cannot open file %s for writing: %s", tempname, strerror(errno)); - return 0; + return false; } if (remap_mode == BLO_WRITE_PATH_REMAP_ABSOLUTE) { @@ -1426,7 +1426,7 @@ bool BLO_write_file(Main *mainvar, BKE_report(reports, RPT_ERROR, strerror(errno)); remove(tempname); - return 0; + return false; } /* file save to temporary file was successful */ @@ -1435,13 +1435,13 @@ bool BLO_write_file(Main *mainvar, const bool err_hist = do_history(filepath, reports); if (err_hist) { BKE_report(reports, RPT_ERROR, "Version backup failed (file saved with @)"); - return 0; + return false; } } if (BLI_rename(tempname, filepath) != 0) { BKE_report(reports, RPT_ERROR, "Cannot change old file (file saved with @)"); - return 0; + return false; } if (G.debug & G_DEBUG_IO && mainvar->lock != NULL) { @@ -1449,7 +1449,7 @@ bool BLO_write_file(Main *mainvar, BLO_main_validate_libraries(mainvar, reports); } - return 1; + return true; } bool BLO_write_file_mem(Main *mainvar, MemFile *compare, MemFile *current, int write_flags) diff --git a/source/blender/blentranslation/BLT_translation.h b/source/blender/blentranslation/BLT_translation.h index a7873d5d2d0..ebb0f604df7 100644 --- a/source/blender/blentranslation/BLT_translation.h +++ b/source/blender/blentranslation/BLT_translation.h @@ -92,7 +92,7 @@ bool BLT_lang_is_ime_supported(void); #define BLT_I18NCONTEXT_ID_CAMERA "Camera" #define BLT_I18NCONTEXT_ID_CACHEFILE "CacheFile" #define BLT_I18NCONTEXT_ID_COLLECTION "Collection" -#define BLT_I18NCONTEXT_ID_CURVE "Curve" +#define BLT_I18NCONTEXT_ID_CURVE_LEGACY "Curve" #define BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE "FreestyleLineStyle" #define BLT_I18NCONTEXT_ID_GPENCIL "GPencil" #define BLT_I18NCONTEXT_ID_CURVES "Curves" @@ -129,6 +129,9 @@ bool BLT_lang_is_ime_supported(void); #define BLT_I18NCONTEXT_ID_MOVIECLIP "MovieClip" #define BLT_I18NCONTEXT_ID_MASK "Mask" +/* Editors-types contexts. */ +#define BLT_I18NCONTEXT_EDITOR_VIEW3D "View3D" + /* Helper for bpy.app.i18n object... */ typedef struct { const char *c_id; @@ -154,7 +157,7 @@ typedef struct { BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_CAMERA, "id_camera"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_CACHEFILE, "id_cachefile"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_COLLECTION, "id_collection"), \ - BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_CURVE, "id_curve"), \ + BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "id_curve"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE, "id_fs_linestyle"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_GPENCIL, "id_gpencil"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_CURVES, "id_curves"), \ @@ -191,6 +194,7 @@ typedef struct { BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_WORLD, "id_world"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_WORKSPACE, "id_workspace"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_WINDOWMANAGER, "id_windowmanager"), \ + BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_EDITOR_VIEW3D, "editor_view3d"), \ { \ NULL, NULL, NULL \ } \ diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index 77b0781bcb6..31492cd5c13 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -9,6 +9,7 @@ set(INC ../depsgraph ../makesdna ../../../intern/atomic + ../../../intern/clog ../../../intern/eigen ../../../intern/guardedalloc ../../../extern/rangetree diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.cc b/source/blender/bmesh/intern/bmesh_mesh_convert.cc index ae6b4da6003..ecdc86d31cf 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.cc +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc @@ -54,6 +54,19 @@ * * This has the effect from the users POV of leaving the mesh un-touched, * and only editing the active shape key-block. + * + * \subsection other_notes Other Notes + * + * Other details noted here which might not be so obvious: + * + * - The #CD_SHAPEKEY layer is only used in edit-mode, + * and the #Mesh.key is only used in object-mode. + * Although the #CD_SHAPEKEY custom-data layer is converted into #Key data-blocks for each + * undo-step while in edit-mode. + * - The #CD_SHAPE_KEYINDEX layer is used to check if vertices existed when entering edit-mode. + * Values of the indices are only used for shape-keys when the #CD_SHAPEKEY layer can't be found, + * allowing coordinates from the #Key to be used to prevent data-loss. + * These indices are also used to maintain correct indices for hook modifiers and vertex parents. */ #include "DNA_key_types.h" @@ -84,6 +97,10 @@ #include "bmesh.h" #include "intern/bmesh_private.h" /* For element checking. */ +#include "CLG_log.h" + +static CLG_LogRef LOG = {"bmesh.mesh.convert"}; + using blender::Array; using blender::IndexRange; using blender::Span; @@ -195,10 +212,10 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar if (!me || !me->totvert) { if (me && is_new) { /* No verts? still copy custom-data layout. */ - CustomData_copy(&me->vdata, &bm->vdata, mask.vmask, CD_ASSIGN, 0); - CustomData_copy(&me->edata, &bm->edata, mask.emask, CD_ASSIGN, 0); - CustomData_copy(&me->ldata, &bm->ldata, mask.lmask, CD_ASSIGN, 0); - CustomData_copy(&me->pdata, &bm->pdata, mask.pmask, CD_ASSIGN, 0); + CustomData_copy(&me->vdata, &bm->vdata, mask.vmask, CD_DEFAULT, 0); + CustomData_copy(&me->edata, &bm->edata, mask.emask, CD_DEFAULT, 0); + CustomData_copy(&me->ldata, &bm->ldata, mask.lmask, CD_DEFAULT, 0); + CustomData_copy(&me->pdata, &bm->pdata, mask.pmask, CD_DEFAULT, 0); CustomData_bmesh_init_pool(&bm->vdata, me->totvert, BM_VERT); CustomData_bmesh_init_pool(&bm->edata, me->totedge, BM_EDGE); @@ -553,8 +570,89 @@ static BMVert **bm_to_mesh_vertex_map(BMesh *bm, int ototvert) return vertMap; } +/* -------------------------------------------------------------------- */ +/** \name Edit-Mesh to Shape Key Conversion + * + * There are some details relating to using data from shape keys that need to be + * considered carefully for shape key synchronization logic. + * + * Key Block Usage + * *************** + * + * Key blocks (data in #Mesh.key must be used carefully). + * + * They can be used to query which key blocks are relative to the basis + * since it's not possible to add/remove/reorder key blocks while in edit-mode. + * + * Key Block Coordinates + * ===================== + * + * Key blocks locations must *not* be used. This was done from v2.67 to 3.0, + * causing bugs T35170 & T44415. + * + * Shape key synchronizing could work under the assumption that the key-block is + * fixed-in-place when entering edit-mode allowing them to be used as a reference when exiting. + * It often does work but isn't reliable since for e.g. rendering may flush changes + * from the edit-mesh to the key-block (there are a handful of other situations where + * changes may be flushed, see #ED_editors_flush_edits and related functions). + * When using undo, it's not known if the data in key-block is from the past or future, + * so just don't use this data as it causes pain and suffering for users and developers alike. + * + * Instead, use the shape-key values stored in #CD_SHAPEKEY since they are reliably + * based on the original locations, unless explicitly manipulated. + * It's important to write the final shape-key values back to the #CD_SHAPEKEY so applying + * the difference between the original-basis and the new coordinates isn't done multiple times. + * Therefore #ED_editors_flush_edits and other flushing calls will update both the #Mesh.key + * and the edit-mode #CD_SHAPEKEY custom-data layers. + * + * WARNING: There is an exception to the rule of ignoring coordinates in the destination: + * that is when shape-key data in `bm` can't be found (which is itself an error/exception). + * In this case our own rule is violated as the alternative is loosing the shape-data entirely. + * + * Flushing Coordinates Back to the #BMesh + * --------------------------------------- + * + * The edit-mesh may be flushed back to the #Mesh and #Key used to generate it. + * When this is done, the new values are written back to the #BMesh's #CD_SHAPEKEY as well. + * This is necessary when editing basis-shapes so the difference in shape keys + * is not applied multiple times. If it were important to avoid it could be skipped while + * exiting edit-mode (as the entire #BMesh is freed in that case), however it's just copying + * back a `float[3]` so the work to check if it's necessary isn't worth the overhead. + * + * In general updating the #BMesh's #CD_SHAPEKEY makes shake-key logic easier to reason about + * since it means flushing data back to the mesh has the same behavior as exiting and entering + * edit-mode (a more common operation). Meaning there is one less corner-case to have to consider. + * + * Exceptional Cases + * ***************** + * + * There are some situations that should not happen in typical usage but are + * still handled in this code, since failure to handle them could loose user-data. + * These could be investigated further since if they never happen in practice, + * we might consider removing them. However, the possibility of an mesh directly + * being modified by Python or some other low level logic that changes key-blocks + * means there is a potential this to happen so keeping code to these cases remain supported. + * + * - Custom Data & Mesh Key Block Synchronization. + * Key blocks in `me->key->block` should always have an associated + * #CD_SHAPEKEY layer in `bm->vdata`. + * If they don't there are two fall-backs for setting the location, + * - Use the value from the original shape key + * WARNING: this is technically incorrect! (see note on "Key Block Usage"). + * - Use the current vertex location, + * Also not correct but it's better then having it zeroed for e.g. + * + * - Missing key-index layer. + * In this case the basis key wont apply it's deltas to other keys and in the case + * a shape-key layer is missing, its coordinates will be initialized from the edit-mesh + * vertex locations instead of attempting to remap the shape-keys coordinates. + * + * \note These cases are considered abnormal and shouldn't occur in typical usage. + * A warning is logged in this case to help troubleshooting bugs with shape-keys. + * \{ */ + /** - * Returns custom-data shapekey index from a keyblock or -1 + * Returns custom-data shape-key index from a key-block or -1 * \note could split this out into a more generic function. */ static int bm_to_mesh_shape_layer_index_from_kb(BMesh *bm, KeyBlock *currkey) @@ -573,6 +671,196 @@ static int bm_to_mesh_shape_layer_index_from_kb(BMesh *bm, KeyBlock *currkey) return -1; } +/** + * Update `key` with shape key data stored in `bm`. + * + * \param bm: The source BMesh. + * \param key: The destination key. + * \param mvert: The destination vertex array (in some situations it's coordinates are updated). + */ +static void bm_to_mesh_shape(BMesh *bm, Key *key, MVert *mvert) +{ + KeyBlock *actkey = static_cast<KeyBlock *>(BLI_findlink(&key->block, bm->shapenr - 1)); + + /* It's unlikely this ever remains false, check for correctness. */ + bool actkey_has_layer = false; + + /* Go through and find any shape-key custom-data layers + * that might not have corresponding KeyBlocks, and add them if necessary. */ + for (int i = 0; i < bm->vdata.totlayer; i++) { + if (bm->vdata.layers[i].type != CD_SHAPEKEY) { + continue; + } + + KeyBlock *currkey; + for (currkey = (KeyBlock *)key->block.first; currkey; currkey = currkey->next) { + if (currkey->uid == bm->vdata.layers[i].uid) { + break; + } + } + + if (currkey) { + if (currkey == actkey) { + actkey_has_layer = true; + } + } + else { + currkey = BKE_keyblock_add(key, bm->vdata.layers[i].name); + currkey->uid = bm->vdata.layers[i].uid; + } + } + + const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX); + BMIter iter; + BMVert *eve; + float(*ofs)[3] = nullptr; + + /* Editing the basis key updates others. */ + if ((key->type == KEY_RELATIVE) && + /* The shape-key coordinates used from entering edit-mode are used. */ + (actkey_has_layer == true) && + /* Original key-indices are only used to check the vertex existed when entering edit-mode. */ + (cd_shape_keyindex_offset != -1) && + /* Offsets are only needed if the current shape is a basis for others. */ + BKE_keyblock_is_basis(key, bm->shapenr - 1)) { + + BLI_assert(actkey != nullptr); /* Assured by `actkey_has_layer` check. */ + const int actkey_uuid = bm_to_mesh_shape_layer_index_from_kb(bm, actkey); + + /* Since `actkey_has_layer == true`, this must never fail. */ + BLI_assert(actkey_uuid != -1); + + const int cd_shape_offset = CustomData_get_n_offset(&bm->vdata, CD_SHAPEKEY, actkey_uuid); + + ofs = static_cast<float(*)[3]>(MEM_mallocN(sizeof(float[3]) * bm->totvert, __func__)); + int i; + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { + const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset); + /* Check the vertex existed when entering edit-mode (otherwise don't apply an offset). */ + if (keyi != ORIGINDEX_NONE) { + float *co_orig = (float *)BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset); + /* Could use 'eve->co' or the destination #MVert.co, they're the same at this point. */ + sub_v3_v3v3(ofs[i], eve->co, co_orig); + } + else { + /* If there are new vertices in the mesh, we can't propagate the offset + * because it will only work for the existing vertices and not the new + * ones, creating a mess when doing e.g. subdivide + translate. */ + MEM_freeN(ofs); + ofs = nullptr; + break; + } + } + } + + LISTBASE_FOREACH (KeyBlock *, currkey, &key->block) { + int keyi; + float(*currkey_data)[3]; + + const int currkey_uuid = bm_to_mesh_shape_layer_index_from_kb(bm, currkey); + const int cd_shape_offset = (currkey_uuid == -1) ? + -1 : + CustomData_get_n_offset(&bm->vdata, CD_SHAPEKEY, currkey_uuid); + + /* Common case, the layer data is available, use it where possible. */ + if (cd_shape_offset != -1) { + const bool apply_offset = (ofs != nullptr) && (currkey != actkey) && + (bm->shapenr - 1 == currkey->relative); + + if (currkey->data && (currkey->totelem == bm->totvert)) { + /* Use memory in-place. */ + } + else { + currkey->data = MEM_reallocN(currkey->data, key->elemsize * bm->totvert); + currkey->totelem = bm->totvert; + } + currkey_data = (float(*)[3])currkey->data; + + int i; + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { + float *co_orig = (float *)BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset); + + if (currkey == actkey) { + copy_v3_v3(currkey_data[i], eve->co); + + if (actkey != key->refkey) { + /* Without this, the real mesh coordinates (uneditable) as soon as you create + * the Basis shape, see: T30771 for details. */ + if (cd_shape_keyindex_offset != -1) { + keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset); + if (keyi != ORIGINDEX_NONE) { + copy_v3_v3(mvert[i].co, co_orig); + } + } + } + } + else { + copy_v3_v3(currkey_data[i], co_orig); + } + + /* Propagate edited basis offsets to other shapes. */ + if (apply_offset) { + add_v3_v3(currkey_data[i], ofs[i]); + } + + /* Apply back new coordinates shape-keys that have offset into #BMesh. + * Otherwise, in case we call again #BM_mesh_bm_to_me on same #BMesh, + * we'll apply diff from previous call to #BM_mesh_bm_to_me, + * to shape-key values from original creation of the #BMesh. See T50524. */ + copy_v3_v3(co_orig, currkey_data[i]); + } + } + else { + /* No original layer data, use fallback information. */ + if (currkey->data && (cd_shape_keyindex_offset != -1)) { + CLOG_WARN(&LOG, + "Found shape-key but no CD_SHAPEKEY layers to read from, " + "using existing shake-key data where possible"); + } + else { + CLOG_WARN(&LOG, + "Found shape-key but no CD_SHAPEKEY layers to read from, " + "using basis shape-key data"); + } + + currkey_data = static_cast<float(*)[3]>( + MEM_mallocN(key->elemsize * bm->totvert, "currkey->data")); + + int i; + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { + + if ((currkey->data != nullptr) && (cd_shape_keyindex_offset != -1) && + ((keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset)) != ORIGINDEX_NONE) && + (keyi < currkey->totelem)) { + /* Reconstruct keys via vertices original key indices. + * WARNING(@campbellbarton): `currkey->data` is known to be unreliable as the edit-mesh + * coordinates may be flushed back to the shape-key when exporting or rendering. + * This is a last resort! If this branch is running as part of regular usage + * it can be considered a bug. */ + const float(*oldkey)[3] = static_cast<const float(*)[3]>(currkey->data); + copy_v3_v3(currkey_data[i], oldkey[keyi]); + } + else { + /* Fail! fill in with dummy value. */ + copy_v3_v3(currkey_data[i], eve->co); + } + } + + currkey->totelem = bm->totvert; + if (currkey->data) { + MEM_freeN(currkey->data); + } + currkey->data = currkey_data; + } + } + + if (ofs) { + MEM_freeN(ofs); + } +} + +/** \} */ + BLI_INLINE void bmesh_quick_edgedraw_flag(MEdge *med, BMEdge *e) { /* This is a cheap way to set the edge draw, its not precise and will @@ -604,23 +892,8 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh const int cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE); const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX); - MVert *oldverts = nullptr; const int ototvert = me->totvert; - if (me->key && (cd_shape_keyindex_offset != -1)) { - /* Keep the old verts in case we are working on* a key, which is done at the end. */ - - /* Use the array in-place instead of duplicating the array. */ -#if 0 - oldverts = MEM_dupallocN(me->mvert); -#else - oldverts = me->mvert; - me->mvert = nullptr; - CustomData_update_typemap(&me->vdata); - CustomData_set_layer(&me->vdata, CD_MVERT, nullptr); -#endif - } - /* Free custom data. */ CustomData_free(&me->vdata, me->totvert); CustomData_free(&me->edata, me->totedge); @@ -661,9 +934,9 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh CustomData_add_layer(&me->ldata, CD_MLOOP, CD_ASSIGN, mloop, me->totloop); CustomData_add_layer(&me->pdata, CD_MPOLY, CD_ASSIGN, mpoly, me->totpoly); - /* There is no way to tell if BMesh normals are dirty or not. Instead of calculating the normals - * on the BMesh possibly unnecessarily, just tag them dirty on the resulting mesh. */ - BKE_mesh_normals_tag_dirty(me); + /* Clear normals on the mesh completely, since the original vertex and polygon count might be + * different than the BMesh's. */ + BKE_mesh_clear_derived_normals(me); me->cd_flag = BM_mesh_cd_flag_from_bmesh(bm); @@ -846,152 +1119,8 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh } } - /* See comment below, this logic is in twice. */ - if (me->key) { - KeyBlock *currkey; - KeyBlock *actkey = static_cast<KeyBlock *>(BLI_findlink(&me->key->block, bm->shapenr - 1)); - - float(*ofs)[3] = nullptr; - - /* Go through and find any shape-key custom-data layers - * that might not have corresponding KeyBlocks, and add them if necessary. */ - for (i = 0; i < bm->vdata.totlayer; i++) { - if (bm->vdata.layers[i].type != CD_SHAPEKEY) { - continue; - } - - for (currkey = (KeyBlock *)me->key->block.first; currkey; currkey = currkey->next) { - if (currkey->uid == bm->vdata.layers[i].uid) { - break; - } - } - - if (!currkey) { - currkey = BKE_keyblock_add(me->key, bm->vdata.layers[i].name); - currkey->uid = bm->vdata.layers[i].uid; - } - } - - /* Editing the base key should update others. */ - if (/* Only need offsets for relative shape keys. */ - (me->key->type == KEY_RELATIVE) && - - /* Unlikely, but the active key may not be valid if the - * BMesh and the mesh are out of sync. */ - (actkey != nullptr) && - - /* Not used here, but 'oldverts' is used later for applying 'ofs'. */ - (oldverts != nullptr) && - - /* Needed for referencing oldverts. */ - (cd_shape_keyindex_offset != -1)) { - - const bool act_is_basis = BKE_keyblock_is_basis(me->key, bm->shapenr - 1); - - /* Active key is a base. */ - if (act_is_basis) { - const float(*fp)[3] = static_cast<const float(*)[3]>(actkey->data); - - ofs = static_cast<float(*)[3]>( - MEM_callocN(sizeof(float[3]) * bm->totvert, "currkey->data")); - mvert = me->mvert; - BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { - const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset); - - /* Could use 'eve->co' or 'mvert->co', they're the same at this point. */ - if (keyi != ORIGINDEX_NONE && keyi < actkey->totelem) { - sub_v3_v3v3(ofs[i], mvert->co, fp[keyi]); - } - else { - /* If there are new vertices in the mesh, we can't propagate the offset - * because it will only work for the existing vertices and not the new - * ones, creating a mess when doing e.g. subdivide + translate. */ - MEM_freeN(ofs); - ofs = nullptr; - break; - } - - mvert++; - } - } - } - - LISTBASE_FOREACH (KeyBlock *, currkey, &me->key->block) { - int keyi; - const float(*ofs_pt)[3] = ofs; - float *newkey, (*oldkey)[3], *fp; - - const int currkey_uuid = bm_to_mesh_shape_layer_index_from_kb(bm, currkey); - const int cd_shape_offset = (currkey_uuid == -1) ? -1 : - CustomData_get_n_offset(&bm->vdata, - CD_SHAPEKEY, - currkey_uuid); - const bool apply_offset = (cd_shape_offset != -1) && (ofs != nullptr) && - (currkey != actkey) && (bm->shapenr - 1 == currkey->relative); - - fp = newkey = static_cast<float *>( - MEM_callocN(me->key->elemsize * bm->totvert, "currkey->data")); - oldkey = static_cast<float(*)[3]>(currkey->data); - - mvert = me->mvert; - BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { - - if (currkey == actkey) { - copy_v3_v3(fp, eve->co); - - if (actkey != me->key->refkey) { /* Important see bug T30771. */ - if (cd_shape_keyindex_offset != -1) { - if (oldverts) { - keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset); - if (keyi != ORIGINDEX_NONE && keyi < currkey->totelem) { /* Valid old vertex. */ - copy_v3_v3(mvert->co, oldverts[keyi].co); - } - } - } - } - } - else if (cd_shape_offset != -1) { - /* In most cases this runs. */ - copy_v3_v3(fp, (const float *)BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset)); - } - else if ((oldkey != nullptr) && (cd_shape_keyindex_offset != -1) && - ((keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset)) != ORIGINDEX_NONE) && - (keyi < currkey->totelem)) { - /* Old method of reconstructing keys via vertices original key indices, - * currently used if the new method above fails - * (which is theoretically possible in certain cases of undo). */ - copy_v3_v3(fp, oldkey[keyi]); - } - else { - /* Fail! fill in with dummy value. */ - copy_v3_v3(fp, mvert->co); - } - - /* Propagate edited basis offsets to other shapes. */ - if (apply_offset) { - add_v3_v3(fp, *ofs_pt++); - /* Apply back new coordinates shape-keys that have offset into BMesh. - * Otherwise, in case we call again #BM_mesh_bm_to_me on same BMesh, - * we'll apply diff from previous call to #BM_mesh_bm_to_me, - * to shape-key values from *original creation of the BMesh*. See T50524. */ - copy_v3_v3((float *)BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset), fp); - } - - fp += 3; - mvert++; - } - - currkey->totelem = bm->totvert; - if (currkey->data) { - MEM_freeN(currkey->data); - } - currkey->data = newkey; - } - - if (ofs) { - MEM_freeN(ofs); - } + bm_to_mesh_shape(bm, me->key, me->mvert); } /* Run this even when shape keys aren't used since it may be used for hooks or vertex parents. */ @@ -1005,10 +1134,6 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh } } - if (oldverts != nullptr) { - MEM_freeN(oldverts); - } - /* Topology could be changed, ensure #CD_MDISPS are ok. */ multires_topology_changed(me); @@ -1028,10 +1153,6 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * me->totloop = bm->totloop; me->totpoly = bm->totface; - CustomData_add_layer(&me->vdata, CD_ORIGINDEX, CD_CALLOC, nullptr, bm->totvert); - CustomData_add_layer(&me->edata, CD_ORIGINDEX, CD_CALLOC, nullptr, bm->totedge); - CustomData_add_layer(&me->pdata, CD_ORIGINDEX, CD_CALLOC, nullptr, bm->totface); - CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, nullptr, bm->totvert); CustomData_add_layer(&me->edata, CD_MEDGE, CD_CALLOC, nullptr, bm->totedge); CustomData_add_layer(&me->ldata, CD_MLOOP, CD_CALLOC, nullptr, bm->totloop); @@ -1059,22 +1180,18 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * MEdge *medge = me->medge; MLoop *mloop = me->mloop; MPoly *mpoly = me->mpoly; - int *index, add_orig; unsigned int i, j; const int cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT); const int cd_edge_bweight_offset = CustomData_get_offset(&bm->edata, CD_BWEIGHT); const int cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE); - BKE_mesh_normals_tag_dirty(me); + /* Clear normals on the mesh completely, since the original vertex and polygon count might be + * different than the BMesh's. */ + BKE_mesh_clear_derived_normals(me); me->runtime.deformed_only = true; - /* Don't add origindex layer if one already exists. */ - add_orig = !CustomData_has_layer(&bm->pdata, CD_ORIGINDEX); - - index = (int *)CustomData_get_layer(&me->vdata, CD_ORIGINDEX); - BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { MVert *mv = &mvert[i]; @@ -1088,15 +1205,10 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * mv->bweight = BM_ELEM_CD_GET_FLOAT_AS_UCHAR(eve, cd_vert_bweight_offset); } - if (add_orig) { - *index++ = i; - } - CustomData_from_bmesh_block(&bm->vdata, &me->vdata, eve->head.data, i); } bm->elem_index_dirty &= ~BM_VERT; - index = (int *)CustomData_get_layer(&me->edata, CD_ORIGINDEX); BM_ITER_MESH_INDEX (eed, &iter, bm, BM_EDGES_OF_MESH, i) { MEdge *med = &medge[i]; @@ -1123,13 +1235,9 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * } CustomData_from_bmesh_block(&bm->edata, &me->edata, eed->head.data, i); - if (add_orig) { - *index++ = i; - } } bm->elem_index_dirty &= ~BM_EDGE; - index = (int *)CustomData_get_layer(&me->pdata, CD_ORIGINDEX); j = 0; BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) { BMLoop *l_iter; @@ -1156,10 +1264,6 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * } while ((l_iter = l_iter->next) != l_first); CustomData_from_bmesh_block(&bm->pdata, &me->pdata, efa->head.data, i); - - if (add_orig) { - *index++ = i; - } } bm->elem_index_dirty &= ~(BM_FACE | BM_LOOP); diff --git a/source/blender/bmesh/intern/bmesh_query.c b/source/blender/bmesh/intern/bmesh_query.c index 45a42c29d84..1bc5b70f874 100644 --- a/source/blender/bmesh/intern/bmesh_query.c +++ b/source/blender/bmesh/intern/bmesh_query.c @@ -2234,7 +2234,9 @@ int BM_mesh_calc_face_groups(BMesh *bm, MEM_freeN(stack); /* reduce alloc to required size */ - group_index = MEM_reallocN(group_index, sizeof(*group_index) * group_curr); + if (group_index_len != group_curr) { + group_index = MEM_reallocN(group_index, sizeof(*group_index) * group_curr); + } *r_group_index = group_index; return group_curr; @@ -2354,7 +2356,9 @@ int BM_mesh_calc_edge_groups(BMesh *bm, MEM_freeN(stack); /* reduce alloc to required size */ - group_index = MEM_reallocN(group_index, sizeof(*group_index) * group_curr); + if (group_index_len != group_curr) { + group_index = MEM_reallocN(group_index, sizeof(*group_index) * group_curr); + } *r_group_index = group_index; return group_curr; diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index e2e26c5c52b..10bdc2c7294 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -532,7 +532,7 @@ static BevVert *find_bevvert(BevelParams *bp, BMVert *bmv) /** * Find the EdgeHalf representing the other end of e->e. - * \return Return other end's BevVert in *r_bvother, if r_bvother is provided. That may not have + * \return other end's BevVert in *r_bvother, if r_bvother is provided. That may not have * been constructed yet, in which case return NULL. */ static EdgeHalf *find_other_end_edge_half(BevelParams *bp, EdgeHalf *e, BevVert **r_bvother) diff --git a/source/blender/compositor/operations/COM_CompositorOperation.cc b/source/blender/compositor/operations/COM_CompositorOperation.cc index f72e81d82b9..23520364bf0 100644 --- a/source/blender/compositor/operations/COM_CompositorOperation.cc +++ b/source/blender/compositor/operations/COM_CompositorOperation.cc @@ -90,11 +90,10 @@ void CompositorOperation::deinit_execution() re = nullptr; } + Image *image = BKE_image_ensure_viewer(G.main, IMA_TYPE_R_RESULT, "Render Result"); + BKE_image_partial_update_mark_full_update(image); BLI_thread_lock(LOCK_DRAW_IMAGE); - BKE_image_signal(G.main, - BKE_image_ensure_viewer(G.main, IMA_TYPE_R_RESULT, "Render Result"), - nullptr, - IMA_SIGNAL_FREE); + BKE_image_signal(G.main, image, nullptr, IMA_SIGNAL_FREE); BLI_thread_unlock(LOCK_DRAW_IMAGE); } else { diff --git a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc index 1b23b0e001c..fa45034b00c 100644 --- a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc @@ -29,8 +29,10 @@ void GaussianBokehBlurOperation::init_data() const float width = this->get_width(); const float height = this->get_height(); - if (!sizeavailable_) { - update_size(); + if (execution_model_ == eExecutionModel::FullFrame) { + if (!sizeavailable_) { + update_size(); + } } radxf_ = size_ * (float)data_.sizex; @@ -96,6 +98,22 @@ void GaussianBokehBlurOperation::update_gauss() void GaussianBokehBlurOperation::execute_pixel(float output[4], int x, int y, void *data) { + float result[4]; + input_size_->read_sampled(result, 0, 0, PixelSampler::Nearest); + size_ = result[0]; + + const float width = this->get_width(); + const float height = this->get_height(); + + radxf_ = size_ * (float)data_.sizex; + CLAMP(radxf_, 0.0f, width / 2.0f); + + radyf_ = size_ * (float)data_.sizey; + CLAMP(radyf_, 0.0f, height / 2.0f); + + radx_ = ceil(radxf_); + rady_ = ceil(radyf_); + float temp_color[4]; temp_color[0] = 0; temp_color[1] = 0; diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 2a0d5ce9116..77597e0db06 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -335,16 +335,10 @@ void DepsgraphNodeBuilder::begin_build() * same as id_orig. Additionally, such ID might have been removed, which makes the check * for whether id_cow is expanded to access freed memory. In order to deal with this we * check whether CoW is needed based on a scalar value which does not lead to access of - * possibly deleted memory. - * Additionally, this saves some space in the map by skipping mapping for datablocks which - * do not need CoW, */ - if (!deg_copy_on_write_is_needed(id_node->id_type)) { - id_node->id_cow = nullptr; - continue; - } - + * possibly deleted memory. */ IDInfo *id_info = (IDInfo *)MEM_mallocN(sizeof(IDInfo), "depsgraph id info"); - if (deg_copy_on_write_is_expanded(id_node->id_cow) && id_node->id_orig != id_node->id_cow) { + if (deg_copy_on_write_is_needed(id_node->id_type) && + deg_copy_on_write_is_expanded(id_node->id_cow) && id_node->id_orig != id_node->id_cow) { id_info->id_cow = id_node->id_cow; } else { @@ -580,7 +574,7 @@ void DepsgraphNodeBuilder::build_id(ID *id) break; case ID_ME: case ID_MB: - case ID_CU: + case ID_CU_LEGACY: case ID_LT: case ID_GD: case ID_CV: @@ -872,7 +866,7 @@ void DepsgraphNodeBuilder::build_object_data(Object *object) /* type-specific data. */ switch (object->type) { case OB_MESH: - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_FONT: case OB_SURF: case OB_MBALL: @@ -1504,7 +1498,7 @@ void DepsgraphNodeBuilder::build_object_data_geometry_datablock(ID *obdata) op_node->set_as_entry(); break; } - case ID_CU: { + case ID_CU_LEGACY: { op_node = add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL, diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 25d7a0a6ac2..faad053e30c 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -221,7 +221,8 @@ OperationCode bone_target_opcode(ID *target, bool object_have_geometry_component(const Object *object) { - return ELEM(object->type, OB_MESH, OB_CURVE, OB_FONT, OB_SURF, OB_MBALL, OB_LATTICE, OB_GPENCIL); + return ELEM( + object->type, OB_MESH, OB_CURVES_LEGACY, OB_FONT, OB_SURF, OB_MBALL, OB_LATTICE, OB_GPENCIL); } } // namespace @@ -537,7 +538,7 @@ void DepsgraphRelationBuilder::build_id(ID *id) break; case ID_ME: case ID_MB: - case ID_CU: + case ID_CU_LEGACY: case ID_LT: case ID_CV: case ID_PT: @@ -827,7 +828,7 @@ void DepsgraphRelationBuilder::build_object_data(Object *object) /* type-specific data. */ switch (object->type) { case OB_MESH: - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_FONT: case OB_SURF: case OB_MBALL: @@ -983,7 +984,7 @@ void DepsgraphRelationBuilder::build_object_parent(Object *object) add_relation(parent_key, object_transform_key, "Lattice Deform Parent"); add_relation(geom_key, object_transform_key, "Lattice Deform Parent Geom"); } - else if (object->parent->type == OB_CURVE) { + else if (object->parent->type == OB_CURVES_LEGACY) { Curve *cu = (Curve *)object->parent->data; if (cu->flag & CU_PATH) { @@ -2040,7 +2041,7 @@ void DepsgraphRelationBuilder::build_shapekeys(Key *key) * Therefore, each user of a piece of shared geometry data ends up evaluating * its own version of the stuff, complete with whatever modifiers it may use. * - * - The data-blocks for the geometry data - "obdata" (e.g. ID_ME, ID_CU, ID_LT.) + * - The data-blocks for the geometry data - "obdata" (e.g. ID_ME, ID_CU_LEGACY, ID_LT.) * are used for * 1) calculating the bounding boxes of the geometry data, * 2) aggregating inward links from other objects (e.g. for text on curve) @@ -2125,7 +2126,7 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object) /* Materials. */ build_materials(object->mat, object->totcol); /* Geometry collision. */ - if (ELEM(object->type, OB_MESH, OB_CURVE, OB_LATTICE)) { + if (ELEM(object->type, OB_MESH, OB_CURVES_LEGACY, OB_LATTICE)) { // add geometry collider relations } /* Make sure uber update is the last in the dependencies. */ @@ -2220,7 +2221,7 @@ void DepsgraphRelationBuilder::build_object_data_geometry_datablock(ID *obdata) break; case ID_MB: break; - case ID_CU: { + case ID_CU_LEGACY: { Curve *cu = (Curve *)obdata; if (cu->bevobj != nullptr) { ComponentKey bevob_geom_key(&cu->bevobj->id, NodeType::GEOMETRY); @@ -2362,8 +2363,9 @@ void DepsgraphRelationBuilder::build_light(Light *lamp) /* light's nodetree */ if (lamp->nodetree != nullptr) { build_nodetree(lamp->nodetree); - ComponentKey nodetree_key(&lamp->nodetree->id, NodeType::NTREE_OUTPUT); - add_relation(nodetree_key, shading_key, "NTree->Light Parameters"); + OperationKey ntree_key( + &lamp->nodetree->id, NodeType::NTREE_OUTPUT, OperationCode::NTREE_OUTPUT); + add_relation(ntree_key, shading_key, "NTree->Light Parameters"); build_nested_nodetree(&lamp->id, lamp->nodetree); } } diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index b5968cbaeca..b8c85430f06 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -71,7 +71,7 @@ void depsgraph_geometry_tag_to_component(const ID *id, NodeType *component_type) bool is_selectable_data_id_type(const ID_Type id_type) { - return ELEM(id_type, ID_ME, ID_CU, ID_MB, ID_LT, ID_GD, ID_CV, ID_PT, ID_VO); + return ELEM(id_type, ID_ME, ID_CU_LEGACY, ID_MB, ID_LT, ID_GD, ID_CV, ID_PT, ID_VO); } void depsgraph_select_tag_to_component_opcode(const ID *id, @@ -332,7 +332,7 @@ void deg_graph_id_tag_legacy_compat( } break; } - case ID_CU: { + case ID_CU_LEGACY: { Curve *curve = (Curve *)id; if (curve->key != nullptr) { ID *key_id = &curve->key->id; @@ -483,6 +483,10 @@ void deg_graph_node_tag_zero(Main *bmain, if (comp_node->type == NodeType::ANIMATION) { continue; } + else if (comp_node->type == NodeType::COPY_ON_WRITE) { + id_node->is_cow_explicitly_tagged = true; + } + comp_node->tag_update(graph, update_source); } deg_graph_id_tag_legacy_compat(bmain, graph, id, (IDRecalcFlag)0, update_source); @@ -569,7 +573,7 @@ NodeType geometry_tag_to_component(const ID *id) const Object *object = (Object *)id; switch (object->type) { case OB_MESH: - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: case OB_FONT: case OB_LATTICE: @@ -586,7 +590,7 @@ NodeType geometry_tag_to_component(const ID *id) break; } case ID_ME: - case ID_CU: + case ID_CU_LEGACY: case ID_LT: case ID_MB: case ID_CV: diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc index 646e4d6d6d6..6346bab1fe8 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc @@ -125,7 +125,7 @@ void nested_id_hack_discard_pointers(ID *id_cow) SPECIAL_CASE(ID_WO, World, nodetree) SPECIAL_CASE(ID_SIM, Simulation, nodetree) - SPECIAL_CASE(ID_CU, Curve, key) + SPECIAL_CASE(ID_CU_LEGACY, Curve, key) SPECIAL_CASE(ID_LT, Lattice, key) SPECIAL_CASE(ID_ME, Mesh, key) @@ -174,7 +174,7 @@ const ID *nested_id_hack_get_discarded_pointers(NestedIDHackTempStorage *storage SPECIAL_CASE(ID_WO, World, nodetree, world) SPECIAL_CASE(ID_SIM, Simulation, nodetree, simulation) - SPECIAL_CASE(ID_CU, Curve, key, curve) + SPECIAL_CASE(ID_CU_LEGACY, Curve, key, curve) SPECIAL_CASE(ID_LT, Lattice, key, lattice) SPECIAL_CASE(ID_ME, Mesh, key, mesh) @@ -214,7 +214,7 @@ void nested_id_hack_restore_pointers(const ID *old_id, ID *new_id) SPECIAL_CASE(ID_WO, World, nodetree) SPECIAL_CASE(ID_SIM, Simulation, nodetree) - SPECIAL_CASE(ID_CU, Curve, key) + SPECIAL_CASE(ID_CU_LEGACY, Curve, key) SPECIAL_CASE(ID_LT, Lattice, key) SPECIAL_CASE(ID_ME, Mesh, key) @@ -252,7 +252,7 @@ void ntree_hack_remap_pointers(const Depsgraph *depsgraph, ID *id_cow) SPECIAL_CASE(ID_WO, World, nodetree, bNodeTree) SPECIAL_CASE(ID_SIM, Simulation, nodetree, bNodeTree) - SPECIAL_CASE(ID_CU, Curve, key, Key) + SPECIAL_CASE(ID_CU_LEGACY, Curve, key, Key) SPECIAL_CASE(ID_LT, Lattice, key, Key) SPECIAL_CASE(ID_ME, Mesh, key, Key) @@ -578,7 +578,7 @@ void update_edit_mode_pointers(const Depsgraph *depsgraph, const ID *id_orig, ID case ID_ME: update_mesh_edit_mode_pointers(id_orig, id_cow); break; - case ID_CU: + case ID_CU_LEGACY: update_curve_edit_mode_pointers(depsgraph, id_orig, id_cow); break; case ID_MB: @@ -953,7 +953,7 @@ void discard_edit_mode_pointers(ID *id_cow) case ID_ME: discard_mesh_edit_mode_pointers(id_cow); break; - case ID_CU: + case ID_CU_LEGACY: discard_curve_edit_mode_pointers(id_cow); break; case ID_MB: diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc index 0992e242c7a..50012350036 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc @@ -81,7 +81,7 @@ void ObjectRuntimeBackup::restore_to_object(Object *object) object->runtime = runtime; object->runtime.data_orig = data_orig; object->runtime.bb = bb; - if (ELEM(object->type, OB_MESH, OB_LATTICE, OB_CURVE, OB_FONT) && data_eval != nullptr) { + if (ELEM(object->type, OB_MESH, OB_LATTICE, OB_CURVES_LEGACY, OB_FONT) && data_eval != nullptr) { if (object->id.recalc & ID_RECALC_GEOMETRY) { /* If geometry is tagged for update it means, that part of * evaluated mesh are not valid anymore. In this case we can not diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 4103d9a7087..98f75ad6106 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -221,6 +221,8 @@ set(SRC engines/image/image_space_image.hh engines/image/image_space_node.hh engines/image/image_space.hh + engines/image/image_texture_info.hh + engines/image/image_usage.hh engines/image/image_wrappers.hh engines/workbench/workbench_engine.h engines/workbench/workbench_private.h diff --git a/source/blender/draw/engines/eevee/eevee_data.c b/source/blender/draw/engines/eevee/eevee_data.c index 58676caa6f9..253981d321b 100644 --- a/source/blender/draw/engines/eevee/eevee_data.c +++ b/source/blender/draw/engines/eevee/eevee_data.c @@ -27,25 +27,12 @@ static void eevee_motion_blur_mesh_data_free(void *val) { - EEVEE_GeometryMotionData *geom_mb = (EEVEE_GeometryMotionData *)val; - EEVEE_HairMotionData *hair_mb = (EEVEE_HairMotionData *)val; - switch (geom_mb->type) { - case EEVEE_MOTION_DATA_HAIR: - for (int j = 0; j < hair_mb->psys_len; j++) { - for (int i = 0; i < ARRAY_SIZE(hair_mb->psys[0].hair_pos); i++) { - GPU_VERTBUF_DISCARD_SAFE(hair_mb->psys[j].hair_pos[i]); - } - for (int i = 0; i < ARRAY_SIZE(hair_mb->psys[0].hair_pos); i++) { - DRW_TEXTURE_FREE_SAFE(hair_mb->psys[j].hair_pos_tx[i]); - } - } - break; - - case EEVEE_MOTION_DATA_MESH: - for (int i = 0; i < ARRAY_SIZE(geom_mb->vbo); i++) { - GPU_VERTBUF_DISCARD_SAFE(geom_mb->vbo[i]); - } - break; + EEVEE_ObjectMotionData *mb_data = (EEVEE_ObjectMotionData *)val; + if (mb_data->hair_data != NULL) { + MEM_freeN(mb_data->hair_data); + } + if (mb_data->geometry_data != NULL) { + MEM_freeN(mb_data->geometry_data); } MEM_freeN(val); } @@ -84,39 +71,57 @@ static bool eevee_object_key_cmp(const void *a, const void *b) return false; } +void EEVEE_motion_hair_step_free(EEVEE_HairMotionStepData *step_data) +{ + GPU_vertbuf_discard(step_data->hair_pos); + DRW_texture_free(step_data->hair_pos_tx); + MEM_freeN(step_data); +} + void EEVEE_motion_blur_data_init(EEVEE_MotionBlurData *mb) { if (mb->object == NULL) { mb->object = BLI_ghash_new(eevee_object_key_hash, eevee_object_key_cmp, "EEVEE Object Motion"); } - if (mb->geom == NULL) { - mb->geom = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "EEVEE Mesh Motion"); + for (int i = 0; i < 2; i++) { + if (mb->position_vbo_cache[i] == NULL) { + mb->position_vbo_cache[i] = BLI_ghash_new( + BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "EEVEE duplicate vbo cache"); + } + if (mb->hair_motion_step_cache[i] == NULL) { + mb->hair_motion_step_cache[i] = BLI_ghash_new( + BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "EEVEE hair motion step cache"); + } } } void EEVEE_motion_blur_data_free(EEVEE_MotionBlurData *mb) { if (mb->object) { - BLI_ghash_free(mb->object, MEM_freeN, MEM_freeN); + BLI_ghash_free(mb->object, MEM_freeN, eevee_motion_blur_mesh_data_free); mb->object = NULL; } - if (mb->geom) { - BLI_ghash_free(mb->geom, NULL, eevee_motion_blur_mesh_data_free); - mb->geom = NULL; + for (int i = 0; i < 2; i++) { + if (mb->position_vbo_cache[i]) { + BLI_ghash_free(mb->position_vbo_cache[i], NULL, (GHashValFreeFP)GPU_vertbuf_discard); + } + if (mb->hair_motion_step_cache[i]) { + BLI_ghash_free( + mb->hair_motion_step_cache[i], NULL, (GHashValFreeFP)EEVEE_motion_hair_step_free); + } } } -EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *mb, - Object *ob, - bool hair) +EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *mb, Object *ob) { if (mb->object == NULL) { return NULL; } EEVEE_ObjectKey key, *key_p; - /* Small hack to avoid another comparison. */ - key.ob = (Object *)((char *)ob + hair); + /* Assumes that all instances have the same object pointer. This is currently the case because + * instance objects are temporary objects on the stack. */ + key.ob = ob; DupliObject *dup = DRW_object_get_dupli(ob); if (dup) { key.parent = DRW_object_get_dupli_parent(ob); @@ -139,53 +144,28 @@ EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData * return ob_step; } -static void *motion_blur_deform_data_get(EEVEE_MotionBlurData *mb, Object *ob, bool hair) +EEVEE_GeometryMotionData *EEVEE_motion_blur_geometry_data_get(EEVEE_ObjectMotionData *mb_data) { - if (mb->geom == NULL) { - return NULL; + if (mb_data->geometry_data == NULL) { + EEVEE_GeometryMotionData *geom_step = MEM_callocN(sizeof(EEVEE_GeometryMotionData), __func__); + geom_step->type = EEVEE_MOTION_DATA_MESH; + mb_data->geometry_data = geom_step; } - DupliObject *dup = DRW_object_get_dupli(ob); - void *key; - if (dup) { - key = dup->ob; - } - else { - key = ob; - } - /* Only use data for object that have no modifiers. */ - if (!BKE_object_is_modified(DRW_context_state_get()->scene, ob)) { - key = ob->data; - } - key = (char *)key + (int)hair; - EEVEE_GeometryMotionData *geom_step = BLI_ghash_lookup(mb->geom, key); - if (geom_step == NULL) { - if (hair) { - EEVEE_HairMotionData *hair_step; - /* Ugly, we allocate for each modifiers and just fill based on modifier index in the list. */ - int psys_len = (ob->type != OB_CURVES) ? BLI_listbase_count(&ob->modifiers) : 1; - hair_step = MEM_callocN(sizeof(EEVEE_HairMotionData) + sizeof(hair_step->psys[0]) * psys_len, - __func__); - hair_step->psys_len = psys_len; - geom_step = (EEVEE_GeometryMotionData *)hair_step; - geom_step->type = EEVEE_MOTION_DATA_HAIR; - } - else { - geom_step = MEM_callocN(sizeof(EEVEE_GeometryMotionData), __func__); - geom_step->type = EEVEE_MOTION_DATA_MESH; - } - BLI_ghash_insert(mb->geom, key, geom_step); - } - return geom_step; + return mb_data->geometry_data; } -EEVEE_GeometryMotionData *EEVEE_motion_blur_geometry_data_get(EEVEE_MotionBlurData *mb, Object *ob) +EEVEE_HairMotionData *EEVEE_motion_blur_hair_data_get(EEVEE_ObjectMotionData *mb_data, Object *ob) { - return motion_blur_deform_data_get(mb, ob, false); -} - -EEVEE_HairMotionData *EEVEE_motion_blur_hair_data_get(EEVEE_MotionBlurData *mb, Object *ob) -{ - return motion_blur_deform_data_get(mb, ob, true); + if (mb_data->hair_data == NULL) { + /* Ugly, we allocate for each modifiers and just fill based on modifier index in the list. */ + int psys_len = (ob->type != OB_CURVES) ? BLI_listbase_count(&ob->modifiers) : 1; + EEVEE_HairMotionData *hair_step = MEM_callocN( + sizeof(EEVEE_HairMotionData) + sizeof(hair_step->psys[0]) * psys_len, __func__); + hair_step->psys_len = psys_len; + hair_step->type = EEVEE_MOTION_DATA_HAIR; + mb_data->hair_data = hair_step; + } + return mb_data->hair_data; } /* View Layer data. */ diff --git a/source/blender/draw/engines/eevee/eevee_depth_of_field.c b/source/blender/draw/engines/eevee/eevee_depth_of_field.c index 39cfbb40318..ef4d88bd521 100644 --- a/source/blender/draw/engines/eevee/eevee_depth_of_field.c +++ b/source/blender/draw/engines/eevee/eevee_depth_of_field.c @@ -626,11 +626,6 @@ static void dof_reduce_pass_init(EEVEE_FramebufferList *fbl, "dof_reduced_color", UNPACK2(res), mip_count, GPU_RGBA16F, NULL); txl->dof_reduced_coc = GPU_texture_create_2d( "dof_reduced_coc", UNPACK2(res), mip_count, GPU_R16F, NULL); - - /* TODO(@fclem): Remove once we have immutable storage or when mips are generated on creation. - */ - GPU_texture_generate_mipmap(txl->dof_reduced_color); - GPU_texture_generate_mipmap(txl->dof_reduced_coc); } GPU_framebuffer_ensure_config(&fbl->dof_reduce_fb, diff --git a/source/blender/draw/engines/eevee/eevee_motion_blur.c b/source/blender/draw/engines/eevee/eevee_motion_blur.c index 93ffa2be9f3..fbc19a01a8b 100644 --- a/source/blender/draw/engines/eevee/eevee_motion_blur.c +++ b/source/blender/draw/engines/eevee/eevee_motion_blur.c @@ -226,15 +226,14 @@ void EEVEE_motion_blur_hair_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata), } /* For now we assume hair objects are always moving. */ - EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get( - &effects->motion_blur, ob, true); + EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get(&effects->motion_blur, ob); if (mb_data) { int mb_step = effects->motion_blur_step; /* Store transform. */ DRW_hair_duplimat_get(ob, psys, md, mb_data->obmat[mb_step]); - EEVEE_HairMotionData *mb_hair = EEVEE_motion_blur_hair_data_get(&effects->motion_blur, ob); + EEVEE_HairMotionData *mb_hair = EEVEE_motion_blur_hair_data_get(mb_data, ob); int psys_id = (md != NULL) ? BLI_findindex(&ob->modifiers, md) : 0; if (psys_id >= mb_hair->psys_len) { @@ -252,8 +251,8 @@ void EEVEE_motion_blur_hair_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata), copy_m4_m4(mb_data->obmat[MB_NEXT], mb_data->obmat[MB_CURR]); } - GPUTexture *tex_prev = mb_hair->psys[psys_id].hair_pos_tx[MB_PREV]; - GPUTexture *tex_next = mb_hair->psys[psys_id].hair_pos_tx[MB_NEXT]; + GPUTexture *tex_prev = mb_hair->psys[psys_id].step_data[MB_PREV].hair_pos_tx; + GPUTexture *tex_next = mb_hair->psys[psys_id].step_data[MB_NEXT].hair_pos_tx; grp = DRW_shgroup_hair_create_sub(ob, psys, md, effects->motion_blur.hair_grp, NULL); DRW_shgroup_uniform_mat4(grp, "prevModelMatrix", mb_data->obmat[MB_PREV]); @@ -265,7 +264,7 @@ void EEVEE_motion_blur_hair_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata), } else { /* Store vertex position buffer. */ - mb_hair->psys[psys_id].hair_pos[mb_step] = DRW_hair_pos_buffer_get(ob, psys, md); + mb_hair->psys[psys_id].step_data[mb_step].hair_pos = DRW_hair_pos_buffer_get(ob, psys, md); mb_hair->use_deform = true; } } @@ -304,24 +303,14 @@ void EEVEE_motion_blur_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata), return; } - const DupliObject *dup = DRW_object_get_dupli(ob); - if (dup != NULL && dup->ob->data != dup->ob_data) { - /* Geometry instances do not support motion blur correctly yet. The #key used in - * #motion_blur_deform_data_get has to take ids of instances (#DupliObject.persistent_id) into - * account. Otherwise it can't find matching geometry instances at different points in time. */ - return; - } - - EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get( - &effects->motion_blur, ob, false); + EEVEE_ObjectMotionData *mb_data = EEVEE_motion_blur_object_data_get(&effects->motion_blur, ob); if (mb_data) { int mb_step = effects->motion_blur_step; /* Store transform. */ copy_m4_m4(mb_data->obmat[mb_step], ob->obmat); - EEVEE_GeometryMotionData *mb_geom = EEVEE_motion_blur_geometry_data_get(&effects->motion_blur, - ob); + EEVEE_GeometryMotionData *mb_geom = EEVEE_motion_blur_geometry_data_get(mb_data); if (mb_step == MB_CURR) { GPUBatch *batch = DRW_cache_object_surface_get(ob); @@ -407,86 +396,93 @@ void EEVEE_motion_blur_cache_finish(EEVEE_Data *vedata) DRW_cache_restart(); } - for (BLI_ghashIterator_init(&ghi, effects->motion_blur.geom); + for (BLI_ghashIterator_init(&ghi, effects->motion_blur.object); BLI_ghashIterator_done(&ghi) == false; BLI_ghashIterator_step(&ghi)) { - EEVEE_GeometryMotionData *mb_geom = BLI_ghashIterator_getValue(&ghi); - EEVEE_HairMotionData *mb_hair = (EEVEE_HairMotionData *)mb_geom; - - if (!mb_geom->use_deform) { - continue; - } - - switch (mb_geom->type) { - case EEVEE_MOTION_DATA_HAIR: - if (mb_step == MB_CURR) { - /* TODO(fclem): Check if vertex count mismatch. */ - mb_hair->use_deform = true; - } - else { - for (int i = 0; i < mb_hair->psys_len; i++) { - if (mb_hair->psys[i].hair_pos[mb_step] == NULL) { - continue; - } - mb_hair->psys[i].hair_pos[mb_step] = GPU_vertbuf_duplicate( - mb_hair->psys[i].hair_pos[mb_step]); - + EEVEE_ObjectMotionData *mb_data = BLI_ghashIterator_getValue(&ghi); + EEVEE_HairMotionData *mb_hair = mb_data->hair_data; + EEVEE_GeometryMotionData *mb_geom = mb_data->geometry_data; + if (mb_hair != NULL && mb_hair->use_deform) { + if (mb_step == MB_CURR) { + /* TODO(fclem): Check if vertex count mismatch. */ + mb_hair->use_deform = true; + } + else { + for (int i = 0; i < mb_hair->psys_len; i++) { + GPUVertBuf *vbo = mb_hair->psys[i].step_data[mb_step].hair_pos; + if (vbo == NULL) { + continue; + } + EEVEE_HairMotionStepData **step_data_cache_ptr; + if (!BLI_ghash_ensure_p(effects->motion_blur.hair_motion_step_cache[mb_step], + vbo, + (void ***)&step_data_cache_ptr)) { + EEVEE_HairMotionStepData *new_step_data = MEM_callocN(sizeof(EEVEE_HairMotionStepData), + __func__); + /* Duplicate the vbo, otherwise it would be lost when evaluating another frame. */ + new_step_data->hair_pos = GPU_vertbuf_duplicate(vbo); /* Create vbo immediately to bind to texture buffer. */ - GPU_vertbuf_use(mb_hair->psys[i].hair_pos[mb_step]); - - mb_hair->psys[i].hair_pos_tx[mb_step] = GPU_texture_create_from_vertbuf( - "hair_pos_motion_blur", mb_hair->psys[i].hair_pos[mb_step]); + GPU_vertbuf_use(new_step_data->hair_pos); + new_step_data->hair_pos_tx = GPU_texture_create_from_vertbuf("hair_pos_motion_blur", + new_step_data->hair_pos); + *step_data_cache_ptr = new_step_data; } + mb_hair->psys[i].step_data[mb_step] = **step_data_cache_ptr; } - break; - - case EEVEE_MOTION_DATA_MESH: - if (mb_step == MB_CURR) { - /* Modify batch to have data from adjacent frames. */ - GPUBatch *batch = mb_geom->batch; - for (int i = 0; i < MB_CURR; i++) { - GPUVertBuf *vbo = mb_geom->vbo[i]; - if (vbo && batch) { - if (GPU_vertbuf_get_vertex_len(vbo) != GPU_vertbuf_get_vertex_len(batch->verts[0])) { - /* Vertex count mismatch, disable deform motion blur. */ - mb_geom->use_deform = false; - } - - if (mb_geom->use_deform == false) { - motion_blur_remove_vbo_reference_from_batch( - batch, mb_geom->vbo[MB_PREV], mb_geom->vbo[MB_NEXT]); - - GPU_VERTBUF_DISCARD_SAFE(mb_geom->vbo[MB_PREV]); - GPU_VERTBUF_DISCARD_SAFE(mb_geom->vbo[MB_NEXT]); - break; - } + } + } + if (mb_geom != NULL && mb_geom->use_deform) { + if (mb_step == MB_CURR) { + /* Modify batch to have data from adjacent frames. */ + GPUBatch *batch = mb_geom->batch; + for (int i = 0; i < MB_CURR; i++) { + GPUVertBuf *vbo = mb_geom->vbo[i]; + if (vbo && batch) { + if (GPU_vertbuf_get_vertex_len(vbo) != GPU_vertbuf_get_vertex_len(batch->verts[0])) { + /* Vertex count mismatch, disable deform motion blur. */ + mb_geom->use_deform = false; + } + if (mb_geom->use_deform == false) { + motion_blur_remove_vbo_reference_from_batch( + batch, mb_geom->vbo[MB_PREV], mb_geom->vbo[MB_NEXT]); + break; + } + /* Avoid adding the same vbo more than once when the batch is used by multiple + * instances. */ + if (!GPU_batch_vertbuf_has(batch, vbo)) { + /* Currently, the code assumes that all objects that share the same mesh in the + * current frame also share the same mesh on other frames. */ GPU_batch_vertbuf_add_ex(batch, vbo, false); } } } - else { - GPUVertBuf *vbo = mb_geom->vbo[mb_step]; - if (vbo) { - /* Use the vbo to perform the copy on the GPU. */ - GPU_vertbuf_use(vbo); - /* Perform a copy to avoid losing it after RE_engine_frame_set(). */ - mb_geom->vbo[mb_step] = vbo = GPU_vertbuf_duplicate(vbo); + } + else { + GPUVertBuf *vbo = mb_geom->vbo[mb_step]; + if (vbo) { + /* Use the vbo to perform the copy on the GPU. */ + GPU_vertbuf_use(vbo); + /* Perform a copy to avoid losing it after RE_engine_frame_set(). */ + GPUVertBuf **vbo_cache_ptr; + if (!BLI_ghash_ensure_p(effects->motion_blur.position_vbo_cache[mb_step], + vbo, + (void ***)&vbo_cache_ptr)) { + /* Duplicate the vbo, otherwise it would be lost when evaluating another frame. */ + GPUVertBuf *duplicated_vbo = GPU_vertbuf_duplicate(vbo); + *vbo_cache_ptr = duplicated_vbo; /* Find and replace "pos" attrib name. */ - GPUVertFormat *format = (GPUVertFormat *)GPU_vertbuf_get_format(vbo); + GPUVertFormat *format = (GPUVertFormat *)GPU_vertbuf_get_format(duplicated_vbo); int attrib_id = GPU_vertformat_attr_id_get(format, "pos"); GPU_vertformat_attr_rename(format, attrib_id, (mb_step == MB_PREV) ? "prv" : "nxt"); } - else { - /* This might happen if the object visibility has been animated. */ - mb_geom->use_deform = false; - } + mb_geom->vbo[mb_step] = vbo = *vbo_cache_ptr; } - break; - - default: - BLI_assert(0); - break; + else { + /* This might happen if the object visibility has been animated. */ + mb_geom->use_deform = false; + } + } } } } @@ -503,54 +499,62 @@ void EEVEE_motion_blur_swap_data(EEVEE_Data *vedata) /* Camera Data. */ effects->motion_blur.camera[MB_PREV] = effects->motion_blur.camera[MB_NEXT]; - /* Object Data. */ - for (BLI_ghashIterator_init(&ghi, effects->motion_blur.object); - BLI_ghashIterator_done(&ghi) == false; - BLI_ghashIterator_step(&ghi)) { - EEVEE_ObjectMotionData *mb_data = BLI_ghashIterator_getValue(&ghi); - - copy_m4_m4(mb_data->obmat[MB_PREV], mb_data->obmat[MB_NEXT]); + /* Swap #position_vbo_cache pointers. */ + if (effects->motion_blur.position_vbo_cache[MB_PREV]) { + BLI_ghash_free(effects->motion_blur.position_vbo_cache[MB_PREV], + NULL, + (GHashValFreeFP)GPU_vertbuf_discard); + } + effects->motion_blur.position_vbo_cache[MB_PREV] = + effects->motion_blur.position_vbo_cache[MB_NEXT]; + effects->motion_blur.position_vbo_cache[MB_NEXT] = NULL; + + /* Swap #hair_motion_step_cache pointers. */ + if (effects->motion_blur.hair_motion_step_cache[MB_PREV]) { + BLI_ghash_free(effects->motion_blur.hair_motion_step_cache[MB_PREV], + NULL, + (GHashValFreeFP)EEVEE_motion_hair_step_free); } + effects->motion_blur.hair_motion_step_cache[MB_PREV] = + effects->motion_blur.hair_motion_step_cache[MB_NEXT]; + effects->motion_blur.hair_motion_step_cache[MB_NEXT] = NULL; - /* Deformation Data. */ - for (BLI_ghashIterator_init(&ghi, effects->motion_blur.geom); - BLI_ghashIterator_done(&ghi) == false; + /* Rename attributes in #position_vbo_cache. */ + for (BLI_ghashIterator_init(&ghi, effects->motion_blur.position_vbo_cache[MB_PREV]); + !BLI_ghashIterator_done(&ghi); BLI_ghashIterator_step(&ghi)) { - EEVEE_GeometryMotionData *mb_geom = BLI_ghashIterator_getValue(&ghi); - EEVEE_HairMotionData *mb_hair = (EEVEE_HairMotionData *)mb_geom; + GPUVertBuf *vbo = BLI_ghashIterator_getValue(&ghi); + GPUVertFormat *format = (GPUVertFormat *)GPU_vertbuf_get_format(vbo); + int attrib_id = GPU_vertformat_attr_id_get(format, "nxt"); + GPU_vertformat_attr_rename(format, attrib_id, "prv"); + } - switch (mb_geom->type) { - case EEVEE_MOTION_DATA_HAIR: - for (int i = 0; i < mb_hair->psys_len; i++) { - GPU_VERTBUF_DISCARD_SAFE(mb_hair->psys[i].hair_pos[MB_PREV]); - DRW_TEXTURE_FREE_SAFE(mb_hair->psys[i].hair_pos_tx[MB_PREV]); - mb_hair->psys[i].hair_pos[MB_PREV] = mb_hair->psys[i].hair_pos[MB_NEXT]; - mb_hair->psys[i].hair_pos_tx[MB_PREV] = mb_hair->psys[i].hair_pos_tx[MB_NEXT]; - mb_hair->psys[i].hair_pos[MB_NEXT] = NULL; - mb_hair->psys[i].hair_pos_tx[MB_NEXT] = NULL; - } - break; + /* Object Data. */ + for (BLI_ghashIterator_init(&ghi, effects->motion_blur.object); !BLI_ghashIterator_done(&ghi); + BLI_ghashIterator_step(&ghi)) { + EEVEE_ObjectMotionData *mb_data = BLI_ghashIterator_getValue(&ghi); + EEVEE_GeometryMotionData *mb_geom = mb_data->geometry_data; + EEVEE_HairMotionData *mb_hair = mb_data->hair_data; - case EEVEE_MOTION_DATA_MESH: - if (mb_geom->batch != NULL) { - motion_blur_remove_vbo_reference_from_batch( - mb_geom->batch, mb_geom->vbo[MB_PREV], mb_geom->vbo[MB_NEXT]); - } - GPU_VERTBUF_DISCARD_SAFE(mb_geom->vbo[MB_PREV]); - mb_geom->vbo[MB_PREV] = mb_geom->vbo[MB_NEXT]; - mb_geom->vbo[MB_NEXT] = NULL; - - if (mb_geom->vbo[MB_PREV]) { - GPUVertBuf *vbo = mb_geom->vbo[MB_PREV]; - GPUVertFormat *format = (GPUVertFormat *)GPU_vertbuf_get_format(vbo); - int attrib_id = GPU_vertformat_attr_id_get(format, "nxt"); - GPU_vertformat_attr_rename(format, attrib_id, "prv"); - } - break; + copy_m4_m4(mb_data->obmat[MB_PREV], mb_data->obmat[MB_NEXT]); - default: - BLI_assert(0); - break; + if (mb_hair != NULL) { + for (int i = 0; i < mb_hair->psys_len; i++) { + mb_hair->psys[i].step_data[MB_PREV].hair_pos = + mb_hair->psys[i].step_data[MB_NEXT].hair_pos; + mb_hair->psys[i].step_data[MB_PREV].hair_pos_tx = + mb_hair->psys[i].step_data[MB_NEXT].hair_pos_tx; + mb_hair->psys[i].step_data[MB_NEXT].hair_pos = NULL; + mb_hair->psys[i].step_data[MB_NEXT].hair_pos_tx = NULL; + } + } + if (mb_geom != NULL) { + if (mb_geom->batch != NULL) { + motion_blur_remove_vbo_reference_from_batch( + mb_geom->batch, mb_geom->vbo[MB_PREV], mb_geom->vbo[MB_NEXT]); + } + mb_geom->vbo[MB_PREV] = mb_geom->vbo[MB_NEXT]; + mb_geom->vbo[MB_NEXT] = NULL; } } } diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index e8828cc7494..2518ee53da3 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -618,8 +618,23 @@ enum { #define MB_CURR 2 typedef struct EEVEE_MotionBlurData { + /** + * Maps #EEVEE_ObjectKey to #EEVEE_ObjectMotionData. + */ struct GHash *object; - struct GHash *geom; + /** + * Maps original #GPUVertBuf to duplicated #GPUVertBuf. + * There are two maps for #MB_PREV and #MB_NEXT. + * Only the values are owned. + */ + struct GHash *position_vbo_cache[2]; + /** + * Maps original #GPUVertBuf to #EEVEE_HairMotionStepData. + * There are two maps for #MB_PREV and #MB_NEXT. + * Only the values are owned. + */ + struct GHash *hair_motion_step_cache[2]; + struct { float viewmat[4][4]; float persmat[4][4]; @@ -637,15 +652,16 @@ typedef struct EEVEE_ObjectKey { int id[8]; /* MAX_DUPLI_RECUR */ } EEVEE_ObjectKey; -typedef struct EEVEE_ObjectMotionData { - float obmat[3][4][4]; -} EEVEE_ObjectMotionData; - typedef enum eEEVEEMotionData { EEVEE_MOTION_DATA_MESH = 0, EEVEE_MOTION_DATA_HAIR, } eEEVEEMotionData; +typedef struct EEVEE_HairMotionStepData { + struct GPUVertBuf *hair_pos; + struct GPUTexture *hair_pos_tx; +} EEVEE_HairMotionStepData; + typedef struct EEVEE_HairMotionData { /** Needs to be first to ensure casting. */ eEEVEEMotionData type; @@ -653,8 +669,8 @@ typedef struct EEVEE_HairMotionData { /** Allocator will alloc enough slot for all particle systems. Or 1 if it's a hair object. */ int psys_len; struct { - struct GPUVertBuf *hair_pos[2]; /* Position buffer for time = t +/- step. */ - struct GPUTexture *hair_pos_tx[2]; /* Buffer Texture of the corresponding VBO. */ + /* The vbos and textures are not owned. */ + EEVEE_HairMotionStepData step_data[2]; /* Data for time = t +/- step. */ } psys[0]; } EEVEE_HairMotionData; @@ -664,10 +680,18 @@ typedef struct EEVEE_GeometryMotionData { /** To disable deform mb if vertcount mismatch. */ int use_deform; + /* The batch and vbos are not owned. */ struct GPUBatch *batch; /* Batch for time = t. */ struct GPUVertBuf *vbo[2]; /* VBO for time = t +/- step. */ } EEVEE_GeometryMotionData; +typedef struct EEVEE_ObjectMotionData { + float obmat[3][4][4]; + + EEVEE_GeometryMotionData *geometry_data; + EEVEE_HairMotionData *hair_data; +} EEVEE_ObjectMotionData; + /* ************ EFFECTS DATA ************* */ typedef enum EEVEE_EffectsFlag { @@ -1062,17 +1086,15 @@ typedef struct EEVEE_PrivateData { void EEVEE_motion_blur_data_init(EEVEE_MotionBlurData *mb); void EEVEE_motion_blur_data_free(EEVEE_MotionBlurData *mb); void EEVEE_view_layer_data_free(void *storage); +void EEVEE_motion_hair_step_free(EEVEE_HairMotionStepData *step_data); EEVEE_ViewLayerData *EEVEE_view_layer_data_get(void); EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure_ex(struct ViewLayer *view_layer); EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure(void); EEVEE_ObjectEngineData *EEVEE_object_data_get(Object *ob); EEVEE_ObjectEngineData *EEVEE_object_data_ensure(Object *ob); -EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *mb, - Object *ob, - bool hair); -EEVEE_GeometryMotionData *EEVEE_motion_blur_geometry_data_get(EEVEE_MotionBlurData *mb, - Object *ob); -EEVEE_HairMotionData *EEVEE_motion_blur_hair_data_get(EEVEE_MotionBlurData *mb, Object *ob); +EEVEE_ObjectMotionData *EEVEE_motion_blur_object_data_get(EEVEE_MotionBlurData *mb, Object *ob); +EEVEE_GeometryMotionData *EEVEE_motion_blur_geometry_data_get(EEVEE_ObjectMotionData *mb_data); +EEVEE_HairMotionData *EEVEE_motion_blur_hair_data_get(EEVEE_ObjectMotionData *mb_data, Object *ob); EEVEE_LightProbeEngineData *EEVEE_lightprobe_data_get(Object *ob); EEVEE_LightProbeEngineData *EEVEE_lightprobe_data_ensure(Object *ob); EEVEE_LightEngineData *EEVEE_light_data_get(Object *ob); diff --git a/source/blender/draw/engines/eevee/eevee_shadows.c b/source/blender/draw/engines/eevee/eevee_shadows.c index b5e9a296c16..29d98f6795d 100644 --- a/source/blender/draw/engines/eevee/eevee_shadows.c +++ b/source/blender/draw/engines/eevee/eevee_shadows.c @@ -273,11 +273,9 @@ void EEVEE_shadows_update(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) /* Resize shcasters buffers if too big. */ if (frontbuffer->alloc_count - frontbuffer->count > SH_CASTER_ALLOC_CHUNK) { - frontbuffer->alloc_count = (frontbuffer->count / SH_CASTER_ALLOC_CHUNK) * + frontbuffer->alloc_count = divide_ceil_u(max_ii(1, frontbuffer->count), + SH_CASTER_ALLOC_CHUNK) * SH_CASTER_ALLOC_CHUNK; - frontbuffer->alloc_count += (frontbuffer->count % SH_CASTER_ALLOC_CHUNK != 0) ? - SH_CASTER_ALLOC_CHUNK : - 0; frontbuffer->bbox = MEM_reallocN(frontbuffer->bbox, sizeof(EEVEE_BoundBox) * frontbuffer->alloc_count); BLI_BITMAP_RESIZE(frontbuffer->update, frontbuffer->alloc_count); diff --git a/source/blender/draw/engines/image/image_buffer_cache.hh b/source/blender/draw/engines/image/image_buffer_cache.hh new file mode 100644 index 00000000000..ef11551c879 --- /dev/null +++ b/source/blender/draw/engines/image/image_buffer_cache.hh @@ -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. + * + * Copyright 2022, Blender Foundation. + */ + +/** \file + * \ingroup draw_engine + */ + +#pragma once + +#include "BLI_vector.hh" + +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +struct FloatImageBuffer { + ImBuf *source_buffer = nullptr; + ImBuf *float_buffer = nullptr; + bool is_used = true; + + FloatImageBuffer(ImBuf *source_buffer, ImBuf *float_buffer) + : source_buffer(source_buffer), float_buffer(float_buffer) + { + } + + FloatImageBuffer(FloatImageBuffer &&other) noexcept + { + source_buffer = other.source_buffer; + float_buffer = other.float_buffer; + is_used = other.is_used; + other.source_buffer = nullptr; + other.float_buffer = nullptr; + } + + virtual ~FloatImageBuffer() + { + IMB_freeImBuf(float_buffer); + float_buffer = nullptr; + source_buffer = nullptr; + } + + FloatImageBuffer &operator=(FloatImageBuffer &&other) noexcept + { + this->source_buffer = other.source_buffer; + this->float_buffer = other.float_buffer; + is_used = other.is_used; + other.source_buffer = nullptr; + other.float_buffer = nullptr; + return *this; + } +}; + +struct FloatBufferCache { + private: + blender::Vector<FloatImageBuffer> cache_; + + public: + ImBuf *ensure_float_buffer(ImBuf *image_buffer) + { + /* Check if we can use the float buffer of the given image_buffer. */ + if (image_buffer->rect_float != nullptr) { + return image_buffer; + } + + /* Do we have a cached float buffer. */ + for (FloatImageBuffer &item : cache_) { + if (item.source_buffer == image_buffer) { + item.is_used = true; + return item.float_buffer; + } + } + + /* Generate a new float buffer. */ + IMB_float_from_rect(image_buffer); + ImBuf *new_imbuf = IMB_allocImBuf(image_buffer->x, image_buffer->y, image_buffer->planes, 0); + new_imbuf->rect_float = image_buffer->rect_float; + new_imbuf->flags |= IB_rectfloat; + new_imbuf->mall |= IB_rectfloat; + image_buffer->rect_float = nullptr; + image_buffer->flags &= ~IB_rectfloat; + image_buffer->mall &= ~IB_rectfloat; + + cache_.append(FloatImageBuffer(image_buffer, new_imbuf)); + return new_imbuf; + } + + void reset_usage_flags() + { + for (FloatImageBuffer &buffer : cache_) { + buffer.is_used = false; + } + } + + void mark_used(const ImBuf *image_buffer) + { + for (FloatImageBuffer &item : cache_) { + if (item.source_buffer == image_buffer) { + item.is_used = true; + return; + } + } + } + + void remove_unused_buffers() + { + for (int64_t i = cache_.size() - 1; i >= 0; i--) { + if (!cache_[i].is_used) { + cache_.remove_and_reorder(i); + } + } + } + + void clear() + { + cache_.clear(); + } +}; diff --git a/source/blender/draw/engines/image/image_drawing_mode.hh b/source/blender/draw/engines/image/image_drawing_mode.hh index b3d6c3abd18..c091f800d95 100644 --- a/source/blender/draw/engines/image/image_drawing_mode.hh +++ b/source/blender/draw/engines/image/image_drawing_mode.hh @@ -157,6 +157,7 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD if (tile_buffer == nullptr) { continue; } + instance_data.float_buffers.mark_used(tile_buffer); BKE_image_release_ibuf(image, tile_buffer, lock); DRWShadingGroup *shsub = DRW_shgroup_create_sub(shgrp); @@ -184,12 +185,14 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD switch (changes.get_result_code()) { case ePartialUpdateCollectResult::FullUpdateNeeded: instance_data.mark_all_texture_slots_dirty(); + instance_data.float_buffers.clear(); break; case ePartialUpdateCollectResult::NoChangesDetected: break; case ePartialUpdateCollectResult::PartialChangesDetected: /* Partial update when wrap repeat is enabled is not supported. */ if (instance_data.flags.do_tile_drawing) { + instance_data.float_buffers.clear(); instance_data.mark_all_texture_slots_dirty(); } else { @@ -200,6 +203,34 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD do_full_update_for_dirty_textures(instance_data, image_user); } + /** + * Update the float buffer in the region given by the partial update checker. + */ + void do_partial_update_float_buffer( + ImBuf *float_buffer, PartialUpdateChecker<ImageTileData>::CollectResult &iterator) const + { + ImBuf *src = iterator.tile_data.tile_buffer; + BLI_assert(float_buffer->rect_float != nullptr); + BLI_assert(float_buffer->rect == nullptr); + BLI_assert(src->rect_float == nullptr); + BLI_assert(src->rect != nullptr); + + /* Calculate the overlap between the updated region and the buffer size. Partial Update Checker + * always returns a tile (256x256). Which could lay partially outside the buffer when using + * different resolutions. + */ + rcti buffer_rect; + BLI_rcti_init(&buffer_rect, 0, float_buffer->x, 0, float_buffer->y); + rcti clipped_update_region; + const bool has_overlap = BLI_rcti_isect( + &buffer_rect, &iterator.changed_region.region, &clipped_update_region); + if (!has_overlap) { + return; + } + + IMB_float_from_rect_ex(float_buffer, src, &clipped_update_region); + } + void do_partial_update(PartialUpdateChecker<ImageTileData>::CollectResult &iterator, IMAGE_InstanceData &instance_data) const { @@ -208,7 +239,11 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD if (iterator.tile_data.tile_buffer == nullptr) { continue; } - ensure_float_buffer(*iterator.tile_data.tile_buffer); + ImBuf *tile_buffer = ensure_float_buffer(instance_data, iterator.tile_data.tile_buffer); + if (tile_buffer != iterator.tile_data.tile_buffer) { + do_partial_update_float_buffer(tile_buffer, iterator); + } + const float tile_width = static_cast<float>(iterator.tile_data.tile_buffer->x); const float tile_height = static_cast<float>(iterator.tile_data.tile_buffer->y); @@ -283,7 +318,6 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD &extracted_buffer, texture_region_width, texture_region_height, 32, IB_rectfloat); int offset = 0; - ImBuf *tile_buffer = iterator.tile_data.tile_buffer; for (int y = gpu_texture_region_to_update.ymin; y < gpu_texture_region_to_update.ymax; y++) { float yf = y / (float)texture_height; @@ -372,16 +406,12 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD * rect_float as the reference-counter isn't 0. To work around this we destruct any created local * buffers ourself. */ - bool ensure_float_buffer(ImBuf &image_buffer) const + ImBuf *ensure_float_buffer(IMAGE_InstanceData &instance_data, ImBuf *image_buffer) const { - if (image_buffer.rect_float == nullptr) { - IMB_float_from_rect(&image_buffer); - return true; - } - return false; + return instance_data.float_buffers.ensure_float_buffer(image_buffer); } - void do_full_update_texture_slot(const IMAGE_InstanceData &instance_data, + void do_full_update_texture_slot(IMAGE_InstanceData &instance_data, const TextureInfo &texture_info, ImBuf &texture_buffer, ImBuf &tile_buffer, @@ -389,10 +419,7 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD { const int texture_width = texture_buffer.x; const int texture_height = texture_buffer.y; - const bool float_buffer_created = ensure_float_buffer(tile_buffer); - /* TODO(jbakker): Find leak when rendering VSE and don't free here. */ - const bool do_free_float_buffer = float_buffer_created && - instance_data.image->type == IMA_TYPE_R_RESULT; + ImBuf *float_tile_buffer = ensure_float_buffer(instance_data, &tile_buffer); /* IMB_transform works in a non-consistent space. This should be documented or fixed!. * Construct a variant of the info_uv_to_texture that adds the texel space @@ -423,16 +450,12 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD transform_mode = IMB_TRANSFORM_MODE_CROP_SRC; } - IMB_transform(&tile_buffer, + IMB_transform(float_tile_buffer, &texture_buffer, transform_mode, IMB_FILTER_NEAREST, uv_to_texel, crop_rect_ptr); - - if (do_free_float_buffer) { - imb_freerectfloatImBuf(&tile_buffer); - } } public: @@ -451,6 +474,7 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD instance_data->partial_update.ensure_image(image); instance_data->clear_dirty_flag(); + instance_data->float_buffers.reset_usage_flags(); /* Step: Find out which screen space textures are needed to draw on the screen. Remove the * screen space textures that aren't needed. */ @@ -459,7 +483,7 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD method.update_screen_uv_bounds(); /* Check for changes in the image user compared to the last time. */ - instance_data->update_image_user(iuser); + instance_data->update_image_usage(iuser); /* Step: Update the GPU textures based on the changes in the image. */ instance_data->update_gpu_texture_allocations(); @@ -467,12 +491,16 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD /* Step: Add the GPU textures to the shgroup. */ instance_data->update_batches(); - add_depth_shgroups(*instance_data, image, iuser); + if (!instance_data->flags.do_tile_drawing) { + add_depth_shgroups(*instance_data, image, iuser); + } add_shgroups(instance_data); } - void draw_finish(IMAGE_Data *UNUSED(vedata)) const override + void draw_finish(IMAGE_Data *vedata) const override { + IMAGE_InstanceData *instance_data = vedata->instance_data; + instance_data->float_buffers.remove_unused_buffers(); } void draw_scene(IMAGE_Data *vedata) const override @@ -481,8 +509,10 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); GPU_framebuffer_bind(dfbl->default_fb); + static float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - GPU_framebuffer_clear_color_depth(dfbl->default_fb, clear_col, 1.0); + float clear_depth = instance_data->flags.do_tile_drawing ? 0.75 : 1.0f; + GPU_framebuffer_clear_color_depth(dfbl->default_fb, clear_col, clear_depth); DRW_view_set_active(instance_data->view); DRW_draw_pass(instance_data->passes.depth_pass); diff --git a/source/blender/draw/engines/image/image_engine.cc b/source/blender/draw/engines/image/image_engine.cc index 180e9601cbd..e972d21cda4 100644 --- a/source/blender/draw/engines/image/image_engine.cc +++ b/source/blender/draw/engines/image/image_engine.cc @@ -107,6 +107,7 @@ class ImageEngine { space->release_buffer(instance_data->image, image_buffer, lock); ImageUser *iuser = space->get_image_user(); + BKE_image_multiview_index(instance_data->image, iuser); drawing_mode.cache_image(vedata, instance_data->image, iuser); } diff --git a/source/blender/draw/engines/image/image_instance_data.hh b/source/blender/draw/engines/image/image_instance_data.hh index dcc3b7d15cb..682b93a80b3 100644 --- a/source/blender/draw/engines/image/image_instance_data.hh +++ b/source/blender/draw/engines/image/image_instance_data.hh @@ -8,10 +8,12 @@ #pragma once #include "image_batches.hh" +#include "image_buffer_cache.hh" #include "image_partial_updater.hh" #include "image_private.hh" #include "image_shader_params.hh" #include "image_texture_info.hh" +#include "image_usage.hh" #include "image_wrappers.hh" #include "DRW_render.h" @@ -25,8 +27,8 @@ constexpr int SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN = 1; struct IMAGE_InstanceData { struct Image *image; - /** Copy of the last image user to detect iuser differences that require a full update. */ - struct ImageUser last_image_user; + /** Usage data of the previous time, to identify changes that require a full update. */ + ImageUsage last_usage; PartialImageUpdater partial_update; @@ -47,11 +49,18 @@ struct IMAGE_InstanceData { DRWPass *depth_pass; } passes; + /** + * Cache containing the float buffers when drawing byte images. + */ + FloatBufferCache float_buffers; + /** \brief Transform matrix to convert a normalized screen space coordinates to texture space. */ float ss_to_texture[4][4]; TextureInfo texture_infos[SCREEN_SPACE_DRAWING_MODE_TEXTURE_LEN]; public: + virtual ~IMAGE_InstanceData() = default; + void clear_dirty_flag() { reset_dirty_flag(false); @@ -95,24 +104,13 @@ struct IMAGE_InstanceData { } } - void update_image_user(const ImageUser *image_user) + void update_image_usage(const ImageUser *image_user) { - short requested_pass = image_user ? image_user->pass : 0; - short requested_layer = image_user ? image_user->layer : 0; - short requested_view = image_user ? image_user->multi_index : 0; - /* There is room for 2 multiview textures. When a higher number is requested we should always - * target the first view slot. This is fine as multi view images aren't used together. */ - if (requested_view > 1) { - requested_view = 0; - } - - if (last_image_user.pass != requested_pass || last_image_user.layer != requested_layer || - last_image_user.multi_index != requested_view) { - - last_image_user.pass = requested_pass; - last_image_user.layer = requested_layer; - last_image_user.multi_index = requested_view; + ImageUsage usage(image, image_user, flags.do_tile_drawing); + if (last_usage != usage) { + last_usage = usage; reset_dirty_flag(true); + float_buffers.clear(); } } diff --git a/source/blender/draw/engines/image/image_usage.hh b/source/blender/draw/engines/image/image_usage.hh new file mode 100644 index 00000000000..bea5c3853b0 --- /dev/null +++ b/source/blender/draw/engines/image/image_usage.hh @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. */ + +/** \file + * \ingroup draw_engine + */ + +#pragma once + +/** + * ImageUsage contains data of the image and image user to identify changes that require a rebuild + * the texture slots. + */ +struct ImageUsage { + /** Render pass of the image that is used. */ + short pass = 0; + /** Layer of the image that is used.*/ + short layer = 0; + /** View of the image that is used. */ + short view = 0; + + ColorManagedColorspaceSettings colorspace_settings; + /** IMA_ALPHA_* */ + char alpha_mode; + bool last_tile_drawing; + + const void *last_image = nullptr; + + ImageUsage() = default; + ImageUsage(const struct Image *image, const struct ImageUser *image_user, bool do_tile_drawing) + { + pass = image_user ? image_user->pass : 0; + layer = image_user ? image_user->layer : 0; + view = image_user ? image_user->multi_index : 0; + colorspace_settings = image->colorspace_settings; + alpha_mode = image->alpha_mode; + last_image = static_cast<const void *>(image); + last_tile_drawing = do_tile_drawing; + } + + bool operator==(const ImageUsage &other) const + { + return memcmp(this, &other, sizeof(ImageUsage)) == 0; + } + bool operator!=(const ImageUsage &other) const + { + return !(*this == other); + } +}; diff --git a/source/blender/draw/engines/overlay/overlay_engine.c b/source/blender/draw/engines/overlay/overlay_engine.c index b41d9ce69ef..ad0d939e99a 100644 --- a/source/blender/draw/engines/overlay/overlay_engine.c +++ b/source/blender/draw/engines/overlay/overlay_engine.c @@ -182,7 +182,9 @@ static void OVERLAY_cache_init(void *vedata) case CTX_MODE_WEIGHT_GPENCIL: OVERLAY_edit_gpencil_cache_init(vedata); break; + case CTX_MODE_SCULPT_CURVES: case CTX_MODE_OBJECT: + case CTX_MODE_EDIT_CURVES: break; default: BLI_assert_msg(0, "Draw mode invalid"); @@ -210,7 +212,7 @@ BLI_INLINE OVERLAY_DupliData *OVERLAY_duplidata_get(Object *ob, void *vedata, bo { OVERLAY_DupliData **dupli_data = (OVERLAY_DupliData **)DRW_duplidata_get(vedata); *do_init = false; - if (!ELEM(ob->type, OB_MESH, OB_SURF, OB_LATTICE, OB_CURVE, OB_FONT)) { + if (!ELEM(ob->type, OB_MESH, OB_SURF, OB_LATTICE, OB_CURVES_LEGACY, OB_FONT)) { return NULL; } @@ -237,7 +239,7 @@ static bool overlay_object_is_edit_mode(const OVERLAY_PrivateData *pd, const Obj return pd->ctx_mode == CTX_MODE_EDIT_MESH; case OB_ARMATURE: return pd->ctx_mode == CTX_MODE_EDIT_ARMATURE; - case OB_CURVE: + case OB_CURVES_LEGACY: return pd->ctx_mode == CTX_MODE_EDIT_CURVE; case OB_SURF: return pd->ctx_mode == CTX_MODE_EDIT_SURFACE; @@ -296,7 +298,7 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob) (ob->sculpt->mode_type == OB_MODE_SCULPT); const bool has_surface = ELEM(ob->type, OB_MESH, - OB_CURVE, + OB_CURVES_LEGACY, OB_SURF, OB_MBALL, OB_FONT, @@ -366,7 +368,7 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob) OVERLAY_edit_armature_cache_populate(vedata, ob); } break; - case OB_CURVE: + case OB_CURVES_LEGACY: OVERLAY_edit_curve_cache_populate(vedata, ob); break; case OB_SURF: @@ -661,6 +663,8 @@ static void OVERLAY_draw_scene(void *vedata) case CTX_MODE_WEIGHT_GPENCIL: OVERLAY_edit_gpencil_draw(vedata); break; + case CTX_MODE_SCULPT_CURVES: + break; default: break; } diff --git a/source/blender/draw/engines/overlay/overlay_extra.c b/source/blender/draw/engines/overlay/overlay_extra.c index e370873c234..aae12e5513e 100644 --- a/source/blender/draw/engines/overlay/overlay_extra.c +++ b/source/blender/draw/engines/overlay/overlay_extra.c @@ -456,7 +456,7 @@ static void OVERLAY_texture_space(OVERLAY_ExtraCallBuffers *cb, Object *ob, cons case ID_ME: BKE_mesh_texspace_get_reference((Mesh *)ob_data, NULL, &texcoloc, &texcosize); break; - case ID_CU: { + case ID_CU_LEGACY: { Curve *cu = (Curve *)ob_data; BKE_curve_texspace_ensure(cu); texcoloc = cu->loc; @@ -499,7 +499,7 @@ static void OVERLAY_forcefield(OVERLAY_ExtraCallBuffers *cb, Object *ob, ViewLay int theme_id = DRW_object_wire_theme_get(ob, view_layer, NULL); float *color = DRW_color_background_blend_get(theme_id); PartDeflect *pd = ob->pd; - Curve *cu = (ob->type == OB_CURVE) ? ob->data : NULL; + Curve *cu = (ob->type == OB_CURVES_LEGACY) ? ob->data : NULL; union { float mat[4][4]; diff --git a/source/blender/draw/engines/overlay/overlay_motion_path.c b/source/blender/draw/engines/overlay/overlay_motion_path.c index 58825923f37..aeba721e7ac 100644 --- a/source/blender/draw/engines/overlay/overlay_motion_path.c +++ b/source/blender/draw/engines/overlay/overlay_motion_path.c @@ -90,8 +90,8 @@ static void motion_path_get_frame_range_to_draw(bAnimVizSettings *avs, end = current_frame + avs->path_ac + 1; } else { - start = avs->path_sf; - end = avs->path_ef; + start = mpath->start_frame; + end = mpath->end_frame; } if (start > end) { diff --git a/source/blender/draw/engines/overlay/overlay_wireframe.c b/source/blender/draw/engines/overlay/overlay_wireframe.c index 24eceb30441..2636d7876d5 100644 --- a/source/blender/draw/engines/overlay/overlay_wireframe.c +++ b/source/blender/draw/engines/overlay/overlay_wireframe.c @@ -196,14 +196,14 @@ void OVERLAY_wireframe_cache_populate(OVERLAY_Data *vedata, } } - if (ELEM(ob->type, OB_CURVE, OB_FONT, OB_SURF)) { + if (ELEM(ob->type, OB_CURVES_LEGACY, OB_FONT, OB_SURF)) { OVERLAY_ExtraCallBuffers *cb = OVERLAY_extra_call_buffer_get(vedata, ob); float *color; DRW_object_wire_theme_get(ob, draw_ctx->view_layer, &color); struct GPUBatch *geom = NULL; switch (ob->type) { - case OB_CURVE: + case OB_CURVES_LEGACY: geom = DRW_cache_curve_edge_wire_get(ob); break; case OB_FONT: diff --git a/source/blender/draw/engines/overlay/shaders/paint_texture_frag.glsl b/source/blender/draw/engines/overlay/shaders/paint_texture_frag.glsl index 4d0692039a4..ebaa898429d 100644 --- a/source/blender/draw/engines/overlay/shaders/paint_texture_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/paint_texture_frag.glsl @@ -15,7 +15,7 @@ void main() if (maskInvertStencil) { mask.rgb = 1.0 - mask.rgb; } - float mask_step = smoothstep(0, 3.0, mask.r + mask.g + mask.b); + float mask_step = smoothstep(0.0, 3.0, mask.r + mask.g + mask.b); mask.rgb *= maskColor; mask.a = mask_step * opacity; diff --git a/source/blender/draw/engines/select/select_draw_utils.c b/source/blender/draw/engines/select/select_draw_utils.c index 82812ef98a5..7615b5bb39c 100644 --- a/source/blender/draw/engines/select/select_draw_utils.c +++ b/source/blender/draw/engines/select/select_draw_utils.c @@ -225,7 +225,7 @@ void select_id_draw_object(void *vedata, stl, ob, select_mode, initial_offset, r_vert_offset, r_edge_offset, r_face_offset); } break; - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: break; } diff --git a/source/blender/draw/engines/workbench/workbench_opaque.c b/source/blender/draw/engines/workbench/workbench_opaque.c index 5e12d6a736c..191a2e6d1cc 100644 --- a/source/blender/draw/engines/workbench/workbench_opaque.c +++ b/source/blender/draw/engines/workbench/workbench_opaque.c @@ -73,11 +73,13 @@ void workbench_opaque_cache_init(WORKBENCH_Data *vedata) sh = workbench_shader_opaque_get(wpd, data); wpd->prepass[opaque][infront][data].common_shgrp = grp = DRW_shgroup_create(sh, pass); + DRW_shgroup_uniform_block(grp, "world_data", wpd->world_ubo); DRW_shgroup_uniform_block(grp, "materials_data", wpd->material_ubo_curr); DRW_shgroup_uniform_int_copy(grp, "materialIndex", -1); DRW_shgroup_uniform_bool_copy(grp, "useMatcap", use_matcap); wpd->prepass[opaque][infront][data].vcol_shgrp = grp = DRW_shgroup_create(sh, pass); + DRW_shgroup_uniform_block(grp, "world_data", wpd->world_ubo); DRW_shgroup_uniform_block(grp, "materials_data", wpd->material_ubo_curr); DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. (uses vcol) */ DRW_shgroup_uniform_bool_copy(grp, "useMatcap", use_matcap); @@ -85,6 +87,7 @@ void workbench_opaque_cache_init(WORKBENCH_Data *vedata) sh = workbench_shader_opaque_image_get(wpd, data, false); wpd->prepass[opaque][infront][data].image_shgrp = grp = DRW_shgroup_create(sh, pass); + DRW_shgroup_uniform_block(grp, "world_data", wpd->world_ubo); DRW_shgroup_uniform_block(grp, "materials_data", wpd->material_ubo_curr); DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. */ DRW_shgroup_uniform_bool_copy(grp, "useMatcap", use_matcap); @@ -92,6 +95,7 @@ void workbench_opaque_cache_init(WORKBENCH_Data *vedata) sh = workbench_shader_opaque_image_get(wpd, data, true); wpd->prepass[opaque][infront][data].image_tiled_shgrp = grp = DRW_shgroup_create(sh, pass); + DRW_shgroup_uniform_block(grp, "world_data", wpd->world_ubo); DRW_shgroup_uniform_block(grp, "materials_data", wpd->material_ubo_curr); DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. */ DRW_shgroup_uniform_bool_copy(grp, "useMatcap", use_matcap); diff --git a/source/blender/draw/intern/DRW_gpu_wrapper.hh b/source/blender/draw/intern/DRW_gpu_wrapper.hh index f387d5371b5..bce001659b2 100644 --- a/source/blender/draw/intern/DRW_gpu_wrapper.hh +++ b/source/blender/draw/intern/DRW_gpu_wrapper.hh @@ -641,11 +641,6 @@ class Texture : NonCopyable { } if (tx_ == nullptr) { tx_ = create(w, h, d, mips, format, data, layered, cubemap); - if (mips > 1) { - /* TODO(@fclem): Remove once we have immutable storage or when mips are - * generated on creation. */ - GPU_texture_generate_mipmap(tx_); - } return true; } return false; diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c index ce8d3136432..8fc97ddcfc2 100644 --- a/source/blender/draw/intern/draw_cache.c +++ b/source/blender/draw/intern/draw_cache.c @@ -813,7 +813,7 @@ GPUBatch *DRW_cache_object_edge_detection_get(Object *ob, bool *r_is_manifold) switch (ob->type) { case OB_MESH: return DRW_cache_mesh_edge_detection_get(ob, r_is_manifold); - case OB_CURVE: + case OB_CURVES_LEGACY: return NULL; case OB_SURF: return DRW_cache_surf_edge_detection_get(ob, r_is_manifold); @@ -837,7 +837,7 @@ GPUBatch *DRW_cache_object_face_wireframe_get(Object *ob) switch (ob->type) { case OB_MESH: return DRW_cache_mesh_face_wireframe_get(ob); - case OB_CURVE: + case OB_CURVES_LEGACY: return NULL; case OB_SURF: return DRW_cache_surf_face_wireframe_get(ob); @@ -864,7 +864,7 @@ GPUBatch *DRW_cache_object_loose_edges_get(struct Object *ob) switch (ob->type) { case OB_MESH: return DRW_cache_mesh_loose_edges_get(ob); - case OB_CURVE: + case OB_CURVES_LEGACY: return NULL; case OB_SURF: return DRW_cache_surf_loose_edges_get(ob); @@ -888,7 +888,7 @@ GPUBatch *DRW_cache_object_surface_get(Object *ob) switch (ob->type) { case OB_MESH: return DRW_cache_mesh_surface_get(ob); - case OB_CURVE: + case OB_CURVES_LEGACY: return NULL; case OB_SURF: return DRW_cache_surf_surface_get(ob); @@ -915,7 +915,7 @@ GPUVertBuf *DRW_cache_object_pos_vertbuf_get(Object *ob) switch (type) { case OB_MESH: return DRW_mesh_batch_cache_pos_vertbuf_get((me != NULL) ? me : ob->data); - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: case OB_FONT: return DRW_curve_batch_cache_pos_vertbuf_get(ob->data); @@ -947,7 +947,7 @@ int DRW_cache_object_material_count_get(struct Object *ob) switch (type) { case OB_MESH: return DRW_mesh_material_count_get(ob, (me != NULL) ? me : ob->data); - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: case OB_FONT: return DRW_curve_material_count_get(ob->data); @@ -972,7 +972,7 @@ GPUBatch **DRW_cache_object_surface_material_get(struct Object *ob, switch (ob->type) { case OB_MESH: return DRW_cache_mesh_surface_shaded_get(ob, gpumat_array, gpumat_array_len); - case OB_CURVE: + case OB_CURVES_LEGACY: return NULL; case OB_SURF: return DRW_cache_surf_surface_shaded_get(ob, gpumat_array, gpumat_array_len); @@ -2922,21 +2922,21 @@ GPUBatch *DRW_cache_mesh_surface_mesh_analysis_get(Object *ob) GPUBatch *DRW_cache_curve_edge_wire_get(Object *ob) { - BLI_assert(ob->type == OB_CURVE); + BLI_assert(ob->type == OB_CURVES_LEGACY); struct Curve *cu = ob->data; return DRW_curve_batch_cache_get_wire_edge(cu); } GPUBatch *DRW_cache_curve_edge_normal_get(Object *ob) { - BLI_assert(ob->type == OB_CURVE); + BLI_assert(ob->type == OB_CURVES_LEGACY); struct Curve *cu = ob->data; return DRW_curve_batch_cache_get_normal_edge(cu); } GPUBatch *DRW_cache_curve_edge_overlay_get(Object *ob) { - BLI_assert(ELEM(ob->type, OB_CURVE, OB_SURF)); + BLI_assert(ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)); struct Curve *cu = ob->data; return DRW_curve_batch_cache_get_edit_edges(cu); @@ -2944,7 +2944,7 @@ GPUBatch *DRW_cache_curve_edge_overlay_get(Object *ob) GPUBatch *DRW_cache_curve_vert_overlay_get(Object *ob) { - BLI_assert(ELEM(ob->type, OB_CURVE, OB_SURF)); + BLI_assert(ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)); struct Curve *cu = ob->data; return DRW_curve_batch_cache_get_edit_verts(cu); @@ -3373,7 +3373,7 @@ void drw_batch_cache_validate(Object *ob) case OB_MESH: DRW_mesh_batch_cache_validate(ob, (Mesh *)ob->data); break; - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_FONT: DRW_curve_batch_cache_validate((Curve *)ob->data); break; @@ -3423,7 +3423,7 @@ void drw_batch_cache_generate_requested(Object *ob) DRW_mesh_batch_cache_create_requested( DST.task_graph, ob, (Mesh *)ob->data, scene, is_paint_mode, use_hide); break; - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_FONT: DRW_curve_batch_cache_create_requested(ob, scene); break; diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index 738a9029167..49e51d77f7b 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -819,6 +819,7 @@ static void mesh_buffer_cache_create_requested_subdiv(MeshBatchCache *cache, EXTRACT_ADD_REQUESTED(vbo, edituv_data); /* Make sure UVs are computed before edituv stuffs. */ EXTRACT_ADD_REQUESTED(vbo, uv); + EXTRACT_ADD_REQUESTED(vbo, tan); EXTRACT_ADD_REQUESTED(vbo, edituv_stretch_area); EXTRACT_ADD_REQUESTED(vbo, edituv_stretch_angle); EXTRACT_ADD_REQUESTED(ibo, lines_adjacency); @@ -832,6 +833,7 @@ static void mesh_buffer_cache_create_requested_subdiv(MeshBatchCache *cache, return; } + mesh_render_data_update_looptris(mr, MR_ITER_LOOPTRI, MR_DATA_LOOPTRI); mesh_render_data_update_loose_geom(mr, mbc, MR_ITER_LEDGE | MR_ITER_LVERT, MR_DATA_LOOSE_GEOM); void *data_stack = MEM_mallocN(extractors.data_size_total(), __func__); diff --git a/source/blender/draw/intern/draw_cache_impl_curve.cc b/source/blender/draw/intern/draw_cache_impl_curve.cc index abba3beb893..6a3d3fa5e9e 100644 --- a/source/blender/draw/intern/draw_cache_impl_curve.cc +++ b/source/blender/draw/intern/draw_cache_impl_curve.cc @@ -945,7 +945,7 @@ int DRW_curve_material_count_get(Curve *cu) void DRW_curve_batch_cache_create_requested(Object *ob, const struct Scene *scene) { - BLI_assert(ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)); + BLI_assert(ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF, OB_FONT)); Curve *cu = (Curve *)ob->data; CurveBatchCache *cache = curve_batch_cache_get(cu); diff --git a/source/blender/draw/intern/draw_cache_impl_curves.cc b/source/blender/draw/intern/draw_cache_impl_curves.cc index a779c694cd2..df1ac12605a 100644 --- a/source/blender/draw/intern/draw_cache_impl_curves.cc +++ b/source/blender/draw/intern/draw_cache_impl_curves.cc @@ -22,7 +22,7 @@ #include "DNA_curves_types.h" #include "DNA_object_types.h" -#include "BKE_curves.h" +#include "BKE_curves.hh" #include "GPU_batch.h" #include "GPU_material.h" @@ -133,12 +133,12 @@ static void curves_batch_cache_fill_segments_proc_pos(Curves *curves, { /* TODO: use hair radius layer if available. */ const int curve_size = curves->geometry.curve_size; - Span<int> offsets{curves->geometry.offsets, curves->geometry.curve_size + 1}; - - Span<float3> positions{(float3 *)curves->geometry.position, curves->geometry.point_size}; + const blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap( + curves->geometry); + Span<float3> positions = geometry.positions(); for (const int i : IndexRange(curve_size)) { - const IndexRange curve_range(offsets[i], offsets[i + 1] - offsets[i]); + const IndexRange curve_range = geometry.range_for_curve(i); Span<float3> spline_positions = positions.slice(curve_range); float total_len = 0.0f; @@ -215,11 +215,11 @@ static void curves_batch_cache_fill_strands_data(Curves *curves, GPUVertBufRaw *data_step, GPUVertBufRaw *seg_step) { - const int curve_size = curves->geometry.curve_size; - Span<int> offsets{curves->geometry.offsets, curves->geometry.curve_size + 1}; + const blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap( + curves->geometry); - for (const int i : IndexRange(curve_size)) { - const IndexRange curve_range(offsets[i], offsets[i + 1] - offsets[i]); + for (const int i : IndexRange(geometry.curves_size())) { + const IndexRange curve_range = geometry.range_for_curve(i); *(uint *)GPU_vertbuf_raw_step(data_step) = curve_range.start(); *(ushort *)GPU_vertbuf_raw_step(seg_step) = curve_range.size() - 1; diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index 8833a354c21..79a080cfccd 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -339,11 +339,7 @@ static void drw_mesh_attributes_merge(DRW_MeshAttributes *dst, /* Return true if all requests in b are in a. */ static bool drw_mesh_attributes_overlap(DRW_MeshAttributes *a, DRW_MeshAttributes *b) { - if (a->num_requests != b->num_requests) { - return false; - } - - for (int i = 0; i < a->num_requests; i++) { + for (int i = 0; i < b->num_requests; i++) { if (!has_request(a, b->requests[i])) { return false; } @@ -1712,7 +1708,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, const int required_mode = BKE_subsurf_modifier_eval_required_mode(DRW_state_is_scene_render(), is_editmode); - const bool do_subdivision = BKE_subsurf_modifier_can_do_gpu_subdiv(scene, ob, required_mode); + const bool do_subdivision = BKE_subsurf_modifier_can_do_gpu_subdiv(scene, ob, me, required_mode); MeshBufferList *mbuflist = &cache->final.buff; diff --git a/source/blender/draw/intern/draw_cache_impl_subdivision.cc b/source/blender/draw/intern/draw_cache_impl_subdivision.cc index ac2e5bbca2e..5d99478476c 100644 --- a/source/blender/draw/intern/draw_cache_impl_subdivision.cc +++ b/source/blender/draw/intern/draw_cache_impl_subdivision.cc @@ -67,7 +67,6 @@ enum { SHADER_BUFFER_NORMALS_ACCUMULATE, SHADER_BUFFER_NORMALS_FINALIZE, SHADER_PATCH_EVALUATION, - SHADER_PATCH_EVALUATION_LIMIT_NORMALS, SHADER_PATCH_EVALUATION_FVAR, SHADER_PATCH_EVALUATION_FACE_DOTS, SHADER_COMP_CUSTOM_DATA_INTERP_1D, @@ -107,7 +106,6 @@ static const char *get_shader_code(int shader_type) return datatoc_common_subdiv_normals_finalize_comp_glsl; } case SHADER_PATCH_EVALUATION: - case SHADER_PATCH_EVALUATION_LIMIT_NORMALS: case SHADER_PATCH_EVALUATION_FVAR: case SHADER_PATCH_EVALUATION_FACE_DOTS: { return datatoc_common_subdiv_patch_evaluation_comp_glsl; @@ -159,9 +157,6 @@ static const char *get_shader_name(int shader_type) case SHADER_PATCH_EVALUATION: { return "subdiv patch evaluation"; } - case SHADER_PATCH_EVALUATION_LIMIT_NORMALS: { - return "subdiv patch evaluation limit normals"; - } case SHADER_PATCH_EVALUATION_FVAR: { return "subdiv patch evaluation face-varying"; } @@ -199,13 +194,7 @@ static GPUShader *get_patch_evaluation_shader(int shader_type) const char *compute_code = get_shader_code(shader_type); const char *defines = nullptr; - if (shader_type == SHADER_PATCH_EVALUATION_LIMIT_NORMALS) { - defines = - "#define OSD_PATCH_BASIS_GLSL\n" - "#define OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES\n" - "#define LIMIT_NORMALS\n"; - } - else if (shader_type == SHADER_PATCH_EVALUATION_FVAR) { + if (shader_type == SHADER_PATCH_EVALUATION_FVAR) { defines = "#define OSD_PATCH_BASIS_GLSL\n" "#define OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES\n" @@ -246,7 +235,6 @@ static GPUShader *get_subdiv_shader(int shader_type, const char *defines) { if (ELEM(shader_type, SHADER_PATCH_EVALUATION, - SHADER_PATCH_EVALUATION_LIMIT_NORMALS, SHADER_PATCH_EVALUATION_FVAR, SHADER_PATCH_EVALUATION_FACE_DOTS)) { return get_patch_evaluation_shader(shader_type); @@ -592,6 +580,67 @@ void draw_subdiv_cache_free(DRWSubdivCache *cache) SUBDIV_COARSE_FACE_FLAG_ACTIVE) \ << SUBDIV_COARSE_FACE_FLAG_OFFSET) +static uint32_t compute_coarse_face_flag(BMFace *f, BMFace *efa_act) +{ + if (f == nullptr) { + /* May happen during mapped extraction. */ + return 0; + } + + uint32_t flag = 0; + if (BM_elem_flag_test(f, BM_ELEM_SMOOTH)) { + flag |= SUBDIV_COARSE_FACE_FLAG_SMOOTH; + } + if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { + flag |= SUBDIV_COARSE_FACE_FLAG_SELECT; + } + if (f == efa_act) { + flag |= SUBDIV_COARSE_FACE_FLAG_ACTIVE; + } + const int loopstart = BM_elem_index_get(f->l_first); + return (uint)(loopstart) | (flag << SUBDIV_COARSE_FACE_FLAG_OFFSET); +} + +static void draw_subdiv_cache_extra_coarse_face_data_bm(BMesh *bm, + BMFace *efa_act, + uint32_t *flags_data) +{ + BMFace *f; + BMIter iter; + + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + const int index = BM_elem_index_get(f); + flags_data[index] = compute_coarse_face_flag(f, efa_act); + } +} + +static void draw_subdiv_cache_extra_coarse_face_data_mesh(Mesh *mesh, uint32_t *flags_data) +{ + for (int i = 0; i < mesh->totpoly; i++) { + uint32_t flag = 0; + if ((mesh->mpoly[i].flag & ME_SMOOTH) != 0) { + flag = SUBDIV_COARSE_FACE_FLAG_SMOOTH; + } + flags_data[i] = (uint)(mesh->mpoly[i].loopstart) | (flag << SUBDIV_COARSE_FACE_FLAG_OFFSET); + } +} + +static void draw_subdiv_cache_extra_coarse_face_data_mapped(Mesh *mesh, + BMesh *bm, + MeshRenderData *mr, + uint32_t *flags_data) +{ + if (bm == nullptr) { + draw_subdiv_cache_extra_coarse_face_data_mesh(mesh, flags_data); + return; + } + + for (int i = 0; i < mesh->totpoly; i++) { + BMFace *f = bm_original_face_get(mr, i); + flags_data[i] = compute_coarse_face_flag(f, mr->efa_act); + } +} + static void draw_subdiv_cache_update_extra_coarse_face_data(DRWSubdivCache *cache, Mesh *mesh, MeshRenderData *mr) @@ -611,56 +660,13 @@ static void draw_subdiv_cache_update_extra_coarse_face_data(DRWSubdivCache *cach uint32_t *flags_data = (uint32_t *)(GPU_vertbuf_get_data(cache->extra_coarse_face_data)); if (mr->extract_type == MR_EXTRACT_BMESH) { - BMesh *bm = cache->bm; - BMFace *f; - BMIter iter; - - /* Ensure all current elements follow new customdata layout. */ - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - const int index = BM_elem_index_get(f); - uint32_t flag = 0; - if (BM_elem_flag_test(f, BM_ELEM_SMOOTH)) { - flag |= SUBDIV_COARSE_FACE_FLAG_SMOOTH; - } - if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { - flag |= SUBDIV_COARSE_FACE_FLAG_SELECT; - } - if (f == mr->efa_act) { - flag |= SUBDIV_COARSE_FACE_FLAG_ACTIVE; - } - const int loopstart = BM_elem_index_get(f->l_first); - flags_data[index] = (uint)(loopstart) | (flag << SUBDIV_COARSE_FACE_FLAG_OFFSET); - } + draw_subdiv_cache_extra_coarse_face_data_bm(cache->bm, mr->efa_act, flags_data); } else if (mr->extract_type == MR_EXTRACT_MAPPED) { - for (int i = 0; i < mesh->totpoly; i++) { - BMFace *f = bm_original_face_get(mr, i); - uint32_t flag = 0; - - if (f) { - if (BM_elem_flag_test(f, BM_ELEM_SMOOTH)) { - flag |= SUBDIV_COARSE_FACE_FLAG_SMOOTH; - } - if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { - flag |= SUBDIV_COARSE_FACE_FLAG_SELECT; - } - if (f == mr->efa_act) { - flag |= SUBDIV_COARSE_FACE_FLAG_ACTIVE; - } - const int loopstart = BM_elem_index_get(f->l_first); - flag = (uint)(loopstart) | (flag << SUBDIV_COARSE_FACE_FLAG_OFFSET); - } - flags_data[i] = flag; - } + draw_subdiv_cache_extra_coarse_face_data_mapped(mesh, cache->bm, mr, flags_data); } else { - for (int i = 0; i < mesh->totpoly; i++) { - uint32_t flag = 0; - if ((mesh->mpoly[i].flag & ME_SMOOTH) != 0) { - flag = SUBDIV_COARSE_FACE_FLAG_SMOOTH; - } - flags_data[i] = (uint)(mesh->mpoly[i].loopstart) | (flag << SUBDIV_COARSE_FACE_FLAG_OFFSET); - } + draw_subdiv_cache_extra_coarse_face_data_mesh(mesh, flags_data); } /* Make sure updated data is re-uploaded. */ @@ -1176,9 +1182,7 @@ static void drw_subdiv_compute_dispatch(const DRWSubdivCache *cache, GPU_compute_dispatch(shader, dispatch_rx, dispatch_ry, 1); } -void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, - GPUVertBuf *pos_nor, - const bool do_limit_normals) +void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, GPUVertBuf *pos_nor) { Subdiv *subdiv = cache->subdiv; OpenSubdiv_Evaluator *evaluator = subdiv->evaluator; @@ -1203,8 +1207,7 @@ void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, get_patch_param_format()); evaluator->wrapPatchParamBuffer(evaluator, &patch_param_buffer_interface); - GPUShader *shader = get_patch_evaluation_shader( - do_limit_normals ? SHADER_PATCH_EVALUATION_LIMIT_NORMALS : SHADER_PATCH_EVALUATION); + GPUShader *shader = get_patch_evaluation_shader(SHADER_PATCH_EVALUATION); GPU_shader_bind(shader); GPU_vertbuf_bind_as_ssbo(src_buffer, 0); @@ -1299,7 +1302,8 @@ void draw_subdiv_interp_custom_data(const DRWSubdivCache *cache, GPUVertBuf *src_data, GPUVertBuf *dst_data, int dimensions, - int dst_offset) + int dst_offset, + bool compress_to_u16) { GPUShader *shader = nullptr; @@ -1319,10 +1323,17 @@ void draw_subdiv_interp_custom_data(const DRWSubdivCache *cache, "#define DIMENSIONS 3\n"); } else if (dimensions == 4) { - shader = get_subdiv_shader(SHADER_COMP_CUSTOM_DATA_INTERP_4D, - "#define SUBDIV_POLYGON_OFFSET\n" - "#define DIMENSIONS 4\n" - "#define GPU_FETCH_U16_TO_FLOAT\n"); + if (compress_to_u16) { + shader = get_subdiv_shader(SHADER_COMP_CUSTOM_DATA_INTERP_4D, + "#define SUBDIV_POLYGON_OFFSET\n" + "#define DIMENSIONS 4\n" + "#define GPU_FETCH_U16_TO_FLOAT\n"); + } + else { + shader = get_subdiv_shader(SHADER_COMP_CUSTOM_DATA_INTERP_4D, + "#define SUBDIV_POLYGON_OFFSET\n" + "#define DIMENSIONS 4\n"); + } } else { /* Crash if dimensions are not supported. */ @@ -1376,6 +1387,7 @@ void draw_subdiv_accumulate_normals(const DRWSubdivCache *cache, GPUVertBuf *pos_nor, GPUVertBuf *face_adjacency_offsets, GPUVertBuf *face_adjacency_lists, + GPUVertBuf *vertex_loop_map, GPUVertBuf *vertex_normals) { GPUShader *shader = get_subdiv_shader(SHADER_BUFFER_NORMALS_ACCUMULATE, nullptr); @@ -1386,6 +1398,7 @@ void draw_subdiv_accumulate_normals(const DRWSubdivCache *cache, GPU_vertbuf_bind_as_ssbo(pos_nor, binding_point++); GPU_vertbuf_bind_as_ssbo(face_adjacency_offsets, binding_point++); GPU_vertbuf_bind_as_ssbo(face_adjacency_lists, binding_point++); + GPU_vertbuf_bind_as_ssbo(vertex_loop_map, binding_point++); GPU_vertbuf_bind_as_ssbo(vertex_normals, binding_point++); drw_subdiv_compute_dispatch(cache, shader, 0, 0, cache->num_subdiv_verts); @@ -1785,9 +1798,9 @@ static bool draw_subdiv_create_requested_buffers(const Scene *scene, const float obmat[4][4], const bool do_final, const bool do_uvedit, - const bool UNUSED(use_subsurf_fdots), + const bool /*use_subsurf_fdots*/, const ToolSettings *ts, - const bool UNUSED(use_hide), + const bool /*use_hide*/, OpenSubdiv_EvaluatorCache *evaluator_cache) { SubsurfModifierData *smd = BKE_object_get_last_subsurf_modifier(ob); @@ -1833,8 +1846,6 @@ static bool draw_subdiv_create_requested_buffers(const Scene *scene, draw_cache->subdiv = subdiv; draw_cache->optimal_display = optimal_display; draw_cache->num_subdiv_triangles = tris_count_from_number_of_loops(draw_cache->num_subdiv_loops); - /* We can only evaluate limit normals if the patches are adaptive. */ - draw_cache->do_limit_normals = settings.is_adaptive; draw_cache->use_custom_loop_normals = (smd->flags & eSubsurfModifierFlag_UseCustomNormals) && (mesh_eval->flag & ME_AUTOSMOOTH) && diff --git a/source/blender/draw/intern/draw_common.c b/source/blender/draw/intern/draw_common.c index fcfaf404fc2..2897234f4dc 100644 --- a/source/blender/draw/intern/draw_common.c +++ b/source/blender/draw/intern/draw_common.c @@ -412,7 +412,7 @@ bool DRW_object_is_flat(Object *ob, int *r_axis) if (!ELEM(ob->type, OB_MESH, - OB_CURVE, + OB_CURVES_LEGACY, OB_SURF, OB_FONT, OB_MBALL, diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 440f74af64b..2886fe53879 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -2774,7 +2774,7 @@ void DRW_draw_depth_object( GPU_uniformbuf_free(ubo); } break; - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: break; } diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index 3b35b8c1f9d..95691a0df68 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -514,7 +514,7 @@ static void drw_call_calc_orco(Object *ob, float (*r_orcofacs)[4]) case ID_ME: BKE_mesh_texspace_get_reference((Mesh *)ob_data, NULL, &texcoloc, &texcosize); break; - case ID_CU: { + case ID_CU_LEGACY: { Curve *cu = (Curve *)ob_data; BKE_curve_texspace_ensure(cu); texcoloc = cu->loc; diff --git a/source/blender/draw/intern/draw_subdivision.h b/source/blender/draw/intern/draw_subdivision.h index 6714ba571e5..bd02df6d48b 100644 --- a/source/blender/draw/intern/draw_subdivision.h +++ b/source/blender/draw/intern/draw_subdivision.h @@ -51,7 +51,6 @@ typedef struct DRWSubdivCache { struct BMesh *bm; struct Subdiv *subdiv; bool optimal_display; - bool do_limit_normals; bool use_custom_loop_normals; /* Coordinates used to evaluate patches for UVs, positions, and normals. */ @@ -165,6 +164,7 @@ void draw_subdiv_accumulate_normals(const DRWSubdivCache *cache, struct GPUVertBuf *pos_nor, struct GPUVertBuf *face_adjacency_offsets, struct GPUVertBuf *face_adjacency_lists, + struct GPUVertBuf *vertex_loop_map, struct GPUVertBuf *vertex_normals); void draw_subdiv_finalize_normals(const DRWSubdivCache *cache, @@ -176,15 +176,14 @@ void draw_subdiv_finalize_custom_normals(const DRWSubdivCache *cache, GPUVertBuf *src_custom_normals, GPUVertBuf *pos_nor); -void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, - struct GPUVertBuf *pos_nor, - bool do_limit_normals); +void draw_subdiv_extract_pos_nor(const DRWSubdivCache *cache, struct GPUVertBuf *pos_nor); void draw_subdiv_interp_custom_data(const DRWSubdivCache *cache, struct GPUVertBuf *src_data, struct GPUVertBuf *dst_data, int dimensions, - int dst_offset); + int dst_offset, + bool compress_to_u16); void draw_subdiv_extract_uvs(const DRWSubdivCache *cache, struct GPUVertBuf *uvs, diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc index d5e34bc082e..4f4aa764fbc 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc @@ -402,7 +402,7 @@ static void extract_attr_init_subdiv(const DRWSubdivCache *subdiv_cache, /* Ensure data is uploaded properly. */ GPU_vertbuf_tag_dirty(src_data); draw_subdiv_interp_custom_data( - subdiv_cache, src_data, dst_buffer, static_cast<int>(dimensions), 0); + subdiv_cache, src_data, dst_buffer, static_cast<int>(dimensions), 0, false); GPU_vertbuf_discard(src_data); } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc index bd7f1ba0128..22fda284a74 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc @@ -217,14 +217,12 @@ static void extract_pos_nor_init_subdiv(const DRWSubdivCache *subdiv_cache, void *UNUSED(data)) { GPUVertBuf *vbo = static_cast<GPUVertBuf *>(buffer); - const bool do_limit_normals = subdiv_cache->do_limit_normals && - !subdiv_cache->use_custom_loop_normals; /* Initialize the vertex buffer, it was already allocated. */ GPU_vertbuf_init_build_on_device( vbo, get_pos_nor_format(), subdiv_cache->num_subdiv_loops + mr->loop_loose_len); - draw_subdiv_extract_pos_nor(subdiv_cache, vbo, do_limit_normals); + draw_subdiv_extract_pos_nor(subdiv_cache, vbo); if (subdiv_cache->use_custom_loop_normals) { Mesh *coarse_mesh = subdiv_cache->mesh; @@ -243,14 +241,15 @@ static void extract_pos_nor_init_subdiv(const DRWSubdivCache *subdiv_cache, GPU_vertbuf_init_build_on_device( dst_custom_normals, get_custom_normals_format(), subdiv_cache->num_subdiv_loops); - draw_subdiv_interp_custom_data(subdiv_cache, src_custom_normals, dst_custom_normals, 3, 0); + draw_subdiv_interp_custom_data( + subdiv_cache, src_custom_normals, dst_custom_normals, 3, 0, false); draw_subdiv_finalize_custom_normals(subdiv_cache, dst_custom_normals, vbo); GPU_vertbuf_discard(src_custom_normals); GPU_vertbuf_discard(dst_custom_normals); } - else if (!do_limit_normals) { + else { /* We cannot evaluate vertex normals using the limit surface, so compute them manually. */ GPUVertBuf *subdiv_loop_subdiv_vert_index = draw_subdiv_build_origindex_buffer( subdiv_cache->subdiv_loop_subdiv_vert_index, subdiv_cache->num_subdiv_loops); @@ -263,6 +262,7 @@ static void extract_pos_nor_init_subdiv(const DRWSubdivCache *subdiv_cache, vbo, subdiv_cache->subdiv_vertex_face_adjacency_offsets, subdiv_cache->subdiv_vertex_face_adjacency, + subdiv_loop_subdiv_vert_index, vertex_normals); draw_subdiv_finalize_normals(subdiv_cache, vertex_normals, subdiv_loop_subdiv_vert_index, vbo); diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc index 78c215845e0..96595df9276 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc @@ -151,7 +151,7 @@ static void extract_sculpt_data_init_subdiv(const DRWSubdivCache *subdiv_cache, GPU_vertbuf_init_build_on_device( subdiv_mask_vbo, &mask_format, subdiv_cache->num_subdiv_loops); - draw_subdiv_interp_custom_data(subdiv_cache, mask_vbo, subdiv_mask_vbo, 1, 0); + draw_subdiv_interp_custom_data(subdiv_cache, mask_vbo, subdiv_mask_vbo, 1, 0, false); } /* Then, gather face sets. */ diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc index 209168750e7..225d1676151 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc @@ -16,22 +16,26 @@ #include "extract_mesh.h" +#include "draw_subdivision.h" + namespace blender::draw { /* ---------------------------------------------------------------------- */ /** \name Extract Tangent layers * \{ */ -static void extract_tan_ex_init(const MeshRenderData *mr, - struct MeshBatchCache *cache, - GPUVertBuf *vbo, - const bool do_hq) +static void extract_tan_init_common(const MeshRenderData *mr, + struct MeshBatchCache *cache, + GPUVertFormat *format, + GPUVertCompType comp_type, + GPUVertFetchMode fetch_mode, + CustomData *r_loop_data, + int *r_v_len, + int *r_tan_len, + char r_tangent_names[MAX_MTFACE][MAX_CUSTOMDATA_LAYER_NAME], + bool *r_use_orco_tan) { - GPUVertCompType comp_type = do_hq ? GPU_COMP_I16 : GPU_COMP_I10; - GPUVertFetchMode fetch_mode = GPU_FETCH_INT_TO_FLOAT_UNIT; - - GPUVertFormat format = {0}; - GPU_vertformat_deinterleave(&format); + GPU_vertformat_deinterleave(format); CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; @@ -41,7 +45,6 @@ static void extract_tan_ex_init(const MeshRenderData *mr, bool use_orco_tan = cache->cd_used.tan_orco != 0; int tan_len = 0; - char tangent_names[MAX_MTFACE][MAX_CUSTOMDATA_LAYER_NAME]; /* FIXME(T91838): This is to avoid a crash when orco tangent was requested but there are valid * uv layers. It would be better to fix the root cause. */ @@ -57,17 +60,17 @@ static void extract_tan_ex_init(const MeshRenderData *mr, GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); /* Tangent layer name. */ BLI_snprintf(attr_name, sizeof(attr_name), "t%s", attr_safe_name); - GPU_vertformat_attr_add(&format, attr_name, comp_type, 4, fetch_mode); + GPU_vertformat_attr_add(format, attr_name, comp_type, 4, fetch_mode); /* Active render layer name. */ if (i == CustomData_get_render_layer(cd_ldata, CD_MLOOPUV)) { - GPU_vertformat_alias_add(&format, "t"); + GPU_vertformat_alias_add(format, "t"); } /* Active display layer name. */ if (i == CustomData_get_active_layer(cd_ldata, CD_MLOOPUV)) { - GPU_vertformat_alias_add(&format, "at"); + GPU_vertformat_alias_add(format, "at"); } - BLI_strncpy(tangent_names[tan_len++], layer_name, MAX_CUSTOMDATA_LAYER_NAME); + BLI_strncpy(r_tangent_names[tan_len++], layer_name, MAX_CUSTOMDATA_LAYER_NAME); } } if (use_orco_tan && orco == nullptr) { @@ -94,20 +97,19 @@ static void extract_tan_ex_init(const MeshRenderData *mr, } /* Start Fresh */ - CustomData loop_data; - CustomData_reset(&loop_data); + CustomData_reset(r_loop_data); if (tan_len != 0 || use_orco_tan) { short tangent_mask = 0; bool calc_active_tangent = false; if (mr->extract_type == MR_EXTRACT_BMESH) { BKE_editmesh_loop_tangent_calc(mr->edit_bmesh, calc_active_tangent, - tangent_names, + r_tangent_names, tan_len, mr->poly_normals, mr->loop_normals, orco, - &loop_data, + r_loop_data, mr->loop_len, &tangent_mask); } @@ -120,13 +122,13 @@ static void extract_tan_ex_init(const MeshRenderData *mr, mr->tri_len, cd_ldata, calc_active_tangent, - tangent_names, + r_tangent_names, tan_len, mr->vert_normals, mr->poly_normals, mr->loop_normals, orco, - &loop_data, + r_loop_data, mr->loop_len, &tangent_mask); } @@ -134,12 +136,12 @@ static void extract_tan_ex_init(const MeshRenderData *mr, if (use_orco_tan) { char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; - const char *layer_name = CustomData_get_layer_name(&loop_data, CD_TANGENT, 0); + const char *layer_name = CustomData_get_layer_name(r_loop_data, CD_TANGENT, 0); GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); BLI_snprintf(attr_name, sizeof(*attr_name), "t%s", attr_safe_name); - GPU_vertformat_attr_add(&format, attr_name, comp_type, 4, fetch_mode); - GPU_vertformat_alias_add(&format, "t"); - GPU_vertformat_alias_add(&format, "at"); + GPU_vertformat_attr_add(format, attr_name, comp_type, 4, fetch_mode); + GPU_vertformat_alias_add(format, "t"); + GPU_vertformat_alias_add(format, "at"); } if (orco_allocated) { @@ -147,12 +149,42 @@ static void extract_tan_ex_init(const MeshRenderData *mr, } int v_len = mr->loop_len; - if (format.attr_len == 0) { - GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + if (format->attr_len == 0) { + GPU_vertformat_attr_add(format, "dummy", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); /* VBO will not be used, only allocate minimum of memory. */ v_len = 1; } + *r_use_orco_tan = use_orco_tan; + *r_v_len = v_len; + *r_tan_len = tan_len; +} + +static void extract_tan_ex_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + GPUVertBuf *vbo, + const bool do_hq) +{ + GPUVertCompType comp_type = do_hq ? GPU_COMP_I16 : GPU_COMP_I10; + GPUVertFetchMode fetch_mode = GPU_FETCH_INT_TO_FLOAT_UNIT; + + GPUVertFormat format = {0}; + CustomData loop_data; + int v_len = 0; + int tan_len = 0; + bool use_orco_tan; + char tangent_names[MAX_MTFACE][MAX_CUSTOMDATA_LAYER_NAME]; + extract_tan_init_common(mr, + cache, + &format, + comp_type, + fetch_mode, + &loop_data, + &v_len, + &tan_len, + tangent_names, + &use_orco_tan); + GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, v_len); @@ -211,10 +243,92 @@ static void extract_tan_init(const MeshRenderData *mr, extract_tan_ex_init(mr, cache, vbo, false); } +static GPUVertFormat *get_coarse_tan_format() +{ + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "tan", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + } + return &format; +} + +static void extract_tan_init_subdiv(const DRWSubdivCache *subdiv_cache, + const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buffer, + void *UNUSED(data)) +{ + GPUVertCompType comp_type = GPU_COMP_F32; + GPUVertFetchMode fetch_mode = GPU_FETCH_FLOAT; + GPUVertFormat format = {0}; + CustomData loop_data; + int coarse_len = 0; + int tan_len = 0; + bool use_orco_tan; + char tangent_names[MAX_MTFACE][MAX_CUSTOMDATA_LAYER_NAME]; + extract_tan_init_common(mr, + cache, + &format, + comp_type, + fetch_mode, + &loop_data, + &coarse_len, + &tan_len, + tangent_names, + &use_orco_tan); + + GPUVertBuf *dst_buffer = static_cast<GPUVertBuf *>(buffer); + GPU_vertbuf_init_build_on_device(dst_buffer, &format, subdiv_cache->num_subdiv_loops); + + GPUVertBuf *coarse_vbo = GPU_vertbuf_calloc(); + /* Dynamic as we upload and interpolate layers one at a time. */ + GPU_vertbuf_init_with_format_ex(coarse_vbo, get_coarse_tan_format(), GPU_USAGE_DYNAMIC); + GPU_vertbuf_data_alloc(coarse_vbo, coarse_len); + + /* Index of the tangent layer in the compact buffer. Used layers are stored in a single buffer. + */ + int pack_layer_index = 0; + for (int i = 0; i < tan_len; i++) { + float(*tan_data)[4] = (float(*)[4])GPU_vertbuf_get_data(coarse_vbo); + const char *name = tangent_names[i]; + float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_named(&loop_data, CD_TANGENT, name); + for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) { + copy_v3_v3(*tan_data, layer_data[ml_index]); + (*tan_data)[3] = (layer_data[ml_index][3] > 0.0f) ? 1.0f : -1.0f; + tan_data++; + } + + /* Ensure data is uploaded properly. */ + GPU_vertbuf_tag_dirty(coarse_vbo); + /* Include stride in offset. */ + const int dst_offset = (int)subdiv_cache->num_subdiv_loops * 4 * pack_layer_index++; + draw_subdiv_interp_custom_data(subdiv_cache, coarse_vbo, dst_buffer, 4, dst_offset, false); + } + if (use_orco_tan) { + float(*tan_data)[4] = (float(*)[4])GPU_vertbuf_get_data(coarse_vbo); + float(*layer_data)[4] = (float(*)[4])CustomData_get_layer_n(&loop_data, CD_TANGENT, 0); + for (int ml_index = 0; ml_index < mr->loop_len; ml_index++) { + copy_v3_v3(*tan_data, layer_data[ml_index]); + (*tan_data)[3] = (layer_data[ml_index][3] > 0.0f) ? 1.0f : -1.0f; + tan_data++; + } + + /* Ensure data is uploaded properly. */ + GPU_vertbuf_tag_dirty(coarse_vbo); + /* Include stride in offset. */ + const int dst_offset = (int)subdiv_cache->num_subdiv_loops * 4 * pack_layer_index++; + draw_subdiv_interp_custom_data(subdiv_cache, coarse_vbo, dst_buffer, 4, dst_offset, true); + } + + CustomData_free(&loop_data, mr->loop_len); + GPU_vertbuf_discard(coarse_vbo); +} + constexpr MeshExtract create_extractor_tan() { MeshExtract extractor = {nullptr}; extractor.init = extract_tan_init; + extractor.init_subdiv = extract_tan_init_subdiv; extractor.data_type = MR_DATA_POLY_NOR | MR_DATA_TAN_LOOP_NOR | MR_DATA_LOOPTRI; extractor.data_size = 0; extractor.use_threading = false; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc index 138ff9fd1ff..7a8f4a9a17e 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc @@ -164,7 +164,7 @@ static void extract_vcol_init_subdiv(const DRWSubdivCache *subdiv_cache, /* Ensure data is uploaded properly. */ GPU_vertbuf_tag_dirty(src_data); - draw_subdiv_interp_custom_data(subdiv_cache, src_data, dst_buffer, 4, dst_offset); + draw_subdiv_interp_custom_data(subdiv_cache, src_data, dst_buffer, 4, dst_offset, true); } } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc index 2e30d6bdfcf..89aa16ca0c7 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc @@ -153,10 +153,10 @@ static void extract_weights_iter_poly_mesh(const MeshRenderData *mr, } static void extract_weights_init_subdiv(const DRWSubdivCache *subdiv_cache, - const MeshRenderData *UNUSED(mr), + const MeshRenderData *mr, struct MeshBatchCache *cache, void *buffer, - void *UNUSED(data)) + void *_data) { Mesh *coarse_mesh = subdiv_cache->mesh; GPUVertBuf *vbo = static_cast<GPUVertBuf *>(buffer); @@ -168,32 +168,24 @@ static void extract_weights_init_subdiv(const DRWSubdivCache *subdiv_cache, GPU_vertbuf_init_build_on_device(vbo, &format, subdiv_cache->num_subdiv_loops); GPUVertBuf *coarse_weights = GPU_vertbuf_calloc(); - GPU_vertbuf_init_with_format(coarse_weights, &format); - GPU_vertbuf_data_alloc(coarse_weights, coarse_mesh->totloop); - float *coarse_weights_data = static_cast<float *>(GPU_vertbuf_get_data(coarse_weights)); + extract_weights_init(mr, cache, coarse_weights, _data); - const DRW_MeshWeightState *wstate = &cache->weight_state; - const MDeformVert *dverts = static_cast<const MDeformVert *>( - CustomData_get_layer(&coarse_mesh->vdata, CD_MDEFORMVERT)); - - for (int i = 0; i < coarse_mesh->totpoly; i++) { - const MPoly *mpoly = &coarse_mesh->mpoly[i]; - - for (int loop_index = mpoly->loopstart; loop_index < mpoly->loopstart + mpoly->totloop; - loop_index++) { - const MLoop *ml = &coarse_mesh->mloop[loop_index]; - - if (dverts != nullptr) { - const MDeformVert *dvert = &dverts[ml->v]; - coarse_weights_data[loop_index] = evaluate_vertex_weight(dvert, wstate); - } - else { - coarse_weights_data[loop_index] = evaluate_vertex_weight(nullptr, wstate); - } + if (mr->extract_type != MR_EXTRACT_BMESH) { + for (int i = 0; i < coarse_mesh->totpoly; i++) { + const MPoly *mpoly = &coarse_mesh->mpoly[i]; + extract_weights_iter_poly_mesh(mr, mpoly, i, _data); + } + } + else { + BMIter f_iter; + BMFace *efa; + int face_index = 0; + BM_ITER_MESH_INDEX (efa, &f_iter, mr->bm, BM_FACES_OF_MESH, face_index) { + extract_weights_iter_poly_bm(mr, efa, face_index, _data); } } - draw_subdiv_interp_custom_data(subdiv_cache, coarse_weights, vbo, 1, 0); + draw_subdiv_interp_custom_data(subdiv_cache, coarse_weights, vbo, 1, 0, false); GPU_vertbuf_discard(coarse_weights); } diff --git a/source/blender/draw/intern/shaders/common_subdiv_custom_data_interp_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_custom_data_interp_comp.glsl index df0016761e2..097ae0b3913 100644 --- a/source/blender/draw/intern/shaders/common_subdiv_custom_data_interp_comp.glsl +++ b/source/blender/draw/intern/shaders/common_subdiv_custom_data_interp_comp.glsl @@ -1,5 +1,5 @@ -/* To be compile with common_subdiv_lib.glsl */ +/* To be compiled with common_subdiv_lib.glsl */ layout(std430, binding = 1) readonly restrict buffer sourceBuffer { diff --git a/source/blender/draw/intern/shaders/common_subdiv_ibo_lines_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_ibo_lines_comp.glsl index f11c0f6427e..3cbb9f980f3 100644 --- a/source/blender/draw/intern/shaders/common_subdiv_ibo_lines_comp.glsl +++ b/source/blender/draw/intern/shaders/common_subdiv_ibo_lines_comp.glsl @@ -1,5 +1,5 @@ -/* To be compile with common_subdiv_lib.glsl */ +/* To be compiled with common_subdiv_lib.glsl */ layout(std430, binding = 0) readonly buffer inputEdgeOrigIndex { diff --git a/source/blender/draw/intern/shaders/common_subdiv_ibo_tris_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_ibo_tris_comp.glsl index 3257ebdae17..3dccc82541e 100644 --- a/source/blender/draw/intern/shaders/common_subdiv_ibo_tris_comp.glsl +++ b/source/blender/draw/intern/shaders/common_subdiv_ibo_tris_comp.glsl @@ -1,5 +1,5 @@ -/* To be compile with common_subdiv_lib.glsl */ +/* To be compiled with common_subdiv_lib.glsl */ /* Generate triangles from subdivision quads indices. */ diff --git a/source/blender/draw/intern/shaders/common_subdiv_lib.glsl b/source/blender/draw/intern/shaders/common_subdiv_lib.glsl index e6538d80111..5d71c5e4bb8 100644 --- a/source/blender/draw/intern/shaders/common_subdiv_lib.glsl +++ b/source/blender/draw/intern/shaders/common_subdiv_lib.glsl @@ -140,6 +140,13 @@ void set_vertex_nor(inout PosNorLoop vertex_data, vec3 nor) set_vertex_nor(vertex_data, nor, 0); } +void add_newell_cross_v3_v3v3(inout vec3 n, vec3 v_prev, vec3 v_curr) +{ + n[0] += (v_prev[1] - v_curr[1]) * (v_prev[2] + v_curr[2]); + n[1] += (v_prev[2] - v_curr[2]) * (v_prev[0] + v_curr[0]); + n[2] += (v_prev[0] - v_curr[0]) * (v_prev[1] + v_curr[1]); +} + #define ORIGINDEX_NONE -1 #ifdef SUBDIV_POLYGON_OFFSET diff --git a/source/blender/draw/intern/shaders/common_subdiv_normals_accumulate_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_normals_accumulate_comp.glsl index 575090472b1..0665cadfd2d 100644 --- a/source/blender/draw/intern/shaders/common_subdiv_normals_accumulate_comp.glsl +++ b/source/blender/draw/intern/shaders/common_subdiv_normals_accumulate_comp.glsl @@ -1,5 +1,5 @@ -/* To be compile with common_subdiv_lib.glsl */ +/* To be compiled with common_subdiv_lib.glsl */ layout(std430, binding = 0) readonly buffer inputVertexData { @@ -16,11 +16,33 @@ layout(std430, binding = 2) readonly buffer faceAdjacencyLists uint face_adjacency_lists[]; }; -layout(std430, binding = 3) writeonly buffer vertexNormals +layout(std430, binding = 3) readonly buffer vertexLoopMap +{ + uint vert_loop_map[]; +}; + +layout(std430, binding = 4) writeonly buffer vertexNormals { vec3 normals[]; }; +void find_prev_and_next_vertex_on_face( + uint face_index, uint vertex_index, out uint curr, out uint next, out uint prev) +{ + uint start_loop_index = face_index * 4; + + for (uint i = 0; i < 4; i++) { + uint subdiv_vert_index = vert_loop_map[start_loop_index + i]; + + if (subdiv_vert_index == vertex_index) { + curr = i; + next = (i + 1) % 4; + prev = (i + 4 - 1) % 4; + break; + } + } +} + void main() { uint vertex_index = get_global_invocation_index(); @@ -39,18 +61,37 @@ void main() uint adjacent_face = face_adjacency_lists[first_adjacent_face_offset + i]; uint start_loop_index = adjacent_face * 4; - /* Compute face normal. */ - vec3 adjacent_verts[3]; - for (uint j = 0; j < 3; j++) { - adjacent_verts[j] = get_vertex_pos(pos_nor[start_loop_index + j]); + /* Compute the face normal using Newell's method. */ + vec3 verts[4]; + for (uint j = 0; j < 4; j++) { + verts[j] = get_vertex_pos(pos_nor[start_loop_index + j]); } - vec3 face_normal = normalize( - cross(adjacent_verts[1] - adjacent_verts[0], adjacent_verts[2] - adjacent_verts[0])); - accumulated_normal += face_normal; + vec3 face_normal = vec3(0.0); + add_newell_cross_v3_v3v3(face_normal, verts[0], verts[1]); + add_newell_cross_v3_v3v3(face_normal, verts[1], verts[2]); + add_newell_cross_v3_v3v3(face_normal, verts[2], verts[3]); + add_newell_cross_v3_v3v3(face_normal, verts[3], verts[0]); + + /* Accumulate angle weighted normal. */ + uint curr_vert = 0; + uint next_vert = 0; + uint prev_vert = 0; + find_prev_and_next_vertex_on_face( + adjacent_face, vertex_index, curr_vert, next_vert, prev_vert); + + vec3 curr_co = verts[curr_vert]; + vec3 prev_co = verts[next_vert]; + vec3 next_co = verts[prev_vert]; + + vec3 edvec_prev = normalize(prev_co - curr_co); + vec3 edvec_next = normalize(curr_co - next_co); + + float fac = acos(-dot(edvec_prev, edvec_next)); + + accumulated_normal += face_normal * fac; } - float weight = 1.0 / float(number_of_adjacent_faces); vec3 normal = normalize(accumulated_normal); normals[vertex_index] = normal; } diff --git a/source/blender/draw/intern/shaders/common_subdiv_normals_finalize_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_normals_finalize_comp.glsl index c2e0e752783..e6a56ff02c7 100644 --- a/source/blender/draw/intern/shaders/common_subdiv_normals_finalize_comp.glsl +++ b/source/blender/draw/intern/shaders/common_subdiv_normals_finalize_comp.glsl @@ -1,5 +1,5 @@ -/* To be compile with common_subdiv_lib.glsl */ +/* To be compiled with common_subdiv_lib.glsl */ #ifdef CUSTOM_NORMALS struct CustomNormal { diff --git a/source/blender/draw/intern/shaders/common_subdiv_patch_evaluation_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_patch_evaluation_comp.glsl index 5dd7decf663..65cf4ebb90f 100644 --- a/source/blender/draw/intern/shaders/common_subdiv_patch_evaluation_comp.glsl +++ b/source/blender/draw/intern/shaders/common_subdiv_patch_evaluation_comp.glsl @@ -1,5 +1,5 @@ -/* To be compile with common_subdiv_lib.glsl */ +/* To be compiled with common_subdiv_lib.glsl */ /* Source buffer. */ layout(std430, binding = 0) buffer src_buffer @@ -394,12 +394,8 @@ void main() evaluate_patches_limits(patch_co.patch_index, uv.x, uv.y, pos, du, dv); -# if defined(LIMIT_NORMALS) - vec3 nor = normalize(cross(du, dv)); -# else /* This will be computed later. */ vec3 nor = vec3(0.0); -# endif int origindex = input_vert_origindex[loop_index]; uint flag = 0; diff --git a/source/blender/draw/intern/shaders/common_subdiv_vbo_edge_fac_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_vbo_edge_fac_comp.glsl index 6c76cd41ca4..2161f0b28a9 100644 --- a/source/blender/draw/intern/shaders/common_subdiv_vbo_edge_fac_comp.glsl +++ b/source/blender/draw/intern/shaders/common_subdiv_vbo_edge_fac_comp.glsl @@ -1,5 +1,5 @@ -/* To be compile with common_subdiv_lib.glsl */ +/* To be compiled with common_subdiv_lib.glsl */ layout(std430, binding = 0) readonly buffer inputVertexData { diff --git a/source/blender/draw/intern/shaders/common_subdiv_vbo_edituv_strech_angle_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_vbo_edituv_strech_angle_comp.glsl index ea73b9482d3..a8c9b7183eb 100644 --- a/source/blender/draw/intern/shaders/common_subdiv_vbo_edituv_strech_angle_comp.glsl +++ b/source/blender/draw/intern/shaders/common_subdiv_vbo_edituv_strech_angle_comp.glsl @@ -1,5 +1,5 @@ -/* To be compile with common_subdiv_lib.glsl */ +/* To be compiled with common_subdiv_lib.glsl */ layout(std430, binding = 0) readonly buffer inputVerts { diff --git a/source/blender/draw/intern/shaders/common_subdiv_vbo_edituv_strech_area_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_vbo_edituv_strech_area_comp.glsl index e897fb3f3c0..230484048b1 100644 --- a/source/blender/draw/intern/shaders/common_subdiv_vbo_edituv_strech_area_comp.glsl +++ b/source/blender/draw/intern/shaders/common_subdiv_vbo_edituv_strech_area_comp.glsl @@ -1,5 +1,5 @@ -/* To be compile with common_subdiv_lib.glsl */ +/* To be compiled with common_subdiv_lib.glsl */ layout(std430, binding = 1) readonly buffer inputCoarseData { diff --git a/source/blender/draw/intern/shaders/common_subdiv_vbo_lnor_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_vbo_lnor_comp.glsl index 41a8df3cf82..b7e04e240fb 100644 --- a/source/blender/draw/intern/shaders/common_subdiv_vbo_lnor_comp.glsl +++ b/source/blender/draw/intern/shaders/common_subdiv_vbo_lnor_comp.glsl @@ -1,5 +1,5 @@ -/* To be compile with common_subdiv_lib.glsl */ +/* To be compiled with common_subdiv_lib.glsl */ layout(std430, binding = 1) readonly buffer inputVertexData { @@ -38,13 +38,18 @@ void main() } } else { - /* Face is flat shaded, compute flat face normal from an inscribed triangle. */ - vec3 verts[3]; - for (int i = 0; i < 3; i++) { - verts[i] = get_vertex_pos(pos_nor[start_loop_index + i]); - } - - vec3 face_normal = normalize(cross(verts[1] - verts[0], verts[2] - verts[0])); + vec3 v0 = get_vertex_pos(pos_nor[start_loop_index + 0]); + vec3 v1 = get_vertex_pos(pos_nor[start_loop_index + 1]); + vec3 v2 = get_vertex_pos(pos_nor[start_loop_index + 2]); + vec3 v3 = get_vertex_pos(pos_nor[start_loop_index + 3]); + + vec3 face_normal = vec3(0.0); + add_newell_cross_v3_v3v3(face_normal, v0, v1); + add_newell_cross_v3_v3v3(face_normal, v1, v2); + add_newell_cross_v3_v3v3(face_normal, v2, v3); + add_newell_cross_v3_v3v3(face_normal, v3, v0); + + face_normal = normalize(face_normal); for (int i = 0; i < 4; i++) { output_lnor[start_loop_index + i] = face_normal; } diff --git a/source/blender/draw/intern/shaders/common_subdiv_vbo_sculpt_data_comp.glsl b/source/blender/draw/intern/shaders/common_subdiv_vbo_sculpt_data_comp.glsl index 7182ce57ad3..77b599f6252 100644 --- a/source/blender/draw/intern/shaders/common_subdiv_vbo_sculpt_data_comp.glsl +++ b/source/blender/draw/intern/shaders/common_subdiv_vbo_sculpt_data_comp.glsl @@ -1,5 +1,5 @@ -/* To be compile with common_subdiv_lib.glsl */ +/* To be compiled with common_subdiv_lib.glsl */ struct SculptData { uint face_set_color; diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index 4780352e5dc..edb6d188ab8 100644 --- a/source/blender/editors/animation/anim_channels_defines.c +++ b/source/blender/editors/animation/anim_channels_defines.c @@ -668,7 +668,7 @@ static int acf_object_icon(bAnimListElem *ale) return ICON_OUTLINER_OB_MESH; case OB_CAMERA: return ICON_OUTLINER_OB_CAMERA; - case OB_CURVE: + case OB_CURVES_LEGACY: return ICON_OUTLINER_OB_CURVE; case OB_MBALL: return ICON_OUTLINER_OB_META; @@ -4601,7 +4601,7 @@ void ANIM_channel_draw( /* Draw slider: * - Even if we can draw sliders for this view, * we must also check that the channel-type supports them - * (only only F-Curves really can support them for now). + * (only F-Curves really can support them for now). * - Slider should start before the toggles (if they're visible) * to keep a clean line down the side. */ @@ -5336,7 +5336,7 @@ void ANIM_channel_draw_widgets(const bContext *C, /* Draw slider: * - Even if we can draw sliders for this view, we must also check that the channel-type - * supports them (only only F-Curves really can support them for now). + * supports them (only F-Curves really can support them for now). * - To make things easier, we use RNA-autobuts for this so that changes are * reflected immediately, wherever they occurred. * BUT, we don't use the layout engine, otherwise we'd get wrong alignment, diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index d6163f21c79..0389e57627a 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -2530,9 +2530,9 @@ static size_t animdata_filter_ds_obdata( expanded = FILTER_LAM_OBJD(la); break; } - case OB_CURVE: /* ------- Curve ---------- */ - case OB_SURF: /* ------- Nurbs Surface ---------- */ - case OB_FONT: /* ------- Text Curve ---------- */ + case OB_CURVES_LEGACY: /* ------- Curve ---------- */ + case OB_SURF: /* ------- Nurbs Surface ---------- */ + case OB_FONT: /* ------- Text Curve ---------- */ { Curve *cu = (Curve *)ob->data; diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c index 9566402ad85..95125516fe8 100644 --- a/source/blender/editors/animation/anim_markers.c +++ b/source/blender/editors/animation/anim_markers.c @@ -700,7 +700,7 @@ static void MARKER_OT_add(wmOperatorType *ot) typedef struct MarkerMove { SpaceLink *slink; ListBase *markers; - int event_type; /* store invoke-event, to verify */ + short event_type, event_val; /* store invoke-event, to verify */ int *oldframe, evtx, firstx; NumInput num; } MarkerMove; @@ -844,6 +844,7 @@ static int ed_marker_move_invoke(bContext *C, wmOperator *op, const wmEvent *eve mm->evtx = event->xy[0]; mm->firstx = event->xy[0]; mm->event_type = event->type; + mm->event_val = event->val; /* add temp handler */ WM_event_add_modal_handler(C, op); @@ -941,7 +942,7 @@ static int ed_marker_move_modal(bContext *C, wmOperator *op, const wmEvent *even case EVT_PADENTER: case LEFTMOUSE: case MIDDLEMOUSE: - if (WM_event_is_modal_tweak_exit(event, mm->event_type)) { + if (WM_event_is_modal_drag_exit(event, mm->event_type, mm->event_val)) { ed_marker_move_exit(C, op); WM_event_add_notifier(C, NC_SCENE | ND_MARKERS, NULL); WM_event_add_notifier(C, NC_ANIMATION | ND_MARKERS, NULL); @@ -960,7 +961,13 @@ static int ed_marker_move_modal(bContext *C, wmOperator *op, const wmEvent *even mm->evtx = event->xy[0]; fac = ((float)(event->xy[0] - mm->firstx) * dx); - apply_keyb_grid(event->shift, event->ctrl, &fac, 0.0, FPS, 0.1 * FPS, 0); + apply_keyb_grid((event->modifier & KM_SHIFT) != 0, + (event->modifier & KM_CTRL) != 0, + &fac, + 0.0, + FPS, + 0.1 * FPS, + 0); RNA_int_set(op->ptr, "frames", (int)fac); ed_marker_move_apply(C, op); diff --git a/source/blender/editors/animation/anim_motion_paths.c b/source/blender/editors/animation/anim_motion_paths.c index 539227933cf..2c99cd1cc1f 100644 --- a/source/blender/editors/animation/anim_motion_paths.c +++ b/source/blender/editors/animation/anim_motion_paths.c @@ -340,6 +340,55 @@ static void motionpath_free_free_tree_data(ListBase *targets) } } +void animviz_motionpath_compute_range(Object *ob, Scene *scene) +{ + struct AnimKeylist *keylist = ED_keylist_create(); + bAnimVizSettings *avs; + if (ob->mode == OB_MODE_POSE) { + avs = &ob->pose->avs; + bArmature *arm = ob->data; + + if (!ELEM(NULL, ob->adt, ob->adt->action, arm->adt)) { + /* Loop through all the fcurves and get only the keylists for the bone location fcurves */ + LISTBASE_FOREACH (FCurve *, fcu, &ob->adt->action->curves) { + if (strstr(fcu->rna_path, "pose.bones[") && strstr(fcu->rna_path, "location")) { + fcurve_to_keylist(arm->adt, fcu, keylist, 0); + } + } + } + } + else { + avs = &ob->avs; + + if (!ELEM(NULL, ob->adt, ob->adt->action)) { + /* Loop through all the fcurves and get only the keylists for the location fcurves */ + LISTBASE_FOREACH (FCurve *, fcu, &ob->adt->action->curves) { + if (strcmp(fcu->rna_path, "location") == 0) { + fcurve_to_keylist(ob->adt, fcu, keylist, 0); + } + } + } + } + + if (ED_keylist_is_empty(keylist) || (avs->path_range == MOTIONPATH_RANGE_SCENE)) { + /* Apply scene frame range if no keys where found or if scene range is selected */ + avs->path_sf = PSFRA; + avs->path_ef = PEFRA; + } + else { + /* Compute keys range */ + Range2f frame_range; + const bool only_selected = avs->path_range == MOTIONPATH_RANGE_KEYS_SELECTED; + /* Get range for all keys if selected_only is false or if no keys are selected */ + if (!(only_selected && ED_keylist_selected_keys_frame_range(keylist, &frame_range))) { + ED_keylist_all_keys_frame_range(keylist, &frame_range); + } + avs->path_sf = frame_range.min; + avs->path_ef = frame_range.max; + } + ED_keylist_free(keylist); +} + void animviz_calc_motionpaths(Depsgraph *depsgraph, Main *bmain, Scene *scene, diff --git a/source/blender/editors/animation/keyframes_keylist.cc b/source/blender/editors/animation/keyframes_keylist.cc index 3f9592fb4ae..0b795fea278 100644 --- a/source/blender/editors/animation/keyframes_keylist.cc +++ b/source/blender/editors/animation/keyframes_keylist.cc @@ -304,7 +304,21 @@ const struct ListBase *ED_keylist_listbase(const AnimKeylist *keylist) return &keylist->key_columns; } -bool ED_keylist_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_range) +static void keylist_first_last(const struct AnimKeylist *keylist, + const struct ActKeyColumn **first_column, + const struct ActKeyColumn **last_column) +{ + if (keylist->is_runtime_initialized) { + *first_column = &keylist->runtime.key_columns[0]; + *last_column = &keylist->runtime.key_columns[keylist->column_len - 1]; + } + else { + *first_column = static_cast<const ActKeyColumn *>(keylist->key_columns.first); + *last_column = static_cast<const ActKeyColumn *>(keylist->key_columns.last); + } +} + +bool ED_keylist_all_keys_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_range) { BLI_assert(r_frame_range); @@ -314,13 +328,33 @@ bool ED_keylist_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_ const ActKeyColumn *first_column; const ActKeyColumn *last_column; - if (keylist->is_runtime_initialized) { - first_column = &keylist->runtime.key_columns[0]; - last_column = &keylist->runtime.key_columns[keylist->column_len - 1]; + keylist_first_last(keylist, &first_column, &last_column); + r_frame_range->min = first_column->cfra; + r_frame_range->max = last_column->cfra; + + return true; +} + +bool ED_keylist_selected_keys_frame_range(const struct AnimKeylist *keylist, + Range2f *r_frame_range) +{ + BLI_assert(r_frame_range); + + if (ED_keylist_is_empty(keylist)) { + return false; } - else { - first_column = static_cast<const ActKeyColumn *>(keylist->key_columns.first); - last_column = static_cast<const ActKeyColumn *>(keylist->key_columns.last); + + const ActKeyColumn *first_column; + const ActKeyColumn *last_column; + keylist_first_last(keylist, &first_column, &last_column); + while (first_column && !(first_column->sel & SELECT)) { + first_column = first_column->next; + } + while (last_column && !(last_column->sel & SELECT)) { + last_column = last_column->prev; + } + if (!first_column || !last_column || first_column == last_column) { + return false; } r_frame_range->min = first_column->cfra; r_frame_range->max = last_column->cfra; diff --git a/source/blender/editors/armature/pose_edit.c b/source/blender/editors/armature/pose_edit.c index e227e69f9e1..128126e515e 100644 --- a/source/blender/editors/armature/pose_edit.c +++ b/source/blender/editors/armature/pose_edit.c @@ -220,8 +220,8 @@ static int pose_calculate_paths_invoke(bContext *C, wmOperator *op, const wmEven bAnimVizSettings *avs = &ob->pose->avs; PointerRNA avs_ptr; - RNA_int_set(op->ptr, "start_frame", avs->path_sf); - RNA_int_set(op->ptr, "end_frame", avs->path_ef); + RNA_enum_set(op->ptr, "display_type", avs->path_type); + RNA_enum_set(op->ptr, "range", avs->path_range); RNA_pointer_create(NULL, &RNA_AnimVizMotionPaths, avs, &avs_ptr); RNA_enum_set(op->ptr, "bake_location", RNA_enum_get(&avs_ptr, "bake_location")); @@ -229,7 +229,7 @@ static int pose_calculate_paths_invoke(bContext *C, wmOperator *op, const wmEven /* show popup dialog to allow editing of range... */ /* FIXME: hard-coded dimensions here are just arbitrary. */ - return WM_operator_props_dialog_popup(C, op, 200); + return WM_operator_props_dialog_popup(C, op, 270); } /* For the object with pose/action: create path curves for selected bones @@ -249,8 +249,9 @@ static int pose_calculate_paths_exec(bContext *C, wmOperator *op) bAnimVizSettings *avs = &ob->pose->avs; PointerRNA avs_ptr; - avs->path_sf = RNA_int_get(op->ptr, "start_frame"); - avs->path_ef = RNA_int_get(op->ptr, "end_frame"); + avs->path_type = RNA_enum_get(op->ptr, "display_type"); + avs->path_range = RNA_enum_get(op->ptr, "range"); + animviz_motionpath_compute_range(ob, scene); RNA_pointer_create(NULL, &RNA_AnimVizMotionPaths, avs, &avs_ptr); RNA_enum_set(&avs_ptr, "bake_location", RNA_enum_get(op->ptr, "bake_location")); @@ -258,7 +259,6 @@ static int pose_calculate_paths_exec(bContext *C, wmOperator *op) /* set up path data for bones being calculated */ CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones_from_active_object) { - /* verify makes sure that the selected bone has a bone with the appropriate settings */ animviz_verify_motionpaths(op->reports, scene, ob, pchan); } CTX_DATA_END; @@ -281,6 +281,19 @@ static int pose_calculate_paths_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static bool pose_calculate_paths_poll(bContext *C) +{ + if (!ED_operator_posemode_exclusive(C)) { + return false; + } + Object *ob = CTX_data_active_object(C); + bArmature *arm = ob->data; + if (ELEM(NULL, ob, arm, ob->pose)) { + return false; + } + return true; +} + void POSE_OT_paths_calculate(wmOperatorType *ot) { /* identifiers */ @@ -291,30 +304,24 @@ void POSE_OT_paths_calculate(wmOperatorType *ot) /* api callbacks */ ot->invoke = pose_calculate_paths_invoke; ot->exec = pose_calculate_paths_exec; - ot->poll = ED_operator_posemode_exclusive; + ot->poll = pose_calculate_paths_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - RNA_def_int(ot->srna, - "start_frame", - 1, - MINAFRAME, - MAXFRAME, - "Start", - "First frame to calculate bone paths on", - MINFRAME, - MAXFRAME / 2.0); - RNA_def_int(ot->srna, - "end_frame", - 250, - MINAFRAME, - MAXFRAME, - "End", - "Last frame to calculate bone paths on", - MINFRAME, - MAXFRAME / 2.0); + RNA_def_enum(ot->srna, + "display_type", + rna_enum_motionpath_display_type_items, + MOTIONPATH_TYPE_RANGE, + "Display type", + ""); + RNA_def_enum(ot->srna, + "range", + rna_enum_motionpath_range_items, + MOTIONPATH_RANGE_SCENE, + "Computation Range", + ""); RNA_def_enum(ot->srna, "bake_location", @@ -336,7 +343,7 @@ static bool pose_update_paths_poll(bContext *C) return false; } -static int pose_update_paths_exec(bContext *C, wmOperator *UNUSED(op)) +static int pose_update_paths_exec(bContext *C, wmOperator *op) { Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); Scene *scene = CTX_data_scene(C); @@ -344,6 +351,13 @@ static int pose_update_paths_exec(bContext *C, wmOperator *UNUSED(op)) if (ELEM(NULL, ob, scene)) { return OPERATOR_CANCELLED; } + animviz_motionpath_compute_range(ob, scene); + + /* set up path data for bones being calculated */ + CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones_from_active_object) { + animviz_verify_motionpaths(op->reports, scene, ob, pchan); + } + CTX_DATA_END; /* Calculate the bones that now have motion-paths. */ /* TODO: only make for the selected bones? */ @@ -427,7 +441,7 @@ static int pose_clear_paths_exec(bContext *C, wmOperator *op) /* operator callback/wrapper */ static int pose_clear_paths_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - if ((event->shift) && !RNA_struct_property_is_set(op->ptr, "only_selected")) { + if ((event->modifier & KM_SHIFT) && !RNA_struct_property_is_set(op->ptr, "only_selected")) { RNA_boolean_set(op->ptr, "only_selected", true); } return pose_clear_paths_exec(C, op); diff --git a/source/blender/editors/asset/intern/asset_library_reference.cc b/source/blender/editors/asset/intern/asset_library_reference.cc index 04f77821114..5096b9d653d 100644 --- a/source/blender/editors/asset/intern/asset_library_reference.cc +++ b/source/blender/editors/asset/intern/asset_library_reference.cc @@ -17,9 +17,9 @@ AssetLibraryReferenceWrapper::AssetLibraryReferenceWrapper(const AssetLibraryRef bool operator==(const AssetLibraryReferenceWrapper &a, const AssetLibraryReferenceWrapper &b) { - return (a.type == b.type) && (a.type == ASSET_LIBRARY_CUSTOM) ? - (a.custom_library_index == b.custom_library_index) : - true; + return (a.type == b.type) && + ((a.type == ASSET_LIBRARY_CUSTOM) ? (a.custom_library_index == b.custom_library_index) : + true); } uint64_t AssetLibraryReferenceWrapper::hash() const diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 20a2251b6e1..a33fbb29f85 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -72,7 +72,7 @@ static bool curve_delete_vertices(Object *obedit, View3D *v3d); ListBase *object_editcurve_get(Object *ob) { - if (ob && ELEM(ob->type, OB_CURVE, OB_SURF)) { + if (ob && ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { Curve *cu = ob->data; return &cu->editnurb->nurbs; } @@ -1238,7 +1238,7 @@ void ED_curve_editnurb_load(Main *bmain, Object *obedit) return; } - if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { + if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) { Curve *cu = obedit->data; ListBase newnurb = {NULL, NULL}, oldnurb = cu->nurb; @@ -1273,7 +1273,7 @@ void ED_curve_editnurb_make(Object *obedit) EditNurb *editnurb = cu->editnurb; KeyBlock *actkey; - if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { + if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) { actkey = BKE_keyblock_from_object(obedit); if (actkey) { @@ -5637,7 +5637,7 @@ static int curve_extrude_exec(bContext *C, wmOperator *UNUSED(op)) } /* First test: curve? */ - if (obedit->type != OB_CURVE) { + if (obedit->type != OB_CURVES_LEGACY) { LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) { if ((nu->pntsv == 1) && (ED_curve_nurb_select_count(v3d, nu) < nu->pntsu)) { as_curve = true; @@ -5646,7 +5646,7 @@ static int curve_extrude_exec(bContext *C, wmOperator *UNUSED(op)) } } - if (obedit->type == OB_CURVE || as_curve) { + if (obedit->type == OB_CURVES_LEGACY || as_curve) { changed = ed_editcurve_extrude(cu, editnurb, v3d); } else { @@ -6715,7 +6715,7 @@ static int shade_smooth_exec(bContext *C, wmOperator *op) Object *obedit = objects[ob_index]; ListBase *editnurb = object_editcurve_get(obedit); - if (obedit->type != OB_CURVE) { + if (obedit->type != OB_CURVES_LEGACY) { continue; } @@ -6874,7 +6874,7 @@ int ED_curve_join_objects_exec(bContext *C, wmOperator *op) cu = ob_active->data; BLI_movelisttolist(&cu->nurb, &tempbase); - if (ob_active->type == OB_CURVE && CU_IS_2D(cu)) { + if (ob_active->type == OB_CURVES_LEGACY && CU_IS_2D(cu)) { /* Account for mixed 2D/3D curves when joining */ BKE_curve_dimension_update(cu); } @@ -6984,7 +6984,7 @@ static bool match_texture_space_poll(bContext *C) { Object *object = CTX_data_active_object(C); - return object && ELEM(object->type, OB_CURVE, OB_SURF, OB_FONT); + return object && ELEM(object->type, OB_CURVES_LEGACY, OB_SURF, OB_FONT); } static int match_texture_space_exec(bContext *C, wmOperator *UNUSED(op)) diff --git a/source/blender/editors/curve/editcurve_add.c b/source/blender/editors/curve/editcurve_add.c index 2aaebf494a6..d7201495f75 100644 --- a/source/blender/editors/curve/editcurve_add.c +++ b/source/blender/editors/curve/editcurve_add.c @@ -53,25 +53,25 @@ static const char *get_curve_defname(int type) if ((type & CU_TYPE) == CU_BEZIER) { switch (stype) { case CU_PRIM_CURVE: - return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "BezierCurve"); + return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "BezierCurve"); case CU_PRIM_CIRCLE: - return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "BezierCircle"); + return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "BezierCircle"); case CU_PRIM_PATH: - return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "CurvePath"); + return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "CurvePath"); default: - return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "Curve"); + return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "Curve"); } } else { switch (stype) { case CU_PRIM_CURVE: - return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "NurbsCurve"); + return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "NurbsCurve"); case CU_PRIM_CIRCLE: - return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "NurbsCircle"); + return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "NurbsCircle"); case CU_PRIM_PATH: - return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "NurbsPath"); + return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "NurbsPath"); default: - return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "Curve"); + return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "Curve"); } } } @@ -82,17 +82,17 @@ static const char *get_surf_defname(int type) switch (stype) { case CU_PRIM_CURVE: - return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "SurfCurve"); + return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfCurve"); case CU_PRIM_CIRCLE: - return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "SurfCircle"); + return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfCircle"); case CU_PRIM_PATCH: - return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "SurfPatch"); + return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfPatch"); case CU_PRIM_SPHERE: - return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "SurfSphere"); + return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfSphere"); case CU_PRIM_DONUT: - return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "SurfTorus"); + return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfTorus"); default: - return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE, "Surface"); + return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "Surface"); } } @@ -510,11 +510,11 @@ static int curvesurf_prim_add(bContext *C, wmOperator *op, int type, int isSurf) } if (!isSurf) { /* adding curve */ - if (obedit == NULL || obedit->type != OB_CURVE) { + if (obedit == NULL || obedit->type != OB_CURVES_LEGACY) { const char *name = get_curve_defname(type); Curve *cu; - obedit = ED_object_add_type(C, OB_CURVE, name, loc, rot, true, local_view_bits); + obedit = ED_object_add_type(C, OB_CURVES_LEGACY, name, loc, rot, true, local_view_bits); newob = true; cu = (Curve *)obedit->data; diff --git a/source/blender/editors/curve/editcurve_undo.c b/source/blender/editors/curve/editcurve_undo.c index 7b68c859b43..888bb2169e0 100644 --- a/source/blender/editors/curve/editcurve_undo.c +++ b/source/blender/editors/curve/editcurve_undo.c @@ -162,7 +162,7 @@ static Object *editcurve_object_from_context(bContext *C) { ViewLayer *view_layer = CTX_data_view_layer(C); Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer); - if (obedit && ELEM(obedit->type, OB_CURVE, OB_SURF)) { + if (obedit && ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) { Curve *cu = obedit->data; if (BKE_curve_editNurbs_get(cu) != NULL) { return obedit; diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c index b2af0643e2c..02c7f3856e8 100644 --- a/source/blender/editors/curve/editfont.c +++ b/source/blender/editors/curve/editfont.c @@ -1640,7 +1640,9 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event) EditFont *ef = cu->editfont; static int accentcode = 0; uintptr_t ascii = event->ascii; - int alt = event->alt, shift = event->shift, ctrl = event->ctrl; + const bool alt = event->modifier & KM_ALT; + const bool shift = event->modifier & KM_SHIFT; + const bool ctrl = event->modifier & KM_CTRL; int event_type = event->type, event_val = event->val; char32_t inserted_text[2] = {0}; diff --git a/source/blender/editors/curves/CMakeLists.txt b/source/blender/editors/curves/CMakeLists.txt index d2b7dacbc20..1731d224b3e 100644 --- a/source/blender/editors/curves/CMakeLists.txt +++ b/source/blender/editors/curves/CMakeLists.txt @@ -6,6 +6,7 @@ set(INC ../../blenlib ../../blentranslation ../../depsgraph + ../../functions ../../makesdna ../../makesrna ../../windowmanager @@ -13,6 +14,7 @@ set(INC ) set(SRC + intern/curves_add.cc intern/curves_ops.cc ) diff --git a/source/blender/editors/curves/intern/curves_add.cc b/source/blender/editors/curves/intern/curves_add.cc new file mode 100644 index 00000000000..9cde23451dc --- /dev/null +++ b/source/blender/editors/curves/intern/curves_add.cc @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup edcurves + */ + +#include "BLI_rand.hh" + +#include "BKE_curves.hh" + +#include "ED_curves.h" + +namespace blender::ed::curves { + +bke::CurvesGeometry primitive_random_sphere(const int curves_size, const int points_per_curve) +{ + bke::CurvesGeometry curves(points_per_curve * curves_size, curves_size); + + MutableSpan<int> offsets = curves.offsets(); + MutableSpan<float3> positions = curves.positions(); + + float *radius_data = (float *)CustomData_add_layer_named( + &curves.point_data, CD_PROP_FLOAT, CD_DEFAULT, nullptr, curves.point_size, "radius"); + MutableSpan<float> radii{radius_data, curves.points_size()}; + + for (const int i : offsets.index_range()) { + offsets[i] = points_per_curve * i; + } + + RandomNumberGenerator rng; + + for (const int i : curves.curves_range()) { + const IndexRange curve_range = curves.range_for_curve(i); + MutableSpan<float3> curve_positions = positions.slice(curve_range); + MutableSpan<float> curve_radii = radii.slice(curve_range); + + const float theta = 2.0f * M_PI * rng.get_float(); + const float phi = saacosf(2.0f * rng.get_float() - 1.0f); + + float3 no = {std::sin(theta) * std::sin(phi), std::cos(theta) * std::sin(phi), std::cos(phi)}; + no = math::normalize(no); + + float3 co = no; + for (int key = 0; key < points_per_curve; key++) { + float t = key / (float)(points_per_curve - 1); + curve_positions[key] = co; + curve_radii[key] = 0.02f * (1.0f - t); + + float3 offset = float3(rng.get_float(), rng.get_float(), rng.get_float()) * 2.0f - 1.0f; + co += (offset + no) / points_per_curve; + } + } + + return curves; +} + +} // namespace blender::ed::curves diff --git a/source/blender/editors/curves/intern/curves_ops.cc b/source/blender/editors/curves/intern/curves_ops.cc index fdda8e636f7..52a55ae1760 100644 --- a/source/blender/editors/curves/intern/curves_ops.cc +++ b/source/blender/editors/curves/intern/curves_ops.cc @@ -4,67 +4,8 @@ * \ingroup edcurves */ -#include "BLI_utildefines.h" - #include "ED_curves.h" -#include "ED_object.h" - -#include "WM_api.h" -#include "WM_types.h" - -#include "BKE_context.h" - -#include "RNA_access.h" -#include "RNA_define.h" -#include "RNA_types.h" - -static bool curves_sculptmode_toggle_poll(bContext *C) -{ - Object *ob = CTX_data_active_object(C); - if (ob == nullptr) { - return false; - } - if (ob->type != OB_CURVES) { - return false; - } - return true; -} - -static int curves_sculptmode_toggle_exec(bContext *C, wmOperator *op) -{ - Object *ob = CTX_data_active_object(C); - const bool is_mode_set = ob->mode == OB_MODE_SCULPT_CURVES; - - if (is_mode_set) { - if (!ED_object_mode_compat_set(C, ob, OB_MODE_SCULPT_CURVES, op->reports)) { - return OPERATOR_CANCELLED; - } - } - - if (is_mode_set) { - ob->mode = OB_MODE_OBJECT; - } - else { - ob->mode = OB_MODE_SCULPT_CURVES; - } - - WM_event_add_notifier(C, NC_SCENE | ND_MODE, nullptr); - return OPERATOR_CANCELLED; -} - -static void CURVES_OT_sculptmode_toggle(wmOperatorType *ot) -{ - ot->name = "Curve Sculpt Mode Toggle"; - ot->idname = "CURVES_OT_sculptmode_toggle"; - ot->description = "Enter/Exit sculpt mode for curves"; - - ot->exec = curves_sculptmode_toggle_exec; - ot->poll = curves_sculptmode_toggle_poll; - - ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; -} void ED_operatortypes_curves() { - WM_operatortype_append(CURVES_OT_sculptmode_toggle); } diff --git a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c index aea6d41202e..447fe1005a1 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c @@ -147,7 +147,7 @@ static void move3d_get_translate(const wmGizmo *gz, float co_delta[3]) { MoveInteraction *inter = gz->interaction_data; - const float mval_delta[2] = { + const float xy_delta[2] = { event->mval[0] - inter->init.mval[0], event->mval[1] - inter->init.mval[1], }; @@ -155,9 +155,9 @@ static void move3d_get_translate(const wmGizmo *gz, RegionView3D *rv3d = region->regiondata; float co_ref[3]; mul_v3_mat3_m4v3(co_ref, gz->matrix_space, inter->init.prop_co); - const float zfac = ED_view3d_calc_zfac(rv3d, co_ref, NULL); + const float zfac = ED_view3d_calc_zfac(rv3d, co_ref); - ED_view3d_win_to_delta(region, mval_delta, co_delta, zfac); + ED_view3d_win_to_delta(region, xy_delta, zfac, co_delta); float matrix_space_inv[3][3]; copy_m3_m4(matrix_space_inv, gz->matrix_space); diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index 163b5657326..5ab4a663efe 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -326,8 +326,7 @@ static void annotation_stroke_convertcoords(tGPsdata *p, } else { float mval_prj[2]; - float rvec[3], dvec[3]; - float zfac; + float rvec[3]; /* Current method just converts each point in screen-coordinates to * 3D-coordinates using the 3D-cursor as reference. In general, this @@ -339,13 +338,14 @@ static void annotation_stroke_convertcoords(tGPsdata *p, */ annotation_get_3d_reference(p, rvec); - zfac = ED_view3d_calc_zfac(p->region->regiondata, rvec, NULL); + const float zfac = ED_view3d_calc_zfac(p->region->regiondata, rvec); if (ED_view3d_project_float_global(p->region, rvec, mval_prj, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { - float mval_f[2]; - sub_v2_v2v2(mval_f, mval_prj, mval); - ED_view3d_win_to_delta(p->region, mval_f, dvec, zfac); + float dvec[3]; + float xy_delta[2]; + sub_v2_v2v2(xy_delta, mval_prj, mval); + ED_view3d_win_to_delta(p->region, xy_delta, zfac, dvec); sub_v3_v3v3(out, rvec, dvec); } else { @@ -2060,7 +2060,7 @@ static void annotation_draw_apply_event( p->mval[1] = (float)event->mval[1] - y; /* Key to toggle stabilization. */ - if (event->shift && p->paintmode == GP_PAINTMODE_DRAW) { + if ((event->modifier & KM_SHIFT) && (p->paintmode == GP_PAINTMODE_DRAW)) { /* Using permanent stabilization, shift will deactivate the flag. */ if (p->flags & GP_PAINTFLAG_USE_STABILIZER) { if (p->flags & GP_PAINTFLAG_USE_STABILIZER_TEMP) { @@ -2075,7 +2075,7 @@ static void annotation_draw_apply_event( } } /* verify key status for straight lines */ - else if (event->ctrl || event->alt) { + else if (event->modifier & (KM_CTRL | KM_ALT)) { if (p->straight[0] == 0) { int dx = abs((int)(p->mval[0] - p->mvalo[0])); int dy = abs((int)(p->mval[1] - p->mvalo[1])); @@ -2299,7 +2299,7 @@ static int annotation_draw_invoke(bContext *C, wmOperator *op, const wmEvent *ev p->flags |= GP_PAINTFLAG_USE_STABILIZER | GP_PAINTFLAG_USE_STABILIZER_TEMP; annotation_draw_toggle_stabilizer_cursor(p, true); } - else if (event->shift) { + else if (event->modifier & KM_SHIFT) { p->flags |= GP_PAINTFLAG_USE_STABILIZER_TEMP; annotation_draw_toggle_stabilizer_cursor(p, true); } diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c index 63239fd6341..d4518f21586 100644 --- a/source/blender/editors/gpencil/gpencil_convert.c +++ b/source/blender/editors/gpencil/gpencil_convert.c @@ -1300,8 +1300,8 @@ static void gpencil_layer_to_curve(bContext *C, /* init the curve object (remove rotation and get curve data from it) * - must clear transforms set on object, as those skew our results */ - ob = BKE_object_add_only_object(bmain, OB_CURVE, gpl->info); - cu = ob->data = BKE_curve_add(bmain, gpl->info, OB_CURVE); + ob = BKE_object_add_only_object(bmain, OB_CURVES_LEGACY, gpl->info); + cu = ob->data = BKE_curve_add(bmain, gpl->info, OB_CURVES_LEGACY); BKE_collection_object_add(bmain, collection, ob); base_new = BKE_view_layer_base_find(view_layer, ob); DEG_relations_tag_update(bmain); /* added object */ diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 680b313c47b..d734fb2678e 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -4356,6 +4356,7 @@ static int gpencil_stroke_sample_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); const float length = RNA_float_get(op->ptr, "length"); + const float sharp_threshold = RNA_float_get(op->ptr, "sharp_threshold"); /* sanity checks */ if (ELEM(NULL, gpd)) { @@ -4365,7 +4366,7 @@ static int gpencil_stroke_sample_exec(bContext *C, wmOperator *op) /* Go through each editable + selected stroke */ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) { if (gps->flag & GP_STROKE_SELECT) { - BKE_gpencil_stroke_sample(gpd, gps, length, true); + BKE_gpencil_stroke_sample(gpd, gps, length, true, sharp_threshold); } } GP_EDITABLE_STROKES_END(gpstroke_iter); diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index 8be34a35ca9..069493025dc 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -1097,7 +1097,7 @@ static void gpencil_erase_processed_area(tGPDfill *tgpf) /** * Naive dilate * - * Expand green areas into enclosing red areas. + * Expand green areas into enclosing red or transparent areas. * Using stack prevents creep when replacing colors directly. * <pre> * ----------- @@ -1110,8 +1110,8 @@ static void gpencil_erase_processed_area(tGPDfill *tgpf) */ static bool dilate_shape(ImBuf *ibuf) { -#define IS_RED (color[0] == 1.0f) #define IS_GREEN (color[1] == 1.0f) +#define IS_NOT_GREEN (color[1] != 1.0f) bool done = false; @@ -1140,7 +1140,7 @@ static bool dilate_shape(ImBuf *ibuf) if (v - 1 >= 0) { index = v - 1; get_pixel(ibuf, index, color); - if (IS_RED) { + if (IS_NOT_GREEN) { BLI_stack_push(stack, &index); lt = index; } @@ -1149,7 +1149,7 @@ static bool dilate_shape(ImBuf *ibuf) if (v + 1 <= maxpixel) { index = v + 1; get_pixel(ibuf, index, color); - if (IS_RED) { + if (IS_NOT_GREEN) { BLI_stack_push(stack, &index); rt = index; } @@ -1158,7 +1158,7 @@ static bool dilate_shape(ImBuf *ibuf) if (v + ibuf->x <= max_size) { index = v + ibuf->x; get_pixel(ibuf, index, color); - if (IS_RED) { + if (IS_NOT_GREEN) { BLI_stack_push(stack, &index); tp = index; } @@ -1167,7 +1167,7 @@ static bool dilate_shape(ImBuf *ibuf) if (v - ibuf->x >= 0) { index = v - ibuf->x; get_pixel(ibuf, index, color); - if (IS_RED) { + if (IS_NOT_GREEN) { BLI_stack_push(stack, &index); bm = index; } @@ -1176,7 +1176,7 @@ static bool dilate_shape(ImBuf *ibuf) if (tp && lt) { index = tp - 1; get_pixel(ibuf, index, color); - if (IS_RED) { + if (IS_NOT_GREEN) { BLI_stack_push(stack, &index); } } @@ -1184,7 +1184,7 @@ static bool dilate_shape(ImBuf *ibuf) if (tp && rt) { index = tp + 1; get_pixel(ibuf, index, color); - if (IS_RED) { + if (IS_NOT_GREEN) { BLI_stack_push(stack, &index); } } @@ -1192,7 +1192,7 @@ static bool dilate_shape(ImBuf *ibuf) if (bm && lt) { index = bm - 1; get_pixel(ibuf, index, color); - if (IS_RED) { + if (IS_NOT_GREEN) { BLI_stack_push(stack, &index); } } @@ -1200,7 +1200,7 @@ static bool dilate_shape(ImBuf *ibuf) if (bm && rt) { index = bm + 1; get_pixel(ibuf, index, color); - if (IS_RED) { + if (IS_NOT_GREEN) { BLI_stack_push(stack, &index); } } @@ -1218,8 +1218,8 @@ static bool dilate_shape(ImBuf *ibuf) return done; -#undef IS_RED #undef IS_GREEN +#undef IS_NOT_GREEN } /** @@ -1239,7 +1239,7 @@ static bool contract_shape(ImBuf *ibuf) const float clear[4] = {0.0f, 0.0f, 0.0f, 0.0f}; const int max_size = (ibuf->x * ibuf->y) - 1; - /* Detect if pixel is near of no green pixels and mark green to be cleared. */ + /* Detect if pixel is near of no green pixels and mark green pixel to be cleared. */ for (int row = 0; row < ibuf->y; row++) { if (!is_row_filled(ibuf, row)) { continue; @@ -2172,7 +2172,8 @@ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event) tgpf->on_back = RNA_boolean_get(op->ptr, "on_back"); const bool is_brush_inv = brush_settings->fill_direction == BRUSH_DIR_IN; - const bool is_inverted = (is_brush_inv && !event->ctrl) || (!is_brush_inv && event->ctrl); + const bool is_inverted = (is_brush_inv && (event->modifier & KM_CTRL) == 0) || + (!is_brush_inv && (event->modifier & KM_CTRL) != 0); const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(tgpf->gpd); const bool do_extend = (tgpf->fill_extend_fac > 0.0f); const bool help_lines = ((tgpf->flag & GP_BRUSH_FILL_SHOW_HELPLINES) || @@ -2313,7 +2314,7 @@ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event) case EVT_PAGEUPKEY: case WHEELUPMOUSE: if (tgpf->oldkey == 1) { - tgpf->fill_extend_fac -= (event->shift) ? 0.01f : 0.1f; + tgpf->fill_extend_fac -= (event->modifier & KM_SHIFT) ? 0.01f : 0.1f; CLAMP_MIN(tgpf->fill_extend_fac, 0.0f); gpencil_update_extend(tgpf); } @@ -2321,7 +2322,7 @@ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event) case EVT_PAGEDOWNKEY: case WHEELDOWNMOUSE: if (tgpf->oldkey == 1) { - tgpf->fill_extend_fac += (event->shift) ? 0.01f : 0.1f; + tgpf->fill_extend_fac += (event->modifier & KM_SHIFT) ? 0.01f : 0.1f; CLAMP_MAX(tgpf->fill_extend_fac, 100.0f); gpencil_update_extend(tgpf); } diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index a6691a12505..5409cea2a2a 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -235,7 +235,7 @@ typedef struct tGPsdata { /** key used for invoking the operator */ short keymodifier; /** shift modifier flag */ - short shift; + bool shift; /** size in pixels for uv calculation */ float totpixlen; /** Special mode for fill brush. */ @@ -447,21 +447,21 @@ static void gpencil_stroke_convertcoords(tGPsdata *p, } float mval_prj[2]; - float rvec[3], dvec[3]; - float mval_f[2]; - float zfac; + float rvec[3]; /* Current method just converts each point in screen-coordinates to * 3D-coordinates using the 3D-cursor as reference. In general, this * works OK, but it could of course be improved. */ gpencil_get_3d_reference(p, rvec); - zfac = ED_view3d_calc_zfac(p->region->regiondata, rvec, NULL); + const float zfac = ED_view3d_calc_zfac(p->region->regiondata, rvec); if (ED_view3d_project_float_global(p->region, rvec, mval_prj, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { - sub_v2_v2v2(mval_f, mval_prj, mval); - ED_view3d_win_to_delta(p->region, mval_f, dvec, zfac); + float dvec[3]; + float xy_delta[2]; + sub_v2_v2v2(xy_delta, mval_prj, mval); + ED_view3d_win_to_delta(p->region, xy_delta, zfac, dvec); sub_v3_v3v3(out, rvec, dvec); } else { @@ -2841,11 +2841,11 @@ static void gpencil_draw_apply_event(bContext *C, * add any x,y override position */ copy_v2fl_v2i(p->mval, event->mval); - p->shift = event->shift; + p->shift = (event->modifier & KM_SHIFT) != 0; /* verify direction for straight lines and guides */ if ((is_speed_guide) || - (event->alt && (RNA_boolean_get(op->ptr, "disable_straight") == false))) { + ((event->modifier & KM_ALT) && (RNA_boolean_get(op->ptr, "disable_straight") == false))) { if (p->straight == 0) { int dx = (int)fabsf(p->mval[0] - p->mvali[0]); int dy = (int)fabsf(p->mval[1] - p->mvali[1]); @@ -2886,13 +2886,13 @@ static void gpencil_draw_apply_event(bContext *C, /* special eraser modes */ if (p->paintmode == GP_PAINTMODE_ERASER) { - if (event->shift) { + if (event->modifier & KM_SHIFT) { p->flags |= GP_PAINTFLAG_HARD_ERASER; } else { p->flags &= ~GP_PAINTFLAG_HARD_ERASER; } - if (event->alt) { + if (event->modifier & KM_ALT) { p->flags |= GP_PAINTFLAG_STROKE_ERASER; } else { @@ -3116,11 +3116,11 @@ static void gpencil_guide_event_handling(bContext *C, else if ((event->type == EVT_LKEY) && (event->val == KM_RELEASE)) { add_notifier = true; guide->use_guide = true; - if (event->ctrl) { + if (event->modifier & KM_CTRL) { guide->angle = 0.0f; guide->type = GP_GUIDE_PARALLEL; } - else if (event->alt) { + else if (event->modifier & KM_ALT) { guide->type = GP_GUIDE_PARALLEL; guide->angle = RNA_float_get(op->ptr, "guide_last_angle"); } @@ -3150,10 +3150,10 @@ static void gpencil_guide_event_handling(bContext *C, add_notifier = true; float angle = guide->angle; float adjust = (float)M_PI / 180.0f; - if (event->alt) { + if (event->modifier & KM_ALT) { adjust *= 45.0f; } - else if (!event->shift) { + else if ((event->modifier & KM_SHIFT) == 0) { adjust *= 15.0f; } angle += (event->type == EVT_JKEY) ? adjust : -adjust; @@ -3633,7 +3633,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) */ } else if (event->type == EVT_ZKEY) { - if (event->ctrl) { + if (event->modifier & KM_CTRL) { p->status = GP_STATUS_DONE; estate = OPERATOR_FINISHED; } diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index 2d761dd6c91..57a184b0e8d 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -1494,7 +1494,7 @@ static void gpencil_primitive_edit_event_handling( float dy = (tgpi->mval[1] - tgpi->mvalo[1]); tgpi->cp1[0] += dx; tgpi->cp1[1] += dy; - if (event->shift) { + if (event->modifier & KM_SHIFT) { copy_v2_v2(tgpi->cp2, tgpi->cp1); } } @@ -1503,7 +1503,7 @@ static void gpencil_primitive_edit_event_handling( float dy = (tgpi->mval[1] - tgpi->mvalo[1]); tgpi->cp2[0] += dx; tgpi->cp2[1] += dy; - if (event->shift) { + if (event->modifier & KM_SHIFT) { copy_v2_v2(tgpi->cp1, tgpi->cp2); } } @@ -1692,7 +1692,7 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e WM_cursor_modal_set(win, WM_CURSOR_NSEW_SCROLL); copy_v2_v2(tgpi->end, tgpi->mval); - if (event->shift) { + if (event->modifier & KM_SHIFT) { gpencil_primitive_constrain(tgpi, true); } @@ -1722,7 +1722,7 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e case EVT_FKEY: /* brush thickness/ brush strength */ { if ((event->val == KM_PRESS)) { - if (event->shift) { + if (event->modifier & KM_SHIFT) { tgpi->prev_flag = tgpi->flag; tgpi->flag = IN_BRUSH_STRENGTH; } @@ -1900,7 +1900,7 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e case EVT_FKEY: /* brush thickness/ brush strength */ { if ((event->val == KM_PRESS)) { - if (event->shift) { + if (event->modifier & KM_SHIFT) { tgpi->prev_flag = tgpi->flag; tgpi->flag = IN_BRUSH_STRENGTH; } @@ -1954,12 +1954,12 @@ static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *e copy_v2_v2(tgpi->origin, tgpi->mval); } /* Keep square if shift key */ - if (event->shift) { + if (event->modifier & KM_SHIFT) { gpencil_primitive_constrain( tgpi, (ELEM(tgpi->type, GP_STROKE_LINE, GP_STROKE_POLYLINE) || tgpi->curve)); } /* Center primitive if alt key */ - if (event->alt && !ELEM(tgpi->type, GP_STROKE_POLYLINE)) { + if ((event->modifier & KM_ALT) && !ELEM(tgpi->type, GP_STROKE_POLYLINE)) { tgpi->start[0] = tgpi->origin[0] - (tgpi->end[0] - tgpi->origin[0]); tgpi->start[1] = tgpi->origin[1] - (tgpi->end[1] - tgpi->origin[1]); } diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c index e8f097d0018..67325e8a3d1 100644 --- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c +++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c @@ -507,7 +507,7 @@ static void gpencil_brush_grab_calc_dvec(tGP_BrushEditData *gso) /* Convert mouse-movements to movement vector */ RegionView3D *rv3d = gso->region->regiondata; float *rvec = gso->object->loc; - float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL); + const float zfac = ED_view3d_calc_zfac(rv3d, rvec); float mval_f[2]; @@ -525,7 +525,7 @@ static void gpencil_brush_grab_calc_dvec(tGP_BrushEditData *gso) copy_v2_v2(mval_f, r); } - ED_view3d_win_to_delta(gso->region, mval_f, gso->dvec, zfac); + ED_view3d_win_to_delta(gso->region, mval_f, zfac, gso->dvec); } /* Apply grab transform to all relevant points of the affected strokes */ @@ -624,17 +624,16 @@ static void gpencil_brush_calc_midpoint(tGP_BrushEditData *gso) */ RegionView3D *rv3d = gso->region->regiondata; const float *rvec = gso->object->loc; - float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL); + const float zfac = ED_view3d_calc_zfac(rv3d, rvec); - float mval_f[2]; - copy_v2_v2(mval_f, gso->mval); float mval_prj[2]; - float dvec[3]; if (ED_view3d_project_float_global(gso->region, rvec, mval_prj, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { - sub_v2_v2v2(mval_f, mval_prj, mval_f); - ED_view3d_win_to_delta(gso->region, mval_f, dvec, zfac); + float dvec[3]; + float xy_delta[2]; + sub_v2_v2v2(xy_delta, mval_prj, gso->mval); + ED_view3d_win_to_delta(gso->region, xy_delta, zfac, dvec); sub_v3_v3v3(gso->dvec, rvec, dvec); } else { @@ -830,10 +829,10 @@ static bool gpencil_brush_randomize_apply(tGP_BrushEditData *gso, /* 3D: Project to 3D space */ bool flip; RegionView3D *rv3d = gso->region->regiondata; - float zfac = ED_view3d_calc_zfac(rv3d, &pt->x, &flip); + const float zfac = ED_view3d_calc_zfac_ex(rv3d, &pt->x, &flip); if (flip == false) { float dvec[3]; - ED_view3d_win_to_delta(gso->gsc.region, svec, dvec, zfac); + ED_view3d_win_to_delta(gso->gsc.region, svec, zfac, dvec); add_v3_v3(&pt->x, dvec); /* compute lock axis */ gpencil_sculpt_compute_lock_axis(gso, pt, save_pt); @@ -1883,7 +1882,7 @@ static void gpencil_sculpt_brush_apply_event(bContext *C, wmOperator *op, const RNA_collection_add(op->ptr, "stroke", &itemptr); RNA_float_set_array(&itemptr, "mouse", mouse); - RNA_boolean_set(&itemptr, "pen_flip", event->ctrl != false); + RNA_boolean_set(&itemptr, "pen_flip", (event->modifier & KM_CTRL) != 0); RNA_boolean_set(&itemptr, "is_start", gso->first); /* handle pressure sensitivity (which is supplied by tablets and otherwise 1.0) */ @@ -1895,7 +1894,7 @@ static void gpencil_sculpt_brush_apply_event(bContext *C, wmOperator *op, const } RNA_float_set(&itemptr, "pressure", pressure); - if (event->shift) { + if (event->modifier & KM_SHIFT) { gso->brush_prev = gso->brush; gso->brush = gpencil_sculpt_get_smooth_brush(gso); diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c index da263d44fc6..fca4ff84dc5 100644 --- a/source/blender/editors/gpencil/gpencil_select.c +++ b/source/blender/editors/gpencil/gpencil_select.c @@ -2659,7 +2659,7 @@ static int gpencil_select_invoke(bContext *C, wmOperator *op, const wmEvent *eve RNA_int_set_array(op->ptr, "location", event->mval); if (!RNA_struct_property_is_set(op->ptr, "use_shift_extend")) { - RNA_boolean_set(op->ptr, "use_shift_extend", event->shift); + RNA_boolean_set(op->ptr, "use_shift_extend", event->modifier & KM_SHIFT); } return gpencil_select_exec(C, op); diff --git a/source/blender/editors/gpencil/gpencil_trace_utils.c b/source/blender/editors/gpencil/gpencil_trace_utils.c index 10be8c3e91e..735759d40ec 100644 --- a/source/blender/editors/gpencil/gpencil_trace_utils.c +++ b/source/blender/editors/gpencil/gpencil_trace_utils.c @@ -314,7 +314,7 @@ void ED_gpencil_trace_data_to_strokes(Main *bmain, if (sample > 0.0f) { /* Resample stroke. Don't need to call to BKE_gpencil_stroke_geometry_update() because * the sample function already call that. */ - BKE_gpencil_stroke_sample(gpd, gps, sample, false); + BKE_gpencil_stroke_sample(gpd, gps, sample, false, 0); } else { BKE_gpencil_stroke_geometry_update(gpd, gps); diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index c0777ac3105..9a658b68f21 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -822,17 +822,16 @@ bool gpencil_point_xy_to_3d(const GP_SpaceConversion *gsc, ED_gpencil_drawing_reference_get(scene, gsc->ob, scene->toolsettings->gpencil_v3d_align, rvec); - float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL); + float zfac = ED_view3d_calc_zfac(rv3d, rvec); - float mval_f[2], mval_prj[2]; - float dvec[3]; - - copy_v2_v2(mval_f, screen_co); + float mval_prj[2]; if (ED_view3d_project_float_global(gsc->region, rvec, mval_prj, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { - sub_v2_v2v2(mval_f, mval_prj, mval_f); - ED_view3d_win_to_delta(gsc->region, mval_f, dvec, zfac); + float dvec[3]; + float xy_delta[2]; + sub_v2_v2v2(xy_delta, mval_prj, screen_co); + ED_view3d_win_to_delta(gsc->region, xy_delta, zfac, dvec); sub_v3_v3v3(r_out, rvec, dvec); return true; @@ -863,21 +862,21 @@ void gpencil_stroke_convertcoords_tpoint(Scene *scene, */ } else { - float mval_f[2] = {UNPACK2(point2D->m_xy)}; float mval_prj[2]; - float rvec[3], dvec[3]; - float zfac; + float rvec[3]; /* Current method just converts each point in screen-coordinates to * 3D-coordinates using the 3D-cursor as reference. */ ED_gpencil_drawing_reference_get(scene, ob, ts->gpencil_v3d_align, rvec); - zfac = ED_view3d_calc_zfac(region->regiondata, rvec, NULL); + const float zfac = ED_view3d_calc_zfac(region->regiondata, rvec); if (ED_view3d_project_float_global(region, rvec, mval_prj, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { - sub_v2_v2v2(mval_f, mval_prj, mval_f); - ED_view3d_win_to_delta(region, mval_f, dvec, zfac); + float dvec[3]; + float xy_delta[2]; + sub_v2_v2v2(xy_delta, mval_prj, point2D->m_xy); + ED_view3d_win_to_delta(region, xy_delta, zfac, dvec); sub_v3_v3v3(r_out, rvec, dvec); } else { @@ -2005,19 +2004,19 @@ static void gpencil_stroke_convertcoords(ARegion *region, const float origin[3], float out[3]) { - float mval_f[2] = {UNPACK2(point2D->m_xy)}; float mval_prj[2]; - float rvec[3], dvec[3]; - float zfac; + float rvec[3]; copy_v3_v3(rvec, origin); - zfac = ED_view3d_calc_zfac(region->regiondata, rvec, NULL); + const float zfac = ED_view3d_calc_zfac(region->regiondata, rvec); if (ED_view3d_project_float_global(region, rvec, mval_prj, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { - sub_v2_v2v2(mval_f, mval_prj, mval_f); - ED_view3d_win_to_delta(region, mval_f, dvec, zfac); + float dvec[3]; + float xy_delta[2]; + sub_v2_v2v2(xy_delta, mval_prj, point2D->m_xy); + ED_view3d_win_to_delta(region, xy_delta, zfac, dvec); sub_v3_v3v3(out, rvec, dvec); } else { diff --git a/source/blender/editors/gpencil/gpencil_vertex_paint.c b/source/blender/editors/gpencil/gpencil_vertex_paint.c index 1d1b00e3f08..244942a87ba 100644 --- a/source/blender/editors/gpencil/gpencil_vertex_paint.c +++ b/source/blender/editors/gpencil/gpencil_vertex_paint.c @@ -1241,7 +1241,7 @@ static void gpencil_vertexpaint_brush_apply_event(bContext *C, RNA_collection_add(op->ptr, "stroke", &itemptr); RNA_float_set_array(&itemptr, "mouse", mouse); - RNA_boolean_set(&itemptr, "pen_flip", event->ctrl != false); + RNA_boolean_set(&itemptr, "pen_flip", event->modifier & KM_CTRL); RNA_boolean_set(&itemptr, "is_start", gso->first); /* Handle pressure sensitivity (which is supplied by tablets). */ diff --git a/source/blender/editors/gpencil/gpencil_weight_paint.c b/source/blender/editors/gpencil/gpencil_weight_paint.c index 8fe4cba7021..01e73cd2abd 100644 --- a/source/blender/editors/gpencil/gpencil_weight_paint.c +++ b/source/blender/editors/gpencil/gpencil_weight_paint.c @@ -698,7 +698,7 @@ static void gpencil_weightpaint_brush_apply_event(bContext *C, RNA_collection_add(op->ptr, "stroke", &itemptr); RNA_float_set_array(&itemptr, "mouse", mouse); - RNA_boolean_set(&itemptr, "pen_flip", event->ctrl != false); + RNA_boolean_set(&itemptr, "pen_flip", event->modifier & KM_CTRL); RNA_boolean_set(&itemptr, "is_start", gso->first); /* Handle pressure sensitivity (which is supplied by tablets). */ diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index e6a68f7fab7..4b6f5e4cac6 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -1090,6 +1090,15 @@ void animviz_calc_motionpaths(struct Depsgraph *depsgraph, bool restore); /** + * Update motion path computation range (in `ob.avs` or `armature.avs`) from user choice in + * `ob.avs.path_range` or `arm.avs.path_range`, depending on active user mode. + * + * \param ob: Object to compute range for (must be provided). + * \param scene: Used when scene range is chosen. + */ +void animviz_motionpath_compute_range(struct Object *ob, struct Scene *scene); + +/** * Get list of motion paths to be baked for the given object. * - assumes the given list is ready to be used. */ diff --git a/source/blender/editors/include/ED_curves.h b/source/blender/editors/include/ED_curves.h index 7316b045646..9233b65b2ce 100644 --- a/source/blender/editors/include/ED_curves.h +++ b/source/blender/editors/include/ED_curves.h @@ -15,3 +15,14 @@ void ED_operatortypes_curves(void); #ifdef __cplusplus } #endif + +#ifdef __cplusplus + +# include "BKE_curves.hh" + +namespace blender::ed::curves { + +bke::CurvesGeometry primitive_random_sphere(int curves_size, int points_per_curve); + +} +#endif diff --git a/source/blender/editors/include/ED_curves_sculpt.h b/source/blender/editors/include/ED_curves_sculpt.h new file mode 100644 index 00000000000..8aab1533e25 --- /dev/null +++ b/source/blender/editors/include/ED_curves_sculpt.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup editors + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +void ED_operatortypes_sculpt_curves(void); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/include/ED_keyframes_keylist.h b/source/blender/editors/include/ED_keyframes_keylist.h index 6a8820d0083..fd3d35e1df7 100644 --- a/source/blender/editors/include/ED_keyframes_keylist.h +++ b/source/blender/editors/include/ED_keyframes_keylist.h @@ -131,7 +131,13 @@ const struct ActKeyColumn *ED_keylist_find_any_between(const struct AnimKeylist 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); +bool ED_keylist_all_keys_frame_range(const struct AnimKeylist *keylist, Range2f *r_frame_range); +/** + * Return the selected key-frame's range. + * \return False If none are selected and does not affect the frame range. + */ +bool ED_keylist_selected_keys_frame_range(const struct AnimKeylist *keylist, + Range2f *r_frame_range); const ActKeyColumn *ED_keylist_array(const struct AnimKeylist *keylist); int64_t ED_keylist_array_len(const struct AnimKeylist *keylist); @@ -183,10 +189,10 @@ void mask_to_keylist(struct bDopeSheet *ads, /* ActKeyColumn API ---------------- */ -/* Checks if ActKeyColumn has any block data */ +/** Checks if #ActKeyColumn has any block data. */ bool actkeyblock_is_valid(const ActKeyColumn *ac); -/* Checks if ActKeyColumn can be used as a block (i.e. drawn/used to detect "holds") */ +/** Checks if #ActKeyColumn can be used as a block (i.e. drawn/used to detect "holds"). */ int actkeyblock_get_valid_hold(const ActKeyColumn *ac); #ifdef __cplusplus diff --git a/source/blender/editors/include/ED_render.h b/source/blender/editors/include/ED_render.h index e1b6a935d6d..ee40cc75e1e 100644 --- a/source/blender/editors/include/ED_render.h +++ b/source/blender/editors/include/ED_render.h @@ -24,6 +24,7 @@ struct Scene; struct ScrArea; struct bContext; struct bScreen; +struct PreviewImage; struct wmWindow; struct wmWindowManager; @@ -87,16 +88,13 @@ void ED_preview_shader_job(const struct bContext *C, ePreviewRenderMethod method); void ED_preview_icon_render(const struct bContext *C, struct Scene *scene, + struct PreviewImage *prv_img, struct ID *id, - unsigned int *rect, - int sizex, - int sizey); + enum eIconSizes icon_size); void ED_preview_icon_job(const struct bContext *C, - void *owner, + struct PreviewImage *prv_img, struct ID *id, - unsigned int *rect, - int sizex, - int sizey, + enum eIconSizes icon_size, bool delay); void ED_preview_restart_queue_free(void); diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index 5b439cb0978..c89df9c3789 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -250,6 +250,12 @@ void ED_area_offscreen_free(struct wmWindowManager *wm, struct wmWindow *win, struct ScrArea *area); +/** + * Search all screens, even non-active or overlapping (multiple windows), return the most-likely + * area of interest. xy is relative to active window, like all similar functions. + */ +ScrArea *ED_area_find_under_cursor(const struct bContext *C, int spacetype, const int xy[2]); + ScrArea *ED_screen_areas_iter_first(const struct wmWindow *win, const bScreen *screen); ScrArea *ED_screen_areas_iter_next(const bScreen *screen, const ScrArea *area); /** diff --git a/source/blender/editors/include/ED_util.h b/source/blender/editors/include/ED_util.h index bd3a6bce8e8..f235f696ccc 100644 --- a/source/blender/editors/include/ED_util.h +++ b/source/blender/editors/include/ED_util.h @@ -106,7 +106,7 @@ void ED_slider_allow_overshoot_set(struct tSlider *slider, bool value); * \note Shift/Control are not configurable key-bindings. */ void apply_keyb_grid( - int shift, int ctrl, float *val, float fac1, float fac2, float fac3, int invert); + bool shift, bool ctrl, float *val, float fac1, float fac2, float fac3, int invert); /* where else to go ? */ void unpack_menu(struct bContext *C, diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 46c9a1973eb..b1435e76eb2 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -507,9 +507,18 @@ float ED_view3d_pixel_size(const struct RegionView3D *rv3d, const float co[3]); float ED_view3d_pixel_size_no_ui_scale(const struct RegionView3D *rv3d, const float co[3]); /** - * Calculate a depth value from \a co, use with #ED_view3d_win_to_delta + * Calculate a depth value from \a co, use with #ED_view3d_win_to_delta. + * + * \param r_flip: Set to `zfac < 0.0` before the value is made signed. + * Since it's important in some cases to know if the value was flipped. + * + * \return The unsigned depth component of `co` multiplied by `rv3d->persmat` matrix, + * with additional sanitation to ensure the result is never negative + * as this isn't useful for tool-code. */ -float ED_view3d_calc_zfac(const struct RegionView3D *rv3d, const float co[3], bool *r_flip); +float ED_view3d_calc_zfac_ex(const struct RegionView3D *rv3d, const float co[3], bool *r_flip); +/** See #ED_view3d_calc_zfac_ex doc-string. */ +float ED_view3d_calc_zfac(const struct RegionView3D *rv3d, const float co[3]); /** * Calculate a depth value from `co` (result should only be used for comparison). */ @@ -627,16 +636,24 @@ bool ED_view3d_win_to_3d_on_plane_int(const struct ARegion *region, float r_out[3]); /** * Calculate a 3d difference vector from 2d window offset. - * note that #ED_view3d_calc_zfac() must be called first to determine + * + * \note that #ED_view3d_calc_zfac() must be called first to determine * the depth used to calculate the delta. + * + * When the `zfac` is calculated based on a world-space location directly under the cursor, + * the value of `r_out` can be subtracted from #RegionView3D.ofs to pan the view + * with the contents following the cursor perfectly (without sliding). + * * \param region: The region (used for the window width and height). - * \param mval: The area relative 2d difference (such as `event->mval[0] - other_x`). - * \param out: The resulting world-space delta. + * \param xy_delta: 2D difference (in pixels) such as `event->mval[0] - other_x`. + * \param zfac: The depth result typically calculated by by #ED_view3d_calc_zfac + * (see it's doc-string for details). + * \param r_out: The resulting world-space delta. */ void ED_view3d_win_to_delta(const struct ARegion *region, - const float mval[2], - float out[3], - float zfac); + const float xy_delta[2], + float zfac, + float r_out[3]); /** * Calculate a 3d origin from 2d window coordinates. * \note Orthographic views have a less obvious origin, @@ -645,23 +662,23 @@ void ED_view3d_win_to_delta(const struct ARegion *region, * * \param region: The region (used for the window width and height). * \param mval: The area relative 2d location (such as event->mval converted to floats). - * \param out: The resulting normalized world-space direction vector. + * \param r_out: The resulting normalized world-space direction vector. */ -void ED_view3d_win_to_origin(const struct ARegion *region, const float mval[2], float out[3]); +void ED_view3d_win_to_origin(const struct ARegion *region, const float mval[2], float r_out[3]); /** * Calculate a 3d direction vector from 2d window coordinates. * This direction vector starts and the view in the direction of the 2d window coordinates. * In orthographic view all window coordinates yield the same vector. * - * \note doesn't rely on ED_view3d_calc_zfac + * \note doesn't rely on #ED_view3d_calc_zfac * for perspective view, get the vector direction to * the mouse cursor as a normalized vector. * * \param region: The region (used for the window width and height). * \param mval: The area relative 2d location (such as event->mval converted to floats). - * \param out: The resulting normalized world-space direction vector. + * \param r_out: The resulting normalized world-space direction vector. */ -void ED_view3d_win_to_vector(const struct ARegion *region, const float mval[2], float out[3]); +void ED_view3d_win_to_vector(const struct ARegion *region, const float mval[2], float r_out[3]); /** * Calculate a 3d segment from 2d window coordinates. * This ray_start is located at the viewpoint, ray_end is a far point. @@ -1078,6 +1095,20 @@ bool ED_view3d_persp_ensure(const struct Depsgraph *depsgraph, struct View3D *v3d, struct ARegion *region); +/* Camera view functions. */ + +/** + * Utility to scale zoom level when in camera-view #RegionView3D.camzoom and apply limits. + * \return true a change was made. + */ +bool ED_view3d_camera_view_zoom_scale(struct RegionView3D *rv3d, const float scale); +/** + * Utility to pan when in camera view. + * \param event_ofs: The offset the pan in screen (pixel) coordinates. + * \return true when a change was made. + */ +bool ED_view3d_camera_view_pan(struct ARegion *region, const float event_ofs[2]); + /* Camera lock functions */ /** diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 7bbc8249a97..060c9dc33d6 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -2902,7 +2902,7 @@ uiBut *UI_context_active_but_prop_get(const struct bContext *C, struct PointerRNA *r_ptr, struct PropertyRNA **r_prop, int *r_index); -void UI_context_active_but_prop_handle(struct bContext *C); +void UI_context_active_but_prop_handle(struct bContext *C, bool handle_undo); void UI_context_active_but_clear(struct bContext *C, struct wmWindow *win, struct ARegion *region); struct wmOperator *UI_context_active_operator_get(const struct bContext *C); diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 4819e8ed82c..3619a7ce317 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -1418,7 +1418,7 @@ static bool ui_but_event_property_operator_string(const bContext *C, } /* This version is only for finding hotkeys for properties. - * These are set set via a data-path which is appended to the context, + * These are set via a data-path which is appended to the context, * manipulated using operators (see #ctx_toggle_opnames). */ if (ptr->owner_id) { @@ -3087,11 +3087,11 @@ bool ui_but_string_set(bContext *C, uiBut *but, const char *str) PointerRNA rptr; /* This is kind of hackish, in theory think we could only ever use the second member of - * this if/else, since ui_searchbox_apply() is supposed to always set that pointer when + * this if/else, since #ui_searchbox_apply() is supposed to always set that pointer when * we are storing pointers... But keeping str search first for now, * to try to break as little as possible existing code. All this is band-aids anyway. - * Fact remains, using editstr as main 'reference' over whole search button thingy - * is utterly weak and should be redesigned imho, but that's not a simple task. */ + * Fact remains, using `editstr` as main 'reference' over whole search button thingy + * is utterly weak and should be redesigned IMHO, but that's not a simple task. */ if (search_but && search_but->rnasearchprop && RNA_property_collection_lookup_string( &search_but->rnasearchpoin, search_but->rnasearchprop, str, &rptr)) { @@ -6529,7 +6529,7 @@ void UI_but_focus_on_enter_event(wmWindow *win, uiBut *but) event.type = EVT_BUT_OPEN; event.val = KM_PRESS; - event.is_repeat = false; + event.flag = 0; event.customdata = but; event.customdata_free = false; diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index 34a20b91172..07efcdcbe38 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -201,7 +201,7 @@ static uiBlock *menu_add_shortcut(bContext *C, ARegion *region, void *arg) /* XXX this guess_opname can potentially return a different keymap * than being found on adding later... */ wmKeyMap *km = WM_keymap_guess_opname(C, idname); - wmKeyMapItem *kmi = WM_keymap_add_item(km, idname, EVT_AKEY, KM_PRESS, 0, 0); + wmKeyMapItem *kmi = WM_keymap_add_item(km, idname, EVT_AKEY, KM_PRESS, 0, 0, KM_ANY); const int kmi_id = kmi->id; /* This takes ownership of prop, or prop can be NULL for reset. */ diff --git a/source/blender/editors/interface/interface_eyedropper.c b/source/blender/editors/interface/interface_eyedropper.c index e72381821e8..eaec1e249b7 100644 --- a/source/blender/editors/interface/interface_eyedropper.c +++ b/source/blender/editors/interface/interface_eyedropper.c @@ -147,8 +147,7 @@ void datadropper_win_area_find( *r_win = CTX_wm_window(C); *r_area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, mval); if (*r_area == NULL) { - wmWindowManager *wm = CTX_wm_manager(C); - *r_win = WM_window_find_under_cursor(wm, NULL, *r_win, mval, r_mval); + *r_win = WM_window_find_under_cursor(*r_win, mval, r_mval); if (*r_win) { screen = WM_window_get_active_screen(*r_win); *r_area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, r_mval); diff --git a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c b/source/blender/editors/interface/interface_eyedropper_gpencil_color.c index d6f529f9f94..f3c70e6a96a 100644 --- a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c +++ b/source/blender/editors/interface/interface_eyedropper_gpencil_color.c @@ -222,9 +222,9 @@ static void eyedropper_add_palette_color(bContext *C, const float col_conv[4]) static void eyedropper_gpencil_color_set(bContext *C, const wmEvent *event, EyedropperGPencil *eye) { - const bool only_stroke = ((!event->ctrl) && (!event->shift)); - const bool only_fill = ((!event->ctrl) && (event->shift)); - const bool both = ((event->ctrl) && (event->shift)); + const bool only_stroke = (event->modifier & (KM_CTRL | KM_SHIFT)) == 0; + const bool only_fill = ((event->modifier & KM_CTRL) == 0 && (event->modifier & KM_SHIFT)); + const bool both = ((event->modifier & KM_CTRL) && (event->modifier & KM_SHIFT)); float col_conv[4]; diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index bbb6bfabdd1..e0b64dcd4d6 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -275,7 +275,7 @@ static void ui_selectcontext_apply(bContext *C, const double value, const double value_orig); -# define IS_ALLSELECT_EVENT(event) ((event)->alt != 0) +# define IS_ALLSELECT_EVENT(event) (((event)->modifier & KM_ALT) != 0) /** just show a tinted color so users know its activated */ # define UI_BUT_IS_SELECT_CONTEXT UI_BUT_NODE_ACTIVE @@ -708,7 +708,8 @@ enum eSnapType { static enum eSnapType ui_event_to_snap(const wmEvent *event) { - return (event->ctrl) ? (event->shift) ? SNAP_ON_SMALL : SNAP_ON : SNAP_OFF; + return (event->modifier & KM_CTRL) ? (event->modifier & KM_SHIFT) ? SNAP_ON_SMALL : SNAP_ON : + SNAP_OFF; } static bool ui_event_is_snap(const wmEvent *event) @@ -1937,7 +1938,7 @@ static void ui_selectcontext_apply(bContext *C, /* could check for 'handle_layer_buttons' */ but->func) { wmWindow *win = CTX_wm_window(C); - if (!win->eventstate->shift) { + if ((win->eventstate->modifier & KM_SHIFT) == 0) { const int len = RNA_property_array_length(&but->rnapoin, prop); bool *tmparray = MEM_callocN(sizeof(bool) * len, __func__); @@ -3747,11 +3748,11 @@ static void ui_do_but_textedit( case EVT_XKEY: case EVT_CKEY: #if defined(__APPLE__) - if ((event->oskey && !IS_EVENT_MOD(event, shift, alt, ctrl)) || - (event->ctrl && !IS_EVENT_MOD(event, shift, alt, oskey))) { + if (ELEM(event->modifier, KM_OSKEY, KM_CTRL)) #else - if (event->ctrl && !IS_EVENT_MOD(event, shift, alt, oskey)) { + if (event->modifier == KM_CTRL) #endif + { if (event->type == EVT_VKEY) { changed = ui_textedit_copypaste(but, data, UI_TEXTEDIT_PASTE); } @@ -3769,16 +3770,16 @@ static void ui_do_but_textedit( ui_textedit_move(but, data, STRCUR_DIR_NEXT, - event->shift != 0, - event->ctrl ? STRCUR_JUMP_DELIM : STRCUR_JUMP_NONE); + event->modifier & KM_SHIFT, + (event->modifier & KM_CTRL) ? STRCUR_JUMP_DELIM : STRCUR_JUMP_NONE); retval = WM_UI_HANDLER_BREAK; break; case EVT_LEFTARROWKEY: ui_textedit_move(but, data, STRCUR_DIR_PREV, - event->shift != 0, - event->ctrl ? STRCUR_JUMP_DELIM : STRCUR_JUMP_NONE); + event->modifier & KM_SHIFT, + (event->modifier & KM_CTRL) ? STRCUR_JUMP_DELIM : STRCUR_JUMP_NONE); retval = WM_UI_HANDLER_BREAK; break; case WHEELDOWNMOUSE: @@ -3795,7 +3796,7 @@ static void ui_do_but_textedit( } ATTR_FALLTHROUGH; case EVT_ENDKEY: - ui_textedit_move(but, data, STRCUR_DIR_NEXT, event->shift != 0, STRCUR_JUMP_ALL); + ui_textedit_move(but, data, STRCUR_DIR_NEXT, event->modifier & KM_SHIFT, STRCUR_JUMP_ALL); retval = WM_UI_HANDLER_BREAK; break; case WHEELUPMOUSE: @@ -3812,7 +3813,7 @@ static void ui_do_but_textedit( } ATTR_FALLTHROUGH; case EVT_HOMEKEY: - ui_textedit_move(but, data, STRCUR_DIR_PREV, event->shift != 0, STRCUR_JUMP_ALL); + ui_textedit_move(but, data, STRCUR_DIR_PREV, event->modifier & KM_SHIFT, STRCUR_JUMP_ALL); retval = WM_UI_HANDLER_BREAK; break; case EVT_PADENTER: @@ -3822,13 +3823,13 @@ static void ui_do_but_textedit( break; case EVT_DELKEY: changed = ui_textedit_delete( - but, data, 1, event->ctrl ? STRCUR_JUMP_DELIM : STRCUR_JUMP_NONE); + but, data, 1, (event->modifier & KM_CTRL) ? STRCUR_JUMP_DELIM : STRCUR_JUMP_NONE); retval = WM_UI_HANDLER_BREAK; break; case EVT_BACKSPACEKEY: changed = ui_textedit_delete( - but, data, 0, event->ctrl ? STRCUR_JUMP_DELIM : STRCUR_JUMP_NONE); + but, data, 0, (event->modifier & KM_CTRL) ? STRCUR_JUMP_DELIM : STRCUR_JUMP_NONE); retval = WM_UI_HANDLER_BREAK; break; @@ -3837,10 +3838,9 @@ static void ui_do_but_textedit( /* Ctrl-A: Select all. */ #if defined(__APPLE__) /* OSX uses Command-A system-wide, so add it. */ - if ((event->oskey && !IS_EVENT_MOD(event, shift, alt, ctrl)) || - (event->ctrl && !IS_EVENT_MOD(event, shift, alt, oskey))) + if (ELEM(event->modifier, KM_OSKEY, KM_CTRL)) #else - if (event->ctrl && !IS_EVENT_MOD(event, shift, alt, oskey)) + if (event->modifier == KM_CTRL) #endif { ui_textedit_move(but, data, STRCUR_DIR_PREV, false, STRCUR_JUMP_ALL); @@ -3859,9 +3859,9 @@ static void ui_do_but_textedit( button_activate_state(C, but, BUTTON_STATE_EXIT); } } - else if (!IS_EVENT_MOD(event, ctrl, alt, oskey)) { + else if ((event->modifier & (KM_CTRL | KM_ALT | KM_OSKEY)) == 0) { /* Use standard keys for cycling through buttons Tab, Shift-Tab to reverse. */ - if (event->shift) { + if (event->modifier & KM_SHIFT) { ui_textedit_prev_but(block, but, data); } else { @@ -3874,12 +3874,12 @@ static void ui_do_but_textedit( case EVT_ZKEY: { /* Ctrl-Z or Ctrl-Shift-Z: Undo/Redo (allowing for OS-Key on Apple). */ - const bool is_redo = (event->shift != 0); + const bool is_redo = (event->modifier & KM_SHIFT); if ( #if defined(__APPLE__) - (event->oskey && !IS_EVENT_MOD(event, alt, ctrl)) || + ((event->modifier & KM_OSKEY) && ((event->modifier & (KM_ALT | KM_CTRL)) == 0)) || #endif - (event->ctrl && !IS_EVENT_MOD(event, alt, oskey))) { + ((event->modifier & KM_CTRL) && ((event->modifier & (KM_ALT | KM_OSKEY)) == 0))) { int undo_pos; const char *undo_str = ui_textedit_undo( data->undo_stack_text, is_redo ? 1 : -1, &undo_pos); @@ -4542,19 +4542,7 @@ static int ui_do_but_HOTKEYEVT(bContext *C, } /* always set */ - but->modifier_key = 0; - if (event->shift) { - but->modifier_key |= KM_SHIFT; - } - if (event->alt) { - but->modifier_key |= KM_ALT; - } - if (event->ctrl) { - but->modifier_key |= KM_CTRL; - } - if (event->oskey) { - but->modifier_key |= KM_OSKEY; - } + but->modifier_key = event->modifier; ui_but_update(but); ED_region_tag_redraw(data->region); @@ -4633,7 +4621,8 @@ static int ui_do_but_TAB( const int rna_type = but->rnaprop ? RNA_property_type(but->rnaprop) : 0; if (is_property && ELEM(rna_type, PROP_POINTER, PROP_STRING) && (but->custom_data != NULL) && - (event->type == LEFTMOUSE) && ((event->val == KM_DBL_CLICK) || event->ctrl)) { + (event->type == LEFTMOUSE) && + ((event->val == KM_DBL_CLICK) || (event->modifier & KM_CTRL))) { button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); return WM_UI_HANDLER_BREAK; } @@ -4666,7 +4655,8 @@ static int ui_do_but_TEX( if (ELEM(event->type, EVT_PADENTER, EVT_RETKEY) && (!UI_but_is_utf8(but))) { /* pass - allow filesel, enter to execute */ } - else if (ELEM(but->emboss, UI_EMBOSS_NONE, UI_EMBOSS_NONE_OR_STATUS) && !event->ctrl) { + else if (ELEM(but->emboss, UI_EMBOSS_NONE, UI_EMBOSS_NONE_OR_STATUS) && + ((event->modifier & KM_CTRL) == 0)) { /* pass */ } else { @@ -4735,7 +4725,7 @@ static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, cons button_activate_state(C, but, BUTTON_STATE_EXIT); return WM_UI_HANDLER_BREAK; } - if (ELEM(event->type, MOUSEPAN, WHEELDOWNMOUSE, WHEELUPMOUSE) && event->ctrl) { + if (ELEM(event->type, MOUSEPAN, WHEELDOWNMOUSE, WHEELUPMOUSE) && (event->modifier & KM_CTRL)) { /* Support Ctrl-Wheel to cycle values on expanded enum rows. */ if (but->type == UI_BTYPE_ROW) { int type = event->type; @@ -5325,24 +5315,24 @@ static int ui_do_but_NUM( } /* XXX hardcoded keymap check.... */ - if (type == MOUSEPAN && event->ctrl) { + if (type == MOUSEPAN && (event->modifier & KM_CTRL)) { /* allow accumulating values, otherwise scrolling gets preference */ retval = WM_UI_HANDLER_BREAK; } - else if (type == WHEELDOWNMOUSE && event->ctrl) { + else if (type == WHEELDOWNMOUSE && (event->modifier & KM_CTRL)) { mx = but->rect.xmin; but->drawflag &= ~UI_BUT_ACTIVE_RIGHT; but->drawflag |= UI_BUT_ACTIVE_LEFT; click = 1; } - else if (type == WHEELUPMOUSE && event->ctrl) { + else if ((type == WHEELUPMOUSE) && (event->modifier & KM_CTRL)) { mx = but->rect.xmax; but->drawflag &= ~UI_BUT_ACTIVE_LEFT; but->drawflag |= UI_BUT_ACTIVE_RIGHT; click = 1; } else if (event->val == KM_PRESS) { - if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && event->ctrl) { + if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && (event->modifier & KM_CTRL)) { button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); retval = WM_UI_HANDLER_BREAK; } @@ -5402,7 +5392,7 @@ static int ui_do_but_NUM( #endif fac = 1.0f; - if (event->shift) { + if (event->modifier & KM_SHIFT) { fac /= 10.0f; } @@ -5668,27 +5658,27 @@ static int ui_do_but_SLI( } /* XXX hardcoded keymap check.... */ - if (type == MOUSEPAN && event->ctrl) { + if ((type == MOUSEPAN) && (event->modifier & KM_CTRL)) { /* allow accumulating values, otherwise scrolling gets preference */ retval = WM_UI_HANDLER_BREAK; } - else if (type == WHEELDOWNMOUSE && event->ctrl) { + else if ((type == WHEELDOWNMOUSE) && (event->modifier & KM_CTRL)) { mx = but->rect.xmin; click = 2; } - else if (type == WHEELUPMOUSE && event->ctrl) { + else if ((type == WHEELUPMOUSE) && (event->modifier & KM_CTRL)) { mx = but->rect.xmax; click = 2; } else if (event->val == KM_PRESS) { - if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && event->ctrl) { + if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && (event->modifier & KM_CTRL)) { button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); retval = WM_UI_HANDLER_BREAK; } #ifndef USE_ALLSELECT /* alt-click on sides to get "arrows" like in UI_BTYPE_NUM buttons, * and match wheel usage above */ - else if (event->type == LEFTMOUSE && event->alt) { + else if ((event->type == LEFTMOUSE) && (event->modifier & KM_ALT)) { int halfpos = BLI_rctf_cent_x(&but->rect); click = 2; if (mx < halfpos) { @@ -5754,8 +5744,13 @@ static int ui_do_but_SLI( data->multi_data.drag_dir[0] += abs(data->draglastx - mx); data->multi_data.drag_dir[1] += abs(data->draglasty - my); #endif - if (ui_numedit_but_SLI( - but, data, mx, true, is_motion, event->ctrl != 0, event->shift != 0)) { + if (ui_numedit_but_SLI(but, + data, + mx, + true, + is_motion, + event->modifier & KM_CTRL, + event->modifier & KM_SHIFT)) { ui_numedit_apply(C, block, but, data); } @@ -5981,8 +5976,8 @@ static int ui_do_but_LISTROW(bContext *C, /* hack to pass on ctrl+click and double click to overlapping text * editing field for editing list item names */ - if ((ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && event->val == KM_PRESS && - event->ctrl) || + if ((ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && (event->val == KM_PRESS) && + (event->modifier & KM_CTRL)) || (event->type == LEFTMOUSE && event->val == KM_DBL_CLICK)) { uiBut *labelbut = ui_but_list_row_text_activate( C, but, data, event, BUTTON_ACTIVATE_TEXT_EDITING); @@ -6023,7 +6018,8 @@ static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, co return WM_UI_HANDLER_BREAK; } if (ui_but_supports_cycling(but)) { - if (ELEM(event->type, MOUSEPAN, WHEELDOWNMOUSE, WHEELUPMOUSE) && event->ctrl) { + if (ELEM(event->type, MOUSEPAN, WHEELDOWNMOUSE, WHEELUPMOUSE) && + (event->modifier & KM_CTRL)) { int type = event->type; int val = event->val; @@ -6210,7 +6206,7 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co button_activate_state(C, but, BUTTON_STATE_MENU_OPEN); return WM_UI_HANDLER_BREAK; } - if (ELEM(event->type, MOUSEPAN, WHEELDOWNMOUSE, WHEELUPMOUSE) && event->ctrl) { + if (ELEM(event->type, MOUSEPAN, WHEELDOWNMOUSE, WHEELUPMOUSE) && (event->modifier & KM_CTRL)) { ColorPicker *cpicker = but->custom_data; float hsv_static[3] = {0.0f}; float *hsv = cpicker ? cpicker->hsv_perceptual : hsv_static; @@ -6269,7 +6265,7 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { if (color_but->is_pallete_color) { - if (!event->ctrl) { + if ((event->modifier & KM_CTRL) == 0) { float color[3]; Paint *paint = BKE_paint_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); @@ -6639,7 +6635,7 @@ static int ui_do_but_HSVCUBE( button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); /* also do drag the first time */ - if (ui_numedit_but_HSVCUBE(but, data, mx, my, snap, event->shift != 0)) { + if (ui_numedit_but_HSVCUBE(but, data, mx, my, snap, event->modifier & KM_SHIFT)) { ui_numedit_apply(C, block, but, data); } @@ -6650,7 +6646,7 @@ static int ui_do_but_HSVCUBE( const wmNDOFMotionData *ndof = event->customdata; const enum eSnapType snap = ui_event_to_snap(event); - ui_ndofedit_but_HSVCUBE(hsv_but, data, ndof, snap, event->shift != 0); + ui_ndofedit_but_HSVCUBE(hsv_but, data, ndof, snap, event->modifier & KM_SHIFT); button_activate_state(C, but, BUTTON_STATE_EXIT); ui_apply_but(C, but->block, but, data, true); @@ -6702,7 +6698,7 @@ static int ui_do_but_HSVCUBE( if (mx != data->draglastx || my != data->draglasty || event->type != MOUSEMOVE) { const enum eSnapType snap = ui_event_to_snap(event); - if (ui_numedit_but_HSVCUBE(but, data, mx, my, snap, event->shift != 0)) { + if (ui_numedit_but_HSVCUBE(but, data, mx, my, snap, event->modifier & KM_SHIFT)) { ui_numedit_apply(C, block, but, data); } } @@ -6914,7 +6910,7 @@ static int ui_do_but_HSVCIRCLE( button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); /* also do drag the first time */ - if (ui_numedit_but_HSVCIRCLE(but, data, mx, my, snap, event->shift != 0)) { + if (ui_numedit_but_HSVCIRCLE(but, data, mx, my, snap, event->modifier & KM_SHIFT)) { ui_numedit_apply(C, block, but, data); } @@ -6925,7 +6921,7 @@ static int ui_do_but_HSVCIRCLE( const enum eSnapType snap = ui_event_to_snap(event); const wmNDOFMotionData *ndof = event->customdata; - ui_ndofedit_but_HSVCIRCLE(but, data, ndof, snap, event->shift != 0); + ui_ndofedit_but_HSVCIRCLE(but, data, ndof, snap, event->modifier & KM_SHIFT); button_activate_state(C, but, BUTTON_STATE_EXIT); ui_apply_but(C, but->block, but, data, true); @@ -6987,7 +6983,7 @@ static int ui_do_but_HSVCIRCLE( if (mx != data->draglastx || my != data->draglasty || event->type != MOUSEMOVE) { const enum eSnapType snap = ui_event_to_snap(event); - if (ui_numedit_but_HSVCIRCLE(but, data, mx, my, snap, event->shift != 0)) { + if (ui_numedit_but_HSVCIRCLE(but, data, mx, my, snap, event->modifier & KM_SHIFT)) { ui_numedit_apply(C, block, but, data); } } @@ -7037,7 +7033,7 @@ static int ui_do_but_COLORBAND( if (event->type == LEFTMOUSE && event->val == KM_PRESS) { ColorBand *coba = (ColorBand *)but->poin; - if (event->ctrl) { + if (event->modifier & KM_CTRL) { /* insert new key on mouse location */ const float pos = ((float)(mx - but->rect.xmin)) / BLI_rctf_size_x(&but->rect); BKE_colorband_element_add(coba, pos); @@ -7237,7 +7233,7 @@ static int ui_do_but_CURVE( float dist_min_sq = square_f(U.dpi_fac * 14.0f); /* 14 pixels radius */ int sel = -1; - if (event->ctrl) { + if (event->modifier & KM_CTRL) { float f_xy[2]; BLI_rctf_transform_pt_v(&cumap->curr, &but->rect, f_xy, m_xy); @@ -7301,7 +7297,7 @@ static int ui_do_but_CURVE( if (sel != -1) { /* ok, we move a point */ /* deselect all if this one is deselect. except if we hold shift */ - if (!event->shift) { + if ((event->modifier & KM_SHIFT) == 0) { for (int a = 0; a < cuma->totpoint; a++) { cmp[a].flag &= ~CUMA_SELECT; } @@ -7336,8 +7332,8 @@ static int ui_do_but_CURVE( data, event->xy[0], event->xy[1], - event->ctrl != 0, - event->shift != 0)) { + event->modifier & KM_CTRL, + event->modifier & KM_SHIFT)) { ui_numedit_apply(C, block, but, data); } } @@ -7350,7 +7346,7 @@ static int ui_do_but_CURVE( if (data->dragchange == false) { /* deselect all, select one */ - if (!event->shift) { + if ((event->modifier & KM_SHIFT) == 0) { for (int a = 0; a < cuma->totpoint; a++) { cmp[a].flag &= ~CUMA_SELECT; } @@ -7539,7 +7535,7 @@ static int ui_do_but_CURVEPROFILE( if (event->type == LEFTMOUSE && event->val == KM_PRESS) { const float m_xy[2] = {mx, my}; - if (event->ctrl) { + if (event->modifier & KM_CTRL) { float f_xy[2]; BLI_rctf_transform_pt_v(&profile->view_rect, &but->rect, f_xy, m_xy); @@ -7616,7 +7612,7 @@ static int ui_do_but_CURVEPROFILE( /* Change the flag for the point(s) if one was selected or added. */ if (i_selected != -1) { /* Deselect all if this one is deselected, except if we hold shift. */ - if (event->shift) { + if (event->modifier & KM_SHIFT) { pts[i_selected].flag ^= selection_type; } else { @@ -7647,7 +7643,7 @@ static int ui_do_but_CURVEPROFILE( if (event->type == MOUSEMOVE) { if (mx != data->draglastx || my != data->draglasty) { if (ui_numedit_but_CURVEPROFILE( - block, but, data, mx, my, event->ctrl != 0, event->shift != 0)) { + block, but, data, mx, my, event->modifier & KM_CTRL, event->modifier & KM_SHIFT)) { ui_numedit_apply(C, block, but, data); } } @@ -7871,7 +7867,7 @@ static int ui_do_but_TRACKPREVIEW( button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); /* also do drag the first time */ - if (ui_numedit_but_TRACKPREVIEW(C, but, data, mx, my, event->shift != 0)) { + if (ui_numedit_but_TRACKPREVIEW(C, but, data, mx, my, event->modifier & KM_SHIFT)) { ui_numedit_apply(C, block, but, data); } @@ -7888,7 +7884,7 @@ static int ui_do_but_TRACKPREVIEW( } else if (event->type == MOUSEMOVE) { if (mx != data->draglastx || my != data->draglasty) { - if (ui_numedit_but_TRACKPREVIEW(C, but, data, mx, my, event->shift != 0)) { + if (ui_numedit_but_TRACKPREVIEW(C, but, data, mx, my, event->modifier & KM_SHIFT)) { ui_numedit_apply(C, block, but, data); } } @@ -7918,8 +7914,9 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * if (data->state == BUTTON_STATE_HIGHLIGHT) { /* handle copy and paste */ - bool is_press_ctrl_but_no_shift = event->val == KM_PRESS && IS_EVENT_MOD(event, ctrl, oskey) && - !event->shift; + bool is_press_ctrl_but_no_shift = (event->val == KM_PRESS) && + (event->modifier & (KM_CTRL | KM_OSKEY)) && + (event->modifier & KM_SHIFT) == 0; const bool do_copy = event->type == EVT_CKEY && is_press_ctrl_but_no_shift; const bool do_paste = event->type == EVT_VKEY && is_press_ctrl_but_no_shift; @@ -7934,12 +7931,14 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * /* do copy first, because it is the only allowed operator when disabled */ if (do_copy) { - ui_but_copy(C, but, event->alt); + ui_but_copy(C, but, event->modifier & KM_ALT); return WM_UI_HANDLER_BREAK; } /* handle menu */ - if ((event->type == RIGHTMOUSE) && !IS_EVENT_MOD(event, shift, ctrl, alt, oskey) && + + if ((event->type == RIGHTMOUSE) && + (event->modifier & (KM_SHIFT | KM_CTRL | KM_ALT | KM_OSKEY)) == 0 && (event->val == KM_PRESS)) { /* For some button types that are typically representing entire sets of data, right-clicking * to spawn the context menu should also activate the item. This makes it clear which item @@ -7960,7 +7959,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * } if (do_paste) { - ui_but_paste(C, but, data, event->alt); + ui_but_paste(C, but, data, event->modifier & KM_ALT); return WM_UI_HANDLER_BREAK; } @@ -8793,7 +8792,7 @@ uiBut *UI_context_active_but_prop_get(const bContext *C, return activebut; } -void UI_context_active_but_prop_handle(bContext *C) +void UI_context_active_but_prop_handle(bContext *C, const bool handle_undo) { uiBut *activebut = ui_context_rna_button_active(C); if (activebut) { @@ -8804,6 +8803,11 @@ void UI_context_active_but_prop_handle(bContext *C) if (block->handle_func) { block->handle_func(C, block->handle_func_arg, activebut->retval); } + if (handle_undo) { + /* Update the button so the undo text uses the correct value. */ + ui_but_update(activebut); + ui_apply_but_undo(activebut); + } } } @@ -8942,7 +8946,7 @@ static int ui_handle_button_over(bContext *C, const wmEvent *event, ARegion *reg if (but) { button_activate_init(C, region, but, BUTTON_ACTIVATE_OVER); - if (event->alt && but->active) { + if ((event->modifier & KM_ALT) && but->active) { /* Display tool-tips if holding Alt on mouse-over when tool-tips are disabled in the * preferences. */ but->active->tooltip_force = true; @@ -8970,7 +8974,7 @@ void ui_but_activate_event(bContext *C, ARegion *region, uiBut *but) wm_event_init_from_window(win, &event); event.type = EVT_BUT_OPEN; event.val = KM_PRESS; - event.is_repeat = false; + event.flag = 0; event.customdata = but; event.customdata_free = false; @@ -9394,7 +9398,7 @@ static int ui_list_activate_hovered_row(bContext *C, } } - const int *mouse_xy = ISTWEAK(event->type) ? event->prev_click_xy : event->xy; + const int *mouse_xy = (event->val == KM_CLICK_DRAG) ? event->prev_click_xy : event->xy; uiBut *listrow = ui_list_row_find_mouse_over(region, mouse_xy); if (listrow) { wmOperatorType *custom_activate_optype = ui_list->dyn_data->custom_activate_optype; @@ -9421,7 +9425,7 @@ static bool ui_list_is_hovering_draggable_but(bContext *C, const wmEvent *event) { /* On a tweak event, uses the coordinates from where tweaking was started. */ - const int *mouse_xy = ISTWEAK(event->type) ? event->prev_click_xy : event->xy; + const int *mouse_xy = (event->val == KM_CLICK_DRAG) ? event->prev_click_xy : event->xy; const uiBut *hovered_but = ui_but_find_mouse_over_ex(region, mouse_xy, false, NULL, NULL); if (list->dyn_data->custom_drag_optype) { @@ -9438,7 +9442,7 @@ static int ui_list_handle_click_drag(bContext *C, ARegion *region, const wmEvent *event) { - if (!ELEM(event->type, LEFTMOUSE, EVT_TWEAK_L)) { + if (event->type != LEFTMOUSE) { return WM_HANDLER_CONTINUE; } @@ -9448,7 +9452,7 @@ static int ui_list_handle_click_drag(bContext *C, bool activate = false; bool activate_dragging = false; - if (event->type == EVT_TWEAK_L) { + if (event->val == KM_CLICK_DRAG) { if (is_draggable) { activate_dragging = true; activate = true; @@ -9458,7 +9462,7 @@ static int ui_list_handle_click_drag(bContext *C, * regular events (including mouse presses to start dragging) and this part only kicks in if it * hasn't handled the release event. Note that if there's no overlaid button, the row selects * on the press event already via regular #UI_BTYPE_LISTROW handling. */ - else if ((event->type == LEFTMOUSE) && (event->val == KM_CLICK)) { + else if (event->val == KM_CLICK) { activate = true; } @@ -9534,7 +9538,7 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi ui_pan_to_scroll(event, &type, &val); /* 'ui_pan_to_scroll' gives the absolute direction. */ - if (event->is_direction_inverted) { + if (event->flag & WM_EVENT_SCROLL_INVERT) { scroll_dir = -1; } @@ -9545,14 +9549,14 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi } } - if (ELEM(event->type, LEFTMOUSE, EVT_TWEAK_L)) { + if (event->type == LEFTMOUSE) { retval = ui_list_handle_click_drag(C, ui_list, region, event); } else if (val == KM_PRESS) { if ((ELEM(type, EVT_UPARROWKEY, EVT_DOWNARROWKEY, EVT_LEFTARROWKEY, EVT_RIGHTARROWKEY) && - !IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) || - ((ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE) && event->ctrl && - !IS_EVENT_MOD(event, shift, alt, oskey)))) { + (event->modifier & (KM_SHIFT | KM_CTRL | KM_ALT | KM_OSKEY)) == 0) || + ((ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE) && (event->modifier & KM_CTRL) && + (event->modifier & (KM_SHIFT | KM_ALT | KM_OSKEY)) == 0))) { const int value_orig = RNA_property_int_get(&listbox->rnapoin, listbox->rnaprop); int value, min, max; @@ -9609,7 +9613,7 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi } retval = WM_UI_HANDLER_BREAK; } - else if (ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE) && event->shift) { + else if (ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE) && (event->modifier & KM_SHIFT)) { /* We now have proper grip, but keep this anyway! */ if (ui_list->list_grip < (dyn_data->visual_height_min - UI_LIST_AUTO_SIZE_THRESHOLD)) { ui_list->list_grip = dyn_data->visual_height; @@ -10263,7 +10267,7 @@ static int ui_handle_menu_event(bContext *C, /* Smooth scrolling for popovers. */ case MOUSEPAN: { - if (IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) { + if (event->modifier & (KM_SHIFT | KM_CTRL | KM_ALT | KM_OSKEY)) { /* pass */ } else if (!ui_block_is_menu(block)) { @@ -10285,7 +10289,7 @@ static int ui_handle_menu_event(bContext *C, } case WHEELUPMOUSE: case WHEELDOWNMOUSE: { - if (IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) { + if (event->modifier & (KM_SHIFT | KM_CTRL | KM_ALT | KM_OSKEY)) { /* pass */ } else if (!ui_block_is_menu(block)) { @@ -10308,7 +10312,7 @@ static int ui_handle_menu_event(bContext *C, case EVT_HOMEKEY: case EVT_ENDKEY: /* Arrow-keys: only handle for block_loop blocks. */ - if (IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) { + if (event->modifier & (KM_SHIFT | KM_CTRL | KM_ALT | KM_OSKEY)) { /* pass */ } else if (inside || (block->flag & UI_BLOCK_LOOP)) { @@ -10455,11 +10459,11 @@ static int ui_handle_menu_event(bContext *C, /* Only respond to explicit press to avoid the event that opened the menu * activating an item when the key is held. */ - if (event->is_repeat) { + if (event->flag & WM_EVENT_IS_REPEAT) { break; } - if (event->alt) { + if (event->modifier & KM_ALT) { act += 10; } @@ -10539,10 +10543,10 @@ static int ui_handle_menu_event(bContext *C, case EVT_YKEY: case EVT_ZKEY: { if (ELEM(event->val, KM_PRESS, KM_DBL_CLICK) && - !IS_EVENT_MOD(event, shift, ctrl, oskey) && + ((event->modifier & (KM_SHIFT | KM_CTRL | KM_OSKEY)) == 0) && /* Only respond to explicit press to avoid the event that opened the menu * activating an item when the key is held. */ - !event->is_repeat) { + (event->flag & WM_EVENT_IS_REPEAT) == 0) { if (ui_menu_pass_event_to_parent_if_nonactive(menu, but, level, retval)) { break; } @@ -11066,7 +11070,7 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle case EVT_YKEY: case EVT_ZKEY: { if ((ELEM(event->val, KM_PRESS, KM_DBL_CLICK)) && - !IS_EVENT_MOD(event, shift, ctrl, oskey)) { + ((event->modifier & (KM_SHIFT | KM_CTRL | KM_OSKEY)) == 0)) { LISTBASE_FOREACH (uiBut *, but, &block->buttons) { if (but->menu_key == event->type) { ui_but_pie_button_activate(C, but, menu); diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index e277aa2e629..9dfc9be2a30 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -573,18 +573,6 @@ int UI_icon_from_event_type(short event_type, short event_value) else if (event_type == EVT_RIGHTALTKEY) { event_type = EVT_LEFTALTKEY; } - else if (event_type == EVT_TWEAK_L) { - event_type = LEFTMOUSE; - event_value = KM_CLICK_DRAG; - } - else if (event_type == EVT_TWEAK_M) { - event_type = MIDDLEMOUSE; - event_value = KM_CLICK_DRAG; - } - else if (event_type == EVT_TWEAK_R) { - event_type = RIGHTMOUSE; - event_value = KM_CLICK_DRAG; - } DrawInfo *di = g_di_event_list; do { @@ -1399,19 +1387,17 @@ static void icon_set_image(const bContext *C, const bool delay = prv_img->rect[size] != NULL; icon_create_rect(prv_img, size); - prv_img->flag[size] |= PRV_RENDERING; if (use_job && (!id || BKE_previewimg_id_supports_jobs(id))) { /* Job (background) version */ - ED_preview_icon_job( - C, prv_img, id, prv_img->rect[size], prv_img->w[size], prv_img->h[size], delay); + ED_preview_icon_job(C, prv_img, id, size, delay); } else { if (!scene) { scene = CTX_data_scene(C); } /* Immediate version */ - ED_preview_icon_render(C, scene, id, prv_img->rect[size], prv_img->w[size], prv_img->h[size]); + ED_preview_icon_render(C, scene, prv_img, id, size); } } @@ -2271,7 +2257,7 @@ int UI_icon_from_idcode(const int idcode) return ICON_CAMERA_DATA; case ID_CF: return ICON_FILE; - case ID_CU: + case ID_CU_LEGACY: return ICON_CURVE_DATA; case ID_GD: return ICON_OUTLINER_DATA_GREASEPENCIL; diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 54a5b496048..bd55d2d9d81 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -492,7 +492,7 @@ static void ui_layer_but_cb(bContext *C, void *arg_but, void *arg_index) PointerRNA *ptr = &but->rnapoin; PropertyRNA *prop = but->rnaprop; const int index = POINTER_AS_INT(arg_index); - const int shift = win->eventstate->shift; + const bool shift = win->eventstate->modifier & KM_SHIFT; const int len = RNA_property_array_length(ptr, prop); if (!shift) { @@ -752,7 +752,7 @@ static void ui_item_enum_expand_handle(bContext *C, void *arg1, void *arg2) { wmWindow *win = CTX_wm_window(C); - if (!win->eventstate->shift) { + if ((win->eventstate->modifier & KM_SHIFT) == 0) { uiBut *but = (uiBut *)arg1; const int enum_value = POINTER_AS_INT(arg2); diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index a96f14d7435..498c22748ce 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -314,7 +314,7 @@ static int operator_button_property_finish(bContext *C, PointerRNA *ptr, Propert RNA_property_update(C, ptr, prop); /* as if we pressed the button */ - UI_context_active_but_prop_handle(C); + UI_context_active_but_prop_handle(C, false); /* Since we don't want to undo _all_ edits to settings, eg window * edits on the screen or on operator settings. @@ -326,6 +326,19 @@ static int operator_button_property_finish(bContext *C, PointerRNA *ptr, Propert return OPERATOR_CANCELLED; } +static int operator_button_property_finish_with_undo(bContext *C, + PointerRNA *ptr, + PropertyRNA *prop) +{ + /* Perform updates required for this property. */ + RNA_property_update(C, ptr, prop); + + /* As if we pressed the button. */ + UI_context_active_but_prop_handle(C, true); + + return OPERATOR_FINISHED; +} + static bool reset_default_button_poll(bContext *C) { PointerRNA ptr; @@ -350,7 +363,7 @@ static int reset_default_button_exec(bContext *C, wmOperator *op) /* if there is a valid property that is editable... */ if (ptr.data && prop && RNA_property_editable(&ptr, prop)) { if (RNA_property_reset(&ptr, prop, (all) ? -1 : index)) { - return operator_button_property_finish(C, &ptr, prop); + return operator_button_property_finish_with_undo(C, &ptr, prop); } } @@ -369,7 +382,9 @@ static void UI_OT_reset_default_button(wmOperatorType *ot) ot->exec = reset_default_button_exec; /* flags */ - ot->flag = OPTYPE_UNDO; + /* Don't set #OPTYPE_UNDO because #operator_button_property_finish_with_undo + * is responsible for the undo push. */ + ot->flag = 0; /* properties */ RNA_def_boolean(ot->srna, "all", 1, "All", "Reset to default values all elements of the array"); diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index 2cb0f256b71..c7f2eb230cb 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -2061,8 +2061,8 @@ static void ui_handle_panel_header(const bContext *C, const uiBlock *block, const int mx, const int event_type, - const short ctrl, - const short shift) + const bool ctrl, + const bool shift) { Panel *panel = block->panel; ARegion *region = CTX_wm_region(C); @@ -2274,7 +2274,7 @@ static int ui_handle_panel_category_cycling(const wmEvent *event, (event->mval[0] > ((PanelCategoryDyn *)region->panels_category.first)->rect.xmin)); /* If mouse is inside non-tab region, ctrl key is required. */ - if (is_mousewheel && !event->ctrl && !inside_tabregion) { + if (is_mousewheel && (event->modifier & KM_CTRL) == 0 && !inside_tabregion) { return WM_UI_HANDLER_CONTINUE; } @@ -2291,7 +2291,7 @@ static int ui_handle_panel_category_cycling(const wmEvent *event, pc_dyn = (event->type == WHEELDOWNMOUSE) ? pc_dyn->next : pc_dyn->prev; } else { - const bool backwards = event->shift; + const bool backwards = event->modifier & KM_SHIFT; pc_dyn = backwards ? pc_dyn->prev : pc_dyn->next; if (!pc_dyn) { /* Proper cyclic behavior, back to first/last category (only used for ctrl+tab). */ @@ -2349,7 +2349,7 @@ int ui_handler_panel_region(bContext *C, retval = WM_UI_HANDLER_BREAK; } } - else if ((event->type == EVT_TABKEY && event->ctrl) || + else if (((event->type == EVT_TABKEY) && (event->modifier & KM_CTRL)) || ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE)) { /* Cycle tabs. */ retval = ui_handle_panel_category_cycling(event, region, active_but); @@ -2386,9 +2386,11 @@ int ui_handler_panel_region(bContext *C, /* The panel collapse / expand key "A" is special as it takes priority over * active button handling. */ - if (event->type == EVT_AKEY && !IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) { + if (event->type == EVT_AKEY && + ((event->modifier & (KM_SHIFT | KM_CTRL | KM_ALT | KM_OSKEY)) == 0)) { retval = WM_UI_HANDLER_BREAK; - ui_handle_panel_header(C, block, mx, event->type, event->ctrl, event->shift); + ui_handle_panel_header( + C, block, mx, event->type, event->modifier & KM_CTRL, event->modifier & KM_SHIFT); break; } } @@ -2402,7 +2404,8 @@ int ui_handler_panel_region(bContext *C, /* All mouse clicks inside panel headers should return in break. */ if (ELEM(event->type, EVT_RETKEY, EVT_PADENTER, LEFTMOUSE)) { retval = WM_UI_HANDLER_BREAK; - ui_handle_panel_header(C, block, mx, event->type, event->ctrl, event->shift); + ui_handle_panel_header( + C, block, mx, event->type, event->modifier & KM_CTRL, event->modifier & KM_SHIFT); } else if (event->type == RIGHTMOUSE) { retval = WM_UI_HANDLER_BREAK; diff --git a/source/blender/editors/interface/interface_query.c b/source/blender/editors/interface/interface_query.c index 8a945c8c913..4703367671d 100644 --- a/source/blender/editors/interface/interface_query.c +++ b/source/blender/editors/interface/interface_query.c @@ -310,7 +310,7 @@ uiBut *ui_but_find_mouse_over_ex(const ARegion *region, uiBut *ui_but_find_mouse_over(const ARegion *region, const wmEvent *event) { - return ui_but_find_mouse_over_ex(region, event->xy, event->ctrl != 0, NULL, NULL); + return ui_but_find_mouse_over_ex(region, event->xy, event->modifier & KM_CTRL, NULL, NULL); } uiBut *ui_but_find_rect_over(const struct ARegion *region, const rcti *rect_px) diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c index 09faf493ce7..29553ff65d1 100644 --- a/source/blender/editors/interface/interface_region_tooltip.c +++ b/source/blender/editors/interface/interface_region_tooltip.c @@ -701,7 +701,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is /* Keymap */ /* This is too handy not to expose somehow, let's be sneaky for now. */ - if ((is_label == false) && CTX_wm_window(C)->eventstate->shift) { + if ((is_label == false) && CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) { const char *expr_imports[] = {"bpy", "bl_ui", NULL}; char expr[256]; SNPRINTF(expr, diff --git a/source/blender/editors/interface/interface_template_list.cc b/source/blender/editors/interface/interface_template_list.cc index 40675da71a9..6139ac8e702 100644 --- a/source/blender/editors/interface/interface_template_list.cc +++ b/source/blender/editors/interface/interface_template_list.cc @@ -598,7 +598,7 @@ static char *uilist_item_tooltip_func(bContext *UNUSED(C), void *argN, const cha } /** - * \note Note that \a layout_type may be null. + * \note that \a layout_type may be null. */ static uiList *ui_list_ensure(bContext *C, uiListType *ui_list_type, diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 2b7ca1f8b71..32b3bb5e926 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -609,7 +609,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr, NULL); RNA_property_update(C, &template_ui->ptr, template_ui->prop); - if (id && CTX_wm_window(C)->eventstate->shift) { + if (id && CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) { /* only way to force-remove data (on save) */ id_us_clear_real(id); id_fake_user_clear(id); @@ -635,7 +635,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) case UI_ID_LOCAL: if (id) { Main *bmain = CTX_data_main(C); - if (CTX_wm_window(C)->eventstate->shift) { + if (CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) { if (ID_IS_OVERRIDABLE_LIBRARY(id)) { /* Only remap that specific ID usage to overriding local data-block. */ ID *override_id = BKE_lib_override_library_create_from_id(bmain, id, false); @@ -731,7 +731,7 @@ static const char *template_id_browse_tip(const StructRNA *type) return N_("Browse Object to be linked"); case ID_ME: return N_("Browse Mesh Data to be linked"); - case ID_CU: + case ID_CU_LEGACY: return N_("Browse Curve Data to be linked"); case ID_MB: return N_("Browse Metaball Data to be linked"); @@ -844,7 +844,7 @@ static uiBut *template_id_def_new_but(uiBlock *block, BLT_I18NCONTEXT_ID_SCENE, BLT_I18NCONTEXT_ID_OBJECT, BLT_I18NCONTEXT_ID_MESH, - BLT_I18NCONTEXT_ID_CURVE, + BLT_I18NCONTEXT_ID_CURVE_LEGACY, BLT_I18NCONTEXT_ID_METABALL, BLT_I18NCONTEXT_ID_MATERIAL, BLT_I18NCONTEXT_ID_TEXTURE, @@ -5539,7 +5539,7 @@ static void handle_layer_buttons(bContext *C, void *arg1, void *arg2) uiBut *but = arg1; const int cur = POINTER_AS_INT(arg2); wmWindow *win = CTX_wm_window(C); - const int shift = win->eventstate->shift; + const bool shift = win->eventstate->modifier & KM_SHIFT; if (!shift) { const int tot = RNA_property_array_length(&but->rnapoin, but->rnaprop); diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index fbbf3c6fdf1..d1f3843c643 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -2579,7 +2579,7 @@ static void widget_state(uiWidgetType *wt, int state, int drawflag, eUIEmbossTyp * * A lot of places of the UI like the Node Editor or panels are zoomable. In most cases we can * get the zoom factor from the aspect, but in some cases like popups we need to fall back to - * using the the size of the element. The latter method relies on the element always being the same + * using the size of the element. The latter method relies on the element always being the same * size. * \{ */ @@ -4293,7 +4293,7 @@ static void widget_tab( const bool is_active = (state & UI_SELECT); /* Draw shaded outline - Disabled for now, - * seems incorrect and also looks nicer without it imho ;) */ + * seems incorrect and also looks nicer without it IMHO ;). */ // #define USE_TAB_SHADED_HIGHLIGHT uchar theme_col_tab_highlight[3]; diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c index 09d57d3ea99..28a025ee581 100644 --- a/source/blender/editors/interface/view2d_ops.c +++ b/source/blender/editors/interface/view2d_ops.c @@ -1449,7 +1449,7 @@ static int view2d_ndof_invoke(bContext *C, wmOperator *op, const wmEvent *event) /* tune these until it feels right */ const float zoom_sensitivity = 0.5f; const float speed = 10.0f; /* match view3d ortho */ - const bool has_translate = (ndof->tvec[0] && ndof->tvec[1]) && view_pan_poll(C); + const bool has_translate = !is_zero_v2(ndof->tvec) && view_pan_poll(C); const bool has_zoom = (ndof->tvec[2] != 0.0f) && view_zoom_poll(C); if (has_translate) { diff --git a/source/blender/editors/io/io_obj.c b/source/blender/editors/io/io_obj.c index 28e14a14f5f..1c821eebdee 100644 --- a/source/blender/editors/io/io_obj.c +++ b/source/blender/editors/io/io_obj.c @@ -38,12 +38,12 @@ static const EnumPropertyItem io_obj_transform_axis_forward[] = { {OBJ_AXIS_Z_FORWARD, "Z_FORWARD", 0, "Z", "Positive Z axis"}, {OBJ_AXIS_NEGATIVE_X_FORWARD, "NEGATIVE_X_FORWARD", 0, "-X", "Negative X axis"}, {OBJ_AXIS_NEGATIVE_Y_FORWARD, "NEGATIVE_Y_FORWARD", 0, "-Y", "Negative Y axis"}, - {OBJ_AXIS_NEGATIVE_Z_FORWARD, "NEGATIVE_Z_FORWARD", 0, "-Z (Default)", "Negative Z axis"}, + {OBJ_AXIS_NEGATIVE_Z_FORWARD, "NEGATIVE_Z_FORWARD", 0, "-Z", "Negative Z axis"}, {0, NULL, 0, NULL, NULL}}; static const EnumPropertyItem io_obj_transform_axis_up[] = { {OBJ_AXIS_X_UP, "X_UP", 0, "X", "Positive X axis"}, - {OBJ_AXIS_Y_UP, "Y_UP", 0, "Y (Default)", "Positive Y axis"}, + {OBJ_AXIS_Y_UP, "Y_UP", 0, "Y", "Positive Y axis"}, {OBJ_AXIS_Z_UP, "Z_UP", 0, "Z", "Positive Z axis"}, {OBJ_AXIS_NEGATIVE_X_UP, "NEGATIVE_X_UP", 0, "-X", "Negative X axis"}, {OBJ_AXIS_NEGATIVE_Y_UP, "NEGATIVE_Y_UP", 0, "-Y", "Negative Y axis"}, @@ -55,7 +55,7 @@ static const EnumPropertyItem io_obj_export_evaluation_mode[] = { {DAG_EVAL_VIEWPORT, "DAG_EVAL_VIEWPORT", 0, - "Viewport (Default)", + "Viewport", "Export objects as they appear in the viewport"}, {0, NULL, 0, NULL, NULL}}; diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c index 429db50f321..e53dda1760e 100644 --- a/source/blender/editors/mesh/editmesh_bevel.c +++ b/source/blender/editors/mesh/editmesh_bevel.c @@ -546,7 +546,7 @@ static void edbm_bevel_mouse_set_value(wmOperator *op, const wmEvent *event) value = value_start[vmode] + value * opdata->scale[vmode]; /* Fake shift-transform... */ - if (event->shift) { + if (event->modifier & KM_SHIFT) { if (opdata->shift_value[vmode] < 0.0f) { opdata->shift_value[vmode] = (vmode == SEGMENTS_VALUE) ? opdata->segments : diff --git a/source/blender/editors/mesh/editmesh_bisect.c b/source/blender/editors/mesh/editmesh_bisect.c index 58bd906101c..7b251b77750 100644 --- a/source/blender/editors/mesh/editmesh_bisect.c +++ b/source/blender/editors/mesh/editmesh_bisect.c @@ -77,14 +77,14 @@ static void mesh_bisect_interactive_calc(bContext *C, const float *co_ref = rv3d->ofs; float co_a_ss[2] = {x_start, y_start}, co_b_ss[2] = {x_end, y_end}, co_delta_ss[2]; float co_a[3], co_b[3]; - const float zfac = ED_view3d_calc_zfac(rv3d, co_ref, NULL); + const float zfac = ED_view3d_calc_zfac(rv3d, co_ref); /* view vector */ ED_view3d_win_to_vector(region, co_a_ss, co_a); /* view delta */ sub_v2_v2v2(co_delta_ss, co_a_ss, co_b_ss); - ED_view3d_win_to_delta(region, co_delta_ss, co_b, zfac); + ED_view3d_win_to_delta(region, co_delta_ss, zfac, co_b); /* cross both to get a normal */ cross_v3_v3v3(plane_no, co_a, co_b); diff --git a/source/blender/editors/mesh/editmesh_knife_project.c b/source/blender/editors/mesh/editmesh_knife_project.c index 84bda411d4a..bce46dd7cf7 100644 --- a/source/blender/editors/mesh/editmesh_knife_project.c +++ b/source/blender/editors/mesh/editmesh_knife_project.c @@ -58,7 +58,7 @@ static LinkNode *knifeproject_poly_from_object(const bContext *C, } me_eval_needs_free = false; } - else if (ELEM(ob->type, OB_FONT, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_FONT, OB_CURVES_LEGACY, OB_SURF)) { Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); me_eval = BKE_mesh_new_nomain_from_curve(ob_eval); me_eval_needs_free = true; diff --git a/source/blender/editors/mesh/editmesh_loopcut.c b/source/blender/editors/mesh/editmesh_loopcut.c index 72844908685..c9fc48c3568 100644 --- a/source/blender/editors/mesh/editmesh_loopcut.c +++ b/source/blender/editors/mesh/editmesh_loopcut.c @@ -581,7 +581,7 @@ static int loopcut_modal(bContext *C, wmOperator *op, const wmEvent *event) handled = true; break; case MOUSEPAN: - if (event->alt == 0) { + if ((event->modifier & KM_ALT) == 0) { cuts += 0.02f * (event->xy[1] - event->prev_xy[1]); if (cuts < 1 && lcd->cuts >= 1) { cuts = 1; @@ -598,7 +598,7 @@ static int loopcut_modal(bContext *C, wmOperator *op, const wmEvent *event) if (event->val == KM_RELEASE) { break; } - if (event->alt == 0) { + if ((event->modifier & KM_ALT) == 0) { cuts += 1; } else { @@ -612,7 +612,7 @@ static int loopcut_modal(bContext *C, wmOperator *op, const wmEvent *event) if (event->val == KM_RELEASE) { break; } - if (event->alt == 0) { + if ((event->modifier & KM_ALT) == 0) { cuts = max_ff(cuts - 1, 1); } else { @@ -755,7 +755,8 @@ void MESH_OT_loopcut(wmOperatorType *ot) RNA_def_property_enum_items(prop, rna_enum_proportional_falloff_curve_only_items); RNA_def_property_enum_default(prop, PROP_INVSQUARE); RNA_def_property_ui_text(prop, "Falloff", "Falloff type the feather"); - RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */ + RNA_def_property_translation_context(prop, + BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */ /* For redo only. */ prop = RNA_def_int(ot->srna, "object_index", -1, -1, INT_MAX, "Object Index", "", 0, INT_MAX); diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index fc1d60fc768..d8fc7a4f9d4 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -1367,10 +1367,10 @@ static int edbm_select_mode_invoke(bContext *C, wmOperator *op, const wmEvent *e /* detecting these options based on shift/ctrl here is weak, but it's done * to make this work when clicking buttons or menus */ if (!RNA_struct_property_is_set(op->ptr, "use_extend")) { - RNA_boolean_set(op->ptr, "use_extend", event->shift); + RNA_boolean_set(op->ptr, "use_extend", event->modifier & KM_SHIFT); } if (!RNA_struct_property_is_set(op->ptr, "use_expand")) { - RNA_boolean_set(op->ptr, "use_expand", event->ctrl); + RNA_boolean_set(op->ptr, "use_expand", event->modifier & KM_CTRL); } return edbm_select_mode_exec(C, op); diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index a1e661cf2ac..2577218d6a9 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -269,7 +269,8 @@ static void mesh_operator_edgering_props(wmOperatorType *ot, RNA_def_property_enum_items(prop, rna_enum_proportional_falloff_curve_only_items); RNA_def_property_enum_default(prop, PROP_SMOOTH); RNA_def_property_ui_text(prop, "Profile Shape", "Shape of the profile"); - RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */ + RNA_def_property_translation_context(prop, + BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */ } static void mesh_operator_edgering_props_get(wmOperator *op, struct EdgeRingOpSubdProps *op_props) @@ -9640,13 +9641,13 @@ static int edbm_smooth_normals_exec(bContext *C, wmOperator *op) float(*smooth_normal)[3] = MEM_callocN(sizeof(*smooth_normal) * lnors_ed_arr->totloop, __func__); - /* This is weird choice of operation, taking all loops of faces of current vertex. - * Could lead to some rather far away loops weighting as much as very close ones + /* NOTE(@mont29): This is weird choice of operation, taking all loops of faces of current + * vertex. Could lead to some rather far away loops weighting as much as very close ones * (topologically speaking), with complex polygons. * Using topological distance here (rather than geometrical one) - * makes sense imho, but would rather go with a more consistent and flexible code, - * we could even add max topological distance to take into account, * and a weighting curve. - * Would do that later though, think for now we can live with that choice. --mont29. */ + * makes sense IMHO, but would rather go with a more consistent and flexible code, + * we could even add max topological distance to take into account, and a weighting curve. + * Would do that later though, think for now we can live with that choice. */ BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata; for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) { l = lnor_ed->loop; diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index 92f2f859965..417fdca4988 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -632,7 +632,7 @@ static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key, Undo return um; } -static void undomesh_to_editmesh(UndoMesh *um, Object *ob, BMEditMesh *em, Key *key) +static void undomesh_to_editmesh(UndoMesh *um, Object *ob, BMEditMesh *em) { BMEditMesh *em_tmp; BMesh *bm; @@ -688,29 +688,6 @@ static void undomesh_to_editmesh(UndoMesh *um, Object *ob, BMEditMesh *em, Key * bm->spacearr_dirty = BM_SPACEARR_DIRTY_ALL; - /* T35170: Restore the active key on the RealMesh. Otherwise 'fake' offset propagation happens - * if the active is a basis for any other. */ - if (key && (key->type == KEY_RELATIVE)) { - /* Since we can't add, remove or reorder keyblocks in editmode, it's safe to assume - * shapenr from restored bmesh and keyblock indices are in sync. */ - const int kb_act_idx = ob->shapenr - 1; - - /* If it is, let's patch the current mesh key block to its restored value. - * Else, the offsets won't be computed and it won't matter. */ - if (BKE_keyblock_is_basis(key, kb_act_idx)) { - KeyBlock *kb_act = BLI_findlink(&key->block, kb_act_idx); - - if (kb_act->totelem != um->me.totvert) { - /* The current mesh has some extra/missing verts compared to the undo, adjust. */ - MEM_SAFE_FREE(kb_act->data); - kb_act->data = MEM_mallocN((size_t)(key->elemsize) * bm->totvert, __func__); - kb_act->totelem = um->me.totvert; - } - - BKE_keyblock_update_from_mesh(&um->me, kb_act); - } - } - ob->shapenr = um->shapenr; MEM_freeN(em_tmp); @@ -858,7 +835,7 @@ static void mesh_undosys_step_decode(struct bContext *C, continue; } BMEditMesh *em = me->edit_mesh; - undomesh_to_editmesh(&elem->data, obedit, em, me->key); + undomesh_to_editmesh(&elem->data, obedit, em); em->needs_flush_to_id = 1; DEG_id_tag_update(&me->id, ID_RECALC_GEOMETRY); } diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.c index 49a5345d048..b3f90880388 100644 --- a/source/blender/editors/mesh/mesh_data.c +++ b/source/blender/editors/mesh/mesh_data.c @@ -21,6 +21,7 @@ #include "BKE_customdata.h" #include "BKE_editmesh.h" #include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" #include "BKE_report.h" #include "DEG_depsgraph.h" @@ -1110,6 +1111,8 @@ static void mesh_add_verts(Mesh *mesh, int len) mesh->vdata = vdata; BKE_mesh_update_customdata_pointers(mesh, false); + BKE_mesh_runtime_clear_cache(mesh); + /* scan the input list and insert the new vertices */ /* set default flags */ @@ -1146,6 +1149,8 @@ static void mesh_add_edges(Mesh *mesh, int len) mesh->edata = edata; BKE_mesh_update_customdata_pointers(mesh, false); /* new edges don't change tessellation */ + BKE_mesh_runtime_clear_cache(mesh); + /* set default flags */ medge = &mesh->medge[mesh->totedge]; for (i = 0; i < len; i++, medge++) { @@ -1174,6 +1179,8 @@ static void mesh_add_loops(Mesh *mesh, int len) CustomData_add_layer(&ldata, CD_MLOOP, CD_CALLOC, NULL, totloop); } + BKE_mesh_runtime_clear_cache(mesh); + CustomData_free(&mesh->ldata, mesh->totloop); mesh->ldata = ldata; BKE_mesh_update_customdata_pointers(mesh, true); @@ -1205,6 +1212,8 @@ static void mesh_add_polys(Mesh *mesh, int len) mesh->pdata = pdata; BKE_mesh_update_customdata_pointers(mesh, true); + BKE_mesh_runtime_clear_cache(mesh); + /* set default flags */ mpoly = &mesh->mpoly[mesh->totpoly]; for (i = 0; i < len; i++, mpoly++) { diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c index 5a8708c84b6..f3782c17845 100644 --- a/source/blender/editors/mesh/meshtools.c +++ b/source/blender/editors/mesh/meshtools.c @@ -412,6 +412,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) * Even though this mesh wont typically have run-time data, the Python API can for e.g. * create loop-triangle cache here, which is confusing when left in the mesh, see: T90798. */ BKE_mesh_runtime_clear_geometry(me); + BKE_mesh_clear_derived_normals(me); /* new material indices and material array */ if (totmat) { diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt index 54db59dc2fa..39ccadd1445 100644 --- a/source/blender/editors/object/CMakeLists.txt +++ b/source/blender/editors/object/CMakeLists.txt @@ -8,6 +8,7 @@ set(INC ../../blentranslation ../../bmesh ../../depsgraph + ../../functions ../../gpencil_modifiers ../../gpu ../../ikplugin @@ -28,7 +29,7 @@ set(INC ) set(SRC - object_add.c + object_add.cc object_bake.c object_bake_api.c object_collection.c diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.cc index c2d811f56dc..7befad3b8d7 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.cc @@ -5,9 +5,9 @@ * \ingroup edobj */ -#include <ctype.h> -#include <stdlib.h> -#include <string.h> +#include <cctype> +#include <cstdlib> +#include <cstring> #include "MEM_guardedalloc.h" @@ -93,6 +93,7 @@ #include "ED_armature.h" #include "ED_curve.h" +#include "ED_curves.h" #include "ED_gpencil.h" #include "ED_mball.h" #include "ED_mesh.h" @@ -122,7 +123,7 @@ const EnumPropertyItem rna_enum_light_type_items[] = { {LA_SUN, "SUN", ICON_LIGHT_SUN, "Sun", "Constant direction parallel ray light source"}, {LA_SPOT, "SPOT", ICON_LIGHT_SPOT, "Spot", "Directional cone light source"}, {LA_AREA, "AREA", ICON_LIGHT_AREA, "Area", "Directional area light source"}, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; /* copy from rna_object_force.c */ @@ -140,7 +141,7 @@ static const EnumPropertyItem field_type_items[] = { {PFIELD_TURBULENCE, "TURBULENCE", ICON_FORCE_TURBULENCE, "Turbulence", ""}, {PFIELD_DRAG, "DRAG", ICON_FORCE_DRAG, "Drag", ""}, {PFIELD_FLUIDFLOW, "FLUID", ICON_FORCE_FLUIDFLOW, "Fluid Flow", ""}, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; static EnumPropertyItem lightprobe_type_items[] = { @@ -159,7 +160,7 @@ static EnumPropertyItem lightprobe_type_items[] = { ICON_LIGHTPROBE_GRID, "Irradiance Volume", "Irradiance probe to capture diffuse indirect lighting"}, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; enum { @@ -172,7 +173,7 @@ static const EnumPropertyItem align_options[] = { {ALIGN_WORLD, "WORLD", 0, "World", "Align the new object to the world"}, {ALIGN_VIEW, "VIEW", 0, "View", "Align the new object to the view"}, {ALIGN_CURSOR, "CURSOR", 0, "3D Cursor", "Use the 3D cursor orientation for the new object"}, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; /** \} */ @@ -198,7 +199,7 @@ static void object_add_drop_xy_props(wmOperatorType *ot) "X-coordinate (screen space) to place the new object under", INT_MIN, INT_MAX); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); prop = RNA_def_int(ot->srna, "drop_y", 0, @@ -208,7 +209,7 @@ static void object_add_drop_xy_props(wmOperatorType *ot) "Y-coordinate (screen space) to place the new object under", INT_MIN, INT_MAX); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); } static bool object_add_drop_xy_is_set(const wmOperator *op) @@ -343,13 +344,13 @@ float ED_object_new_primitive_matrix(bContext *C, invert_m3_m3(imat, mat); mul_m3_v3(imat, r_primmat[3]); - if (scale != NULL) { + if (scale != nullptr) { rescale_m4(r_primmat, scale); } { - const float dia = v3d ? ED_view3d_grid_scale(scene, v3d, NULL) : - ED_scene_grid_scale(scene, NULL); + const float dia = v3d ? ED_view3d_grid_scale(scene, v3d, nullptr) : + ED_scene_grid_scale(scene, nullptr); return dia; } @@ -393,20 +394,20 @@ void ED_object_add_generic_props(wmOperatorType *ot, bool do_editmode) if (do_editmode) { prop = RNA_def_boolean(ot->srna, "enter_editmode", - 0, + false, "Enter Edit Mode", "Enter edit mode when adding this object"); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); } /* NOTE: this property gets hidden for add-camera operator. */ prop = RNA_def_enum( ot->srna, "align", align_options, ALIGN_WORLD, "Align", "The alignment of the new object"); - RNA_def_property_update_runtime(prop, view_align_update); + RNA_def_property_update_runtime(prop, (void *)view_align_update); prop = RNA_def_float_vector_xyz(ot->srna, "location", 3, - NULL, + nullptr, -OBJECT_ADD_SIZE_MAXF, OBJECT_ADD_SIZE_MAXF, "Location", @@ -417,7 +418,7 @@ void ED_object_add_generic_props(wmOperatorType *ot, bool do_editmode) prop = RNA_def_float_rotation(ot->srna, "rotation", 3, - NULL, + nullptr, -OBJECT_ADD_SIZE_MAXF, OBJECT_ADD_SIZE_MAXF, "Rotation", @@ -429,14 +430,14 @@ void ED_object_add_generic_props(wmOperatorType *ot, bool do_editmode) prop = RNA_def_float_vector_xyz(ot->srna, "scale", 3, - NULL, + nullptr, -OBJECT_ADD_SIZE_MAXF, OBJECT_ADD_SIZE_MAXF, "Scale", "Scale for the newly added object", -1000.0f, 1000.0f); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); } void ED_object_add_mesh_props(wmOperatorType *ot) @@ -461,11 +462,11 @@ bool ED_object_add_generic_get_opts(bContext *C, r_enter_editmode = &_enter_editmode; } /* Only to ensure the value is _always_ set. - * Typically the property will exist when the argument is non-NULL. */ + * Typically the property will exist when the argument is non-nullptr. */ *r_enter_editmode = false; PropertyRNA *prop = RNA_struct_find_property(op->ptr, "enter_editmode"); - if (prop != NULL) { + if (prop != nullptr) { if (RNA_property_is_set(op->ptr, prop) && r_enter_editmode) { *r_enter_editmode = RNA_property_boolean_get(op->ptr, prop); } @@ -571,7 +572,7 @@ bool ED_object_add_generic_get_opts(bContext *C, copy_v3_fl(r_scale, 1.0f); PropertyRNA *prop = RNA_struct_find_property(op->ptr, "scale"); - if (prop != NULL) { + if (prop != nullptr) { if (RNA_property_is_set(op->ptr, prop)) { RNA_property_float_get_array(op->ptr, prop, r_scale); } @@ -600,19 +601,19 @@ Object *ED_object_add_type_with_obdata(bContext *C, { Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer); - if (obedit != NULL) { + if (obedit != nullptr) { ED_object_editmode_exit_ex(bmain, scene, obedit, EM_FREEDATA); } } /* deselects all, sets active object */ Object *ob; - if (obdata != NULL) { + if (obdata != nullptr) { BLI_assert(type == BKE_object_obdata_to_type(obdata)); ob = BKE_object_add_for_data(bmain, view_layer, type, name, obdata, true); const short *materials_len_p = BKE_id_material_len_p(obdata); if (materials_len_p && *materials_len_p > 0) { - BKE_object_materials_test(bmain, ob, ob->data); + BKE_object_materials_test(bmain, ob, static_cast<ID *>(ob->data)); } } else { @@ -630,7 +631,7 @@ Object *ED_object_add_type_with_obdata(bContext *C, */ DEG_id_type_tag(bmain, ID_OB); DEG_relations_tag_update(bmain); - if (ob->data != NULL) { + if (ob->data != nullptr) { DEG_id_tag_update_ex(bmain, (ID *)ob->data, ID_RECALC_EDITORS); } @@ -657,7 +658,7 @@ Object *ED_object_add_type(bContext *C, const ushort local_view_bits) { return ED_object_add_type_with_obdata( - C, type, name, loc, rot, enter_editmode, local_view_bits, NULL); + C, type, name, loc, rot, enter_editmode, local_view_bits, nullptr); } /* for object add operator */ @@ -668,12 +669,12 @@ static int object_add_exec(bContext *C, wmOperator *op) float loc[3], rot[3], radius; WM_operator_view3d_unit_defaults(C, op); if (!ED_object_add_generic_get_opts( - C, op, 'Z', loc, rot, NULL, &enter_editmode, &local_view_bits, NULL)) { + C, op, 'Z', loc, rot, nullptr, &enter_editmode, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } radius = RNA_float_get(op->ptr, "radius"); Object *ob = ED_object_add_type( - C, RNA_enum_get(op->ptr, "type"), NULL, loc, rot, enter_editmode, local_view_bits); + C, RNA_enum_get(op->ptr, "type"), nullptr, loc, rot, enter_editmode, local_view_bits); if (ob->type == OB_LATTICE) { /* lattice is a special case! @@ -737,7 +738,7 @@ static int lightprobe_add_exec(bContext *C, wmOperator *op) float loc[3], rot[3]; WM_operator_view3d_unit_defaults(C, op); if (!ED_object_add_generic_get_opts( - C, op, 'Z', loc, rot, NULL, &enter_editmode, &local_view_bits, NULL)) { + C, op, 'Z', loc, rot, nullptr, &enter_editmode, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } int type = RNA_enum_get(op->ptr, "type"); @@ -829,26 +830,25 @@ static int effector_add_exec(bContext *C, wmOperator *op) float loc[3], rot[3]; WM_operator_view3d_unit_defaults(C, op); if (!ED_object_add_generic_get_opts( - C, op, 'Z', loc, rot, NULL, &enter_editmode, &local_view_bits, NULL)) { + C, op, 'Z', loc, rot, nullptr, &enter_editmode, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } - int type = RNA_enum_get(op->ptr, "type"); + const ePFieldType type = static_cast<ePFieldType>(RNA_enum_get(op->ptr, "type")); float dia = RNA_float_get(op->ptr, "radius"); Object *ob; if (type == PFIELD_GUIDE) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); - Curve *cu; ob = ED_object_add_type( - C, OB_CURVE, get_effector_defname(type), loc, rot, false, local_view_bits); + C, OB_CURVES_LEGACY, get_effector_defname(type), loc, rot, false, local_view_bits); - cu = ob->data; + Curve *cu = static_cast<Curve *>(ob->data); cu->flag |= CU_PATH | CU_3D; ED_object_editmode_enter_ex(bmain, scene, ob, 0); float mat[4][4]; - ED_object_new_primitive_matrix(C, ob, loc, rot, NULL, mat); + ED_object_new_primitive_matrix(C, ob, loc, rot, nullptr, mat); mul_mat3_m4_fl(mat, dia); BLI_addtail(&cu->editnurb->nurbs, ED_curve_add_nurbs_primitive(C, ob, mat, CU_NURBS | CU_PRIM_PATH, 1)); @@ -909,22 +909,23 @@ static int object_camera_add_exec(bContext *C, wmOperator *op) bool enter_editmode; float loc[3], rot[3]; if (!ED_object_add_generic_get_opts( - C, op, 'Z', loc, rot, NULL, &enter_editmode, &local_view_bits, NULL)) { + C, op, 'Z', loc, rot, nullptr, &enter_editmode, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } - Object *ob = ED_object_add_type(C, OB_CAMERA, NULL, loc, rot, false, local_view_bits); + Object *ob = ED_object_add_type(C, OB_CAMERA, nullptr, loc, rot, false, local_view_bits); if (v3d) { - if (v3d->camera == NULL) { + if (v3d->camera == nullptr) { v3d->camera = ob; } - if (v3d->scenelock && scene->camera == NULL) { + if (v3d->scenelock && scene->camera == nullptr) { scene->camera = ob; } } - Camera *cam = ob->data; - cam->drawsize = v3d ? ED_view3d_grid_scale(scene, v3d, NULL) : ED_scene_grid_scale(scene, NULL); + Camera *cam = static_cast<Camera *>(ob->data); + cam->drawsize = v3d ? ED_view3d_grid_scale(scene, v3d, nullptr) : + ED_scene_grid_scale(scene, nullptr); return OPERATOR_FINISHED; } @@ -969,14 +970,14 @@ static int object_metaball_add_exec(bContext *C, wmOperator *op) float loc[3], rot[3]; WM_operator_view3d_unit_defaults(C, op); if (!ED_object_add_generic_get_opts( - C, op, 'Z', loc, rot, NULL, &enter_editmode, &local_view_bits, NULL)) { + C, op, 'Z', loc, rot, nullptr, &enter_editmode, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } bool newob = false; Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer); - if (obedit == NULL || obedit->type != OB_MBALL) { - obedit = ED_object_add_type(C, OB_MBALL, NULL, loc, rot, true, local_view_bits); + if (obedit == nullptr || obedit->type != OB_MBALL) { + obedit = ED_object_add_type(C, OB_MBALL, nullptr, loc, rot, true, local_view_bits); newob = true; } else { @@ -984,7 +985,7 @@ static int object_metaball_add_exec(bContext *C, wmOperator *op) } float mat[4][4]; - ED_object_new_primitive_matrix(C, obedit, loc, rot, NULL, mat); + ED_object_new_primitive_matrix(C, obedit, loc, rot, nullptr, mat); /* Halving here is done to account for constant values from #BKE_mball_element_add. * While the default radius of the resulting meta element is 2, * we want to pass in 1 so other values such as resolution are scaled by 1.0. */ @@ -1040,14 +1041,14 @@ static int object_add_text_exec(bContext *C, wmOperator *op) WM_operator_view3d_unit_defaults(C, op); if (!ED_object_add_generic_get_opts( - C, op, 'Z', loc, rot, NULL, &enter_editmode, &local_view_bits, NULL)) { + C, op, 'Z', loc, rot, nullptr, &enter_editmode, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } if (obedit && obedit->type == OB_FONT) { return OPERATOR_CANCELLED; } - obedit = ED_object_add_type(C, OB_FONT, NULL, loc, rot, enter_editmode, local_view_bits); + obedit = ED_object_add_type(C, OB_FONT, nullptr, loc, rot, enter_editmode, local_view_bits); BKE_object_obdata_size_init(obedit, RNA_float_get(op->ptr, "radius")); return OPERATOR_FINISHED; @@ -1094,11 +1095,11 @@ static int object_armature_add_exec(bContext *C, wmOperator *op) WM_operator_view3d_unit_defaults(C, op); if (!ED_object_add_generic_get_opts( - C, op, 'Z', loc, rot, NULL, &enter_editmode, &local_view_bits, NULL)) { + C, op, 'Z', loc, rot, nullptr, &enter_editmode, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } - if ((obedit == NULL) || (obedit->type != OB_ARMATURE)) { - obedit = ED_object_add_type(C, OB_ARMATURE, NULL, loc, rot, true, local_view_bits); + if ((obedit == nullptr) || (obedit->type != OB_ARMATURE)) { + obedit = ED_object_add_type(C, OB_ARMATURE, nullptr, loc, rot, true, local_view_bits); ED_object_editmode_enter_ex(bmain, scene, obedit, 0); newob = true; } @@ -1106,7 +1107,7 @@ static int object_armature_add_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY); } - if (obedit == NULL) { + if (obedit == nullptr) { BKE_report(op->reports, RPT_ERROR, "Cannot create editmode armature"); return OPERATOR_CANCELLED; } @@ -1155,10 +1156,11 @@ static int object_empty_add_exec(bContext *C, wmOperator *op) float loc[3], rot[3]; WM_operator_view3d_unit_defaults(C, op); - if (!ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, NULL, NULL, &local_view_bits, NULL)) { + if (!ED_object_add_generic_get_opts( + C, op, 'Z', loc, rot, nullptr, nullptr, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } - ob = ED_object_add_type(C, OB_EMPTY, NULL, loc, rot, false, local_view_bits); + ob = ED_object_add_type(C, OB_EMPTY, nullptr, loc, rot, false, local_view_bits); BKE_object_empty_draw_type_set(ob, type); BKE_object_obdata_size_init(ob, RNA_float_get(op->ptr, "radius")); @@ -1192,7 +1194,7 @@ static int empty_drop_named_image_invoke(bContext *C, wmOperator *op, const wmEv { Scene *scene = CTX_data_scene(C); - Image *ima = NULL; + Image *ima = nullptr; ima = (Image *)WM_operator_drop_load_path(C, op, ID_IM); if (!ima) { @@ -1201,7 +1203,7 @@ static int empty_drop_named_image_invoke(bContext *C, wmOperator *op, const wmEv /* handled below */ id_us_min(&ima->id); - Object *ob = NULL; + Object *ob = nullptr; Object *ob_cursor = ED_view3d_give_object_under_cursor(C, event->mval); /* either change empty under cursor or create a new empty */ @@ -1216,10 +1218,10 @@ static int empty_drop_named_image_invoke(bContext *C, wmOperator *op, const wmEv float rot[3]; if (!ED_object_add_generic_get_opts( - C, op, 'Z', NULL, rot, NULL, NULL, &local_view_bits, NULL)) { + C, op, 'Z', nullptr, rot, nullptr, nullptr, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } - ob = ED_object_add_type(C, OB_EMPTY, NULL, NULL, rot, false, local_view_bits); + ob = ED_object_add_type(C, OB_EMPTY, nullptr, nullptr, rot, false, local_view_bits); ED_object_location_from_view(C, ob->loc); ED_view3d_cursor3d_position(C, event->mval, false, ob->loc); @@ -1229,9 +1231,9 @@ static int empty_drop_named_image_invoke(bContext *C, wmOperator *op, const wmEv BKE_object_empty_draw_type_set(ob, OB_EMPTY_IMAGE); - id_us_min(ob->data); + id_us_min(static_cast<ID *>(ob->data)); ob->data = ima; - id_us_plus(ob->data); + id_us_plus(static_cast<ID *>(ob->data)); return OPERATOR_FINISHED; } @@ -1253,16 +1255,17 @@ void OBJECT_OT_drop_named_image(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - prop = RNA_def_string(ot->srna, "filepath", NULL, FILE_MAX, "Filepath", "Path to image file"); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_string(ot->srna, "filepath", nullptr, FILE_MAX, "Filepath", "Path to image file"); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); RNA_def_boolean(ot->srna, "relative_path", true, "Relative Path", "Select the file relative to the blend file"); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); - prop = RNA_def_string(ot->srna, "name", NULL, MAX_ID_NAME - 2, "Name", "Image name to assign"); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); + prop = RNA_def_string( + ot->srna, "name", nullptr, MAX_ID_NAME - 2, "Name", "Image name to assign"); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); ED_object_add_generic_props(ot, false); } @@ -1277,7 +1280,7 @@ static bool object_gpencil_add_poll(bContext *C) Scene *scene = CTX_data_scene(C); Object *obact = CTX_data_active_object(C); - if ((scene == NULL) || (ID_IS_LINKED(scene))) { + if ((scene == nullptr) || (ID_IS_LINKED(scene))) { return false; } @@ -1293,7 +1296,7 @@ static bool object_gpencil_add_poll(bContext *C) static int object_gpencil_add_exec(bContext *C, wmOperator *op) { Object *ob = CTX_data_active_object(C), *ob_orig = ob; - bGPdata *gpd = (ob && (ob->type == OB_GPENCIL)) ? ob->data : NULL; + bGPdata *gpd = (ob && (ob->type == OB_GPENCIL)) ? static_cast<bGPdata *>(ob->data) : nullptr; const int type = RNA_enum_get(op->ptr, "type"); const bool use_in_front = RNA_boolean_get(op->ptr, "use_in_front"); @@ -1307,12 +1310,13 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) /* NOTE: We use 'Y' here (not 'Z'), as. */ WM_operator_view3d_unit_defaults(C, op); - if (!ED_object_add_generic_get_opts(C, op, 'Y', loc, rot, NULL, NULL, &local_view_bits, NULL)) { + if (!ED_object_add_generic_get_opts( + C, op, 'Y', loc, rot, nullptr, nullptr, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } /* Add new object if not currently editing a GP object. */ - if ((gpd == NULL) || (GPENCIL_ANY_MODE(gpd) == false)) { - const char *ob_name = NULL; + if ((gpd == nullptr) || (GPENCIL_ANY_MODE(gpd) == false)) { + const char *ob_name = nullptr; switch (type) { case GP_EMPTY: { ob_name = "GPencil"; @@ -1338,12 +1342,12 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) } ob = ED_object_add_type(C, OB_GPENCIL, ob_name, loc, rot, true, local_view_bits); - gpd = ob->data; + gpd = static_cast<bGPdata *>(ob->data); newob = true; } else { DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_ADDED, NULL); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_ADDED, nullptr); } /* create relevant geometry */ @@ -1351,7 +1355,7 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) case GP_EMPTY: { float mat[4][4]; - ED_object_new_primitive_matrix(C, ob, loc, rot, NULL, mat); + ED_object_new_primitive_matrix(C, ob, loc, rot, nullptr, mat); ED_gpencil_create_blank(C, ob, mat); break; } @@ -1389,7 +1393,7 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) ED_gpencil_create_lineart(C, ob); - gpd = ob->data; + gpd = static_cast<bGPdata *>(ob->data); /* Add Line Art modifier */ LineartGpencilModifierData *md = (LineartGpencilModifierData *)BKE_gpencil_modifier_new( @@ -1458,21 +1462,21 @@ static void object_add_ui(bContext *UNUSED(C), wmOperator *op) uiLayoutSetPropSep(layout, true); - uiItemR(layout, op->ptr, "radius", 0, NULL, ICON_NONE); - uiItemR(layout, op->ptr, "align", 0, NULL, ICON_NONE); - uiItemR(layout, op->ptr, "location", 0, NULL, ICON_NONE); - uiItemR(layout, op->ptr, "rotation", 0, NULL, ICON_NONE); - uiItemR(layout, op->ptr, "type", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "radius", 0, nullptr, ICON_NONE); + uiItemR(layout, op->ptr, "align", 0, nullptr, ICON_NONE); + uiItemR(layout, op->ptr, "location", 0, nullptr, ICON_NONE); + uiItemR(layout, op->ptr, "rotation", 0, nullptr, ICON_NONE); + uiItemR(layout, op->ptr, "type", 0, nullptr, ICON_NONE); int type = RNA_enum_get(op->ptr, "type"); if (ELEM(type, GP_LRT_COLLECTION, GP_LRT_OBJECT, GP_LRT_SCENE)) { - uiItemR(layout, op->ptr, "use_lights", 0, NULL, ICON_NONE); - uiItemR(layout, op->ptr, "use_in_front", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "use_lights", 0, nullptr, ICON_NONE); + uiItemR(layout, op->ptr, "use_in_front", 0, nullptr, ICON_NONE); bool in_front = RNA_boolean_get(op->ptr, "use_in_front"); uiLayout *col = uiLayoutColumn(layout, false); uiLayoutSetActive(col, !in_front); - uiItemR(col, op->ptr, "stroke_depth_offset", 0, NULL, ICON_NONE); - uiItemR(col, op->ptr, "stroke_depth_order", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "stroke_depth_offset", 0, nullptr, ICON_NONE); + uiItemR(col, op->ptr, "stroke_depth_order", 0, nullptr, ICON_NONE); } } @@ -1483,7 +1487,7 @@ static EnumPropertyItem rna_enum_gpencil_add_stroke_depth_order_items[] = { "2D Layers", "Display strokes using grease pencil layers to define order"}, {GP_DRAWMODE_3D, "3D", 0, "3D Location", "Display strokes using real 3D position in 3D space"}, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; void OBJECT_OT_gpencil_add(wmOperatorType *ot) @@ -1565,7 +1569,8 @@ static int object_light_add_exec(bContext *C, wmOperator *op) float loc[3], rot[3]; WM_operator_view3d_unit_defaults(C, op); - if (!ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, NULL, NULL, &local_view_bits, NULL)) { + if (!ED_object_add_generic_get_opts( + C, op, 'Z', loc, rot, nullptr, nullptr, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } ob = ED_object_add_type(C, OB_LAMP, get_light_defname(type), loc, rot, false, local_view_bits); @@ -1648,7 +1653,8 @@ static int collection_instance_add_exec(bContext *C, wmOperator *op) update_location_if_necessary = true; } else { - collection = BLI_findlink(&bmain->collections, RNA_enum_get(op->ptr, "collection")); + collection = static_cast<Collection *>( + BLI_findlink(&bmain->collections, RNA_enum_get(op->ptr, "collection"))); } if (update_location_if_necessary) { @@ -1660,11 +1666,12 @@ static int collection_instance_add_exec(bContext *C, wmOperator *op) } } - if (collection == NULL) { + if (collection == nullptr) { return OPERATOR_CANCELLED; } - if (!ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, NULL, NULL, &local_view_bits, NULL)) { + if (!ED_object_add_generic_get_opts( + C, op, 'Z', loc, rot, nullptr, nullptr, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } @@ -1735,7 +1742,7 @@ void OBJECT_OT_collection_instance_add(wmOperatorType *ot) "Session UUID of the collection to add", INT32_MIN, INT32_MAX); - RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_SKIP_SAVE | PROP_HIDDEN)); object_add_drop_xy_props(ot); } @@ -1751,7 +1758,7 @@ void OBJECT_OT_collection_instance_add(wmOperatorType *ot) static int object_data_instance_add_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); - ID *id = NULL; + ID *id = nullptr; ushort local_view_bits; float loc[3], rot[3]; @@ -1767,7 +1774,7 @@ static int object_data_instance_add_exec(bContext *C, wmOperator *op) char name[MAX_ID_NAME - 2]; RNA_property_string_get(op->ptr, prop_name, name); id = BKE_libblock_find_name(bmain, id_type, name); - if (id == NULL) { + if (id == nullptr) { return OPERATOR_CANCELLED; } const int object_type = BKE_object_obdata_to_type(id); @@ -1782,7 +1789,8 @@ static int object_data_instance_add_exec(bContext *C, wmOperator *op) RNA_property_float_set_array(op->ptr, prop_location, loc); } - if (!ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, NULL, NULL, &local_view_bits, NULL)) { + if (!ED_object_add_generic_get_opts( + C, op, 'Z', loc, rot, nullptr, nullptr, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } @@ -1829,10 +1837,11 @@ static int object_speaker_add_exec(bContext *C, wmOperator *op) ushort local_view_bits; float loc[3], rot[3]; - if (!ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, NULL, NULL, &local_view_bits, NULL)) { + if (!ED_object_add_generic_get_opts( + C, op, 'Z', loc, rot, nullptr, nullptr, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } - Object *ob = ED_object_add_type(C, OB_SPEAKER, NULL, loc, rot, false, local_view_bits); + Object *ob = ED_object_add_type(C, OB_SPEAKER, nullptr, loc, rot, false, local_view_bits); const bool is_liboverride = ID_IS_OVERRIDE_LIBRARY(ob); /* To make it easier to start using this immediately in NLA, a default sound clip is created @@ -1840,8 +1849,8 @@ static int object_speaker_add_exec(bContext *C, wmOperator *op) { /* create new data for NLA hierarchy */ AnimData *adt = BKE_animdata_ensure_id(&ob->id); - NlaTrack *nlt = BKE_nlatrack_add(adt, NULL, is_liboverride); - NlaStrip *strip = BKE_nla_add_soundstrip(bmain, scene, ob->data); + NlaTrack *nlt = BKE_nlatrack_add(adt, nullptr, is_liboverride); + NlaStrip *strip = BKE_nla_add_soundstrip(bmain, scene, static_cast<Speaker *>(ob->data)); strip->start = CFRA; strip->end += strip->start; @@ -1852,7 +1861,7 @@ static int object_speaker_add_exec(bContext *C, wmOperator *op) BLI_strncpy(nlt->name, DATA_("SoundTrack"), sizeof(nlt->name)); BKE_nlastrip_validate_name(adt, strip); - WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_ADDED, NULL); + WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_ADDED, nullptr); } return OPERATOR_FINISHED; @@ -1891,15 +1900,21 @@ static bool object_hair_curves_add_poll(bContext *C) static int object_hair_curves_add_exec(bContext *C, wmOperator *op) { + using namespace blender; + ushort local_view_bits; float loc[3], rot[3]; - if (!ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, NULL, NULL, &local_view_bits, NULL)) { + if (!ED_object_add_generic_get_opts( + C, op, 'Z', loc, rot, nullptr, nullptr, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } - Object *object = ED_object_add_type(C, OB_CURVES, NULL, loc, rot, false, local_view_bits); + Object *object = ED_object_add_type(C, OB_CURVES, nullptr, loc, rot, false, local_view_bits); object->dtx |= OB_DRAWBOUNDOX; /* TODO: remove once there is actual drawing. */ + Curves *curves_id = static_cast<Curves *>(object->data); + bke::CurvesGeometry::wrap(curves_id->geometry) = ed::curves::primitive_random_sphere(500, 8); + return OPERATOR_FINISHED; } @@ -1938,11 +1953,12 @@ static int object_pointcloud_add_exec(bContext *C, wmOperator *op) { ushort local_view_bits; float loc[3], rot[3]; - if (!ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, NULL, NULL, &local_view_bits, NULL)) { + if (!ED_object_add_generic_get_opts( + C, op, 'Z', loc, rot, nullptr, nullptr, &local_view_bits, nullptr)) { return OPERATOR_CANCELLED; } - Object *object = ED_object_add_type(C, OB_POINTCLOUD, NULL, loc, rot, false, local_view_bits); + Object *object = ED_object_add_type(C, OB_POINTCLOUD, nullptr, loc, rot, false, local_view_bits); object->dtx |= OB_DRAWBOUNDOX; /* TODO: remove once there is actual drawing. */ return OPERATOR_FINISHED; @@ -2062,11 +2078,11 @@ static int object_delete_exec(bContext *C, wmOperator *op) /* FIXME: this will also remove parent from grease pencil from other scenes. */ /* Remove from Grease Pencil parent */ - for (bGPdata *gpd = bmain->gpencils.first; gpd; gpd = gpd->id.next) { + LISTBASE_FOREACH (bGPdata *, gpd, &bmain->gpencils) { LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { - if (gpl->parent != NULL) { + if (gpl->parent != nullptr) { if (gpl->parent == ob) { - gpl->parent = NULL; + gpl->parent = nullptr; } } } @@ -2123,8 +2139,8 @@ void OBJECT_OT_delete(wmOperatorType *ot) PropertyRNA *prop; prop = RNA_def_boolean( - ot->srna, "use_global", 0, "Delete Globally", "Remove object from all scenes"); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + ot->srna, "use_global", false, "Delete Globally", "Remove object from all scenes"); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); WM_operator_properties_confirm_or_exec(ot); } @@ -2188,7 +2204,7 @@ static void copy_object_set_idnew(bContext *C) */ static uint dupliobject_hash(const void *ptr) { - const DupliObject *dob = ptr; + const DupliObject *dob = static_cast<const DupliObject *>(ptr); uint hash = BLI_ghashutil_ptrhash(dob->ob); if (dob->type == OB_DUPLICOLLECTION) { @@ -2210,7 +2226,7 @@ static uint dupliobject_hash(const void *ptr) */ static uint dupliobject_instancer_hash(const void *ptr) { - const DupliObject *dob = ptr; + const DupliObject *dob = static_cast<const DupliObject *>(ptr); uint hash = BLI_ghashutil_inthash(dob->persistent_id[0]); for (int i = 1; (i < MAX_DUPLI_RECUR) && dob->persistent_id[i] != INT_MAX; i++) { hash ^= (dob->persistent_id[i] ^ i); @@ -2221,8 +2237,8 @@ static uint dupliobject_instancer_hash(const void *ptr) /* Compare function that matches dupliobject_hash */ static bool dupliobject_cmp(const void *a_, const void *b_) { - const DupliObject *a = a_; - const DupliObject *b = b_; + const DupliObject *a = static_cast<const DupliObject *>(a_); + const DupliObject *b = static_cast<const DupliObject *>(b_); if (a->ob != b->ob) { return true; @@ -2255,8 +2271,8 @@ static bool dupliobject_cmp(const void *a_, const void *b_) /* Compare function that matches dupliobject_instancer_hash. */ static bool dupliobject_instancer_cmp(const void *a_, const void *b_) { - const DupliObject *a = a_; - const DupliObject *b = b_; + const DupliObject *a = static_cast<const DupliObject *>(a_); + const DupliObject *b = static_cast<const DupliObject *>(b_); for (int i = 0; (i < MAX_DUPLI_RECUR); i++) { if (a->persistent_id[i] != b->persistent_id[i]) { @@ -2280,7 +2296,7 @@ static void make_object_duplilist_real(bContext *C, { Main *bmain = CTX_data_main(C); ViewLayer *view_layer = CTX_data_view_layer(C); - GHash *parent_gh = NULL, *instancer_gh = NULL; + GHash *parent_gh = nullptr, *instancer_gh = nullptr; Object *object_eval = DEG_get_evaluated_object(depsgraph, base->object); @@ -2308,19 +2324,19 @@ static void make_object_duplilist_real(bContext *C, LISTBASE_FOREACH (DupliObject *, dob, lb_duplis) { Object *ob_src = DEG_get_original_object(dob->ob); - Object *ob_dst = ID_NEW_SET(ob_src, BKE_id_copy(bmain, &ob_src->id)); + Object *ob_dst = static_cast<Object *>(ID_NEW_SET(ob_src, BKE_id_copy(bmain, &ob_src->id))); id_us_min(&ob_dst->id); /* font duplis can have a totcol without material, we get them from parent * should be implemented better... */ - if (ob_dst->mat == NULL) { + if (ob_dst->mat == nullptr) { ob_dst->totcol = 0; } BKE_collection_object_add_from(bmain, scene, base->object, ob_dst); Base *base_dst = BKE_view_layer_base_find(view_layer, ob_dst); - BLI_assert(base_dst != NULL); + BLI_assert(base_dst != nullptr); ED_object_base_select(base_dst, BA_SELECT); DEG_id_tag_update(&ob_dst->id, ID_RECALC_SELECT); @@ -2329,17 +2345,17 @@ static void make_object_duplilist_real(bContext *C, /* make sure apply works */ BKE_animdata_free(&ob_dst->id, true); - ob_dst->adt = NULL; + ob_dst->adt = nullptr; - ob_dst->parent = NULL; + ob_dst->parent = nullptr; BKE_constraints_free(&ob_dst->constraints); - ob_dst->runtime.curve_cache = NULL; + ob_dst->runtime.curve_cache = nullptr; const bool is_dupli_instancer = (ob_dst->transflag & OB_DUPLI) != 0; ob_dst->transflag &= ~OB_DUPLI; /* Remove instantiated collection, it's annoying to keep it here * (and get potentially a lot of usages of it then...). */ id_us_min((ID *)ob_dst->instance_collection); - ob_dst->instance_collection = NULL; + ob_dst->instance_collection = nullptr; copy_m4_m4(ob_dst->obmat, dob->mat); BKE_object_apply_mat4(ob_dst, ob_dst->obmat, false, false); @@ -2365,7 +2381,7 @@ static void make_object_duplilist_real(bContext *C, LISTBASE_FOREACH (DupliObject *, dob, lb_duplis) { Object *ob_src = dob->ob; - Object *ob_dst = BLI_ghash_lookup(dupli_gh, dob); + Object *ob_dst = static_cast<Object *>(BLI_ghash_lookup(dupli_gh, dob)); /* Remap new object to itself, and clear again newid pointer of orig object. */ BKE_libblock_relink_to_newid(bmain, &ob_dst->id, 0); @@ -2375,7 +2391,7 @@ static void make_object_duplilist_real(bContext *C, if (use_hierarchy) { /* original parents */ Object *ob_src_par = ob_src->parent; - Object *ob_dst_par = NULL; + Object *ob_dst_par = nullptr; /* find parent that was also made real */ if (ob_src_par) { @@ -2392,7 +2408,7 @@ static void make_object_duplilist_real(bContext *C, else { dob_key.persistent_id[0] = dob->persistent_id[0]; } - ob_dst_par = BLI_ghash_lookup(parent_gh, &dob_key); + ob_dst_par = static_cast<Object *>(BLI_ghash_lookup(parent_gh, &dob_key)); } if (ob_dst_par) { @@ -2408,10 +2424,10 @@ static void make_object_duplilist_real(bContext *C, ob_dst->parent = ob_dst_par; } } - if (use_base_parent && ob_dst->parent == NULL) { - Object *ob_dst_par = NULL; + if (use_base_parent && ob_dst->parent == nullptr) { + Object *ob_dst_par = nullptr; - if (instancer_gh != NULL) { + if (instancer_gh != nullptr) { /* OK to keep most of the members uninitialized, * they won't be read, this is simply for a hash lookup. */ DupliObject dob_key; @@ -2421,10 +2437,10 @@ static void make_object_duplilist_real(bContext *C, memcpy(&dob_key.persistent_id[0], &dob->persistent_id[1], sizeof(dob_key.persistent_id[0]) * (MAX_DUPLI_RECUR - 1)); - ob_dst_par = BLI_ghash_lookup(instancer_gh, &dob_key); + ob_dst_par = static_cast<Object *>(BLI_ghash_lookup(instancer_gh, &dob_key)); } - if (ob_dst_par == NULL) { + if (ob_dst_par == nullptr) { /* Default to parenting to root object... * Always the case when use_hierarchy is false. */ ob_dst_par = base->object; @@ -2445,18 +2461,18 @@ static void make_object_duplilist_real(bContext *C, } if (base->object->transflag & OB_DUPLICOLLECTION && base->object->instance_collection) { - base->object->instance_collection = NULL; + base->object->instance_collection = nullptr; } ED_object_base_select(base, BA_DESELECT); DEG_id_tag_update(&base->object->id, ID_RECALC_SELECT); - BLI_ghash_free(dupli_gh, NULL, NULL); + BLI_ghash_free(dupli_gh, nullptr, nullptr); if (parent_gh) { - BLI_ghash_free(parent_gh, NULL, NULL); + BLI_ghash_free(parent_gh, nullptr, nullptr); } if (instancer_gh) { - BLI_ghash_free(instancer_gh, NULL, NULL); + BLI_ghash_free(instancer_gh, nullptr, nullptr); } free_object_duplilist(lb_duplis); @@ -2488,7 +2504,7 @@ static int object_duplicates_make_real_exec(bContext *C, wmOperator *op) DEG_relations_tag_update(bmain); WM_event_add_notifier(C, NC_SCENE, scene); - WM_main_add_notifier(NC_OBJECT | ND_DRAW, NULL); + WM_main_add_notifier(NC_OBJECT | ND_DRAW, nullptr); ED_outliner_select_sync_from_object_tag(C); return OPERATOR_FINISHED; @@ -2511,11 +2527,11 @@ void OBJECT_OT_duplicates_make_real(wmOperatorType *ot) RNA_def_boolean(ot->srna, "use_base_parent", - 0, + false, "Parent", "Parent newly created objects to the original instancer"); RNA_def_boolean( - ot->srna, "use_hierarchy", 0, "Keep Hierarchy", "Maintain parent child relationships"); + ot->srna, "use_hierarchy", false, "Keep Hierarchy", "Maintain parent child relationships"); } /** \} */ @@ -2525,7 +2541,11 @@ void OBJECT_OT_duplicates_make_real(wmOperatorType *ot) * \{ */ static const EnumPropertyItem convert_target_items[] = { - {OB_CURVE, "CURVE", ICON_OUTLINER_OB_CURVE, "Curve", "Curve from Mesh or Text objects"}, + {OB_CURVES_LEGACY, + "CURVE", + ICON_OUTLINER_OB_CURVE, + "Curve", + "Curve from Mesh or Text objects"}, {OB_MESH, "MESH", ICON_OUTLINER_OB_MESH, @@ -2547,19 +2567,19 @@ static const EnumPropertyItem convert_target_items[] = { "Point Cloud", "Point Cloud from Mesh objects"}, #endif - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; static void object_data_convert_ensure_curve_cache(Depsgraph *depsgraph, Scene *scene, Object *ob) { - if (ob->runtime.curve_cache == NULL) { + if (ob->runtime.curve_cache == nullptr) { /* Force creation. This is normally not needed but on operator * redo we might end up with an object which isn't evaluated yet. * Also happens in case we are working on a copy of the object * (all its caches have been nuked then). */ - if (ELEM(ob->type, OB_SURF, OB_CURVE, OB_FONT)) { - /* We need 'for render' ON here, to enable computing bevel dipslist if needed. + if (ELEM(ob->type, OB_SURF, OB_CURVES_LEGACY, OB_FONT)) { + /* We need 'for render' ON here, to enable computing bevel #DispList if needed. * Also makes sense anyway, we would not want e.g. to lose hidden parts etc. */ BKE_displist_make_curveTypes(depsgraph, scene, ob, true); } @@ -2572,10 +2592,10 @@ static void object_data_convert_ensure_curve_cache(Depsgraph *depsgraph, Scene * static void object_data_convert_curve_to_mesh(Main *bmain, Depsgraph *depsgraph, Object *ob) { Object *object_eval = DEG_get_evaluated_object(depsgraph, ob); - Curve *curve = ob->data; + Curve *curve = static_cast<Curve *>(ob->data); Mesh *mesh = BKE_mesh_new_from_object_to_bmain(bmain, depsgraph, object_eval, true); - if (mesh == NULL) { + if (mesh == nullptr) { /* Unable to convert the curve to a mesh. */ return; } @@ -2606,9 +2626,9 @@ static bool object_convert_poll(bContext *C) { Scene *scene = CTX_data_scene(C); Base *base_act = CTX_data_active_base(C); - Object *obact = base_act ? base_act->object : NULL; + Object *obact = base_act ? base_act->object : nullptr; - if (obact == NULL || obact->data == NULL || ID_IS_LINKED(obact) || + if (obact == nullptr || obact->data == nullptr || ID_IS_LINKED(obact) || ID_IS_OVERRIDE_LIBRARY(obact) || ID_IS_OVERRIDE_LIBRARY(obact->data)) { return false; } @@ -2621,7 +2641,7 @@ static bool object_convert_poll(bContext *C) static Base *duplibase_for_convert( Main *bmain, Depsgraph *depsgraph, Scene *scene, ViewLayer *view_layer, Base *base, Object *ob) { - if (ob == NULL) { + if (ob == nullptr) { ob = base->object; } @@ -2636,14 +2656,14 @@ static Base *duplibase_for_convert( /* XXX: An ugly hack needed because if we re-run depsgraph with some new meta-ball objects * having same 'family name' as orig ones, they will affect end result of meta-ball computation. - * For until we get rid of that name-based thingy in MBalls, that should do the trick - * (this is weak, but other solution (to change name of `obn`) is even worse imho). + * For until we get rid of that name-based thingy in meta-balls, that should do the trick + * (this is weak, but other solution (to change name of `obn`) is even worse IMHO). * See T65996. */ const bool is_meta_ball = (obn->type == OB_MBALL); void *obdata = obn->data; if (is_meta_ball) { obn->type = OB_EMPTY; - obn->data = NULL; + obn->data = nullptr; } /* XXX Doing that here is stupid, it means we update and re-evaluate the whole depsgraph every @@ -2675,7 +2695,7 @@ static int object_convert_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); - Base *basen = NULL, *basact = NULL; + Base *basen = nullptr, *basact = nullptr; Object *ob1, *obact = CTX_data_active_object(C); const short target = RNA_enum_get(op->ptr, "target"); bool keep_original = RNA_boolean_get(op->ptr, "keep_original"); @@ -2723,7 +2743,7 @@ static int object_convert_exec(bContext *C, wmOperator *op) * on other objects data masks too, see: T50950. */ { LISTBASE_FOREACH (CollectionPointerLink *, link, &selected_editable_bases) { - Base *base = link->ptr.data; + Base *base = static_cast<Base *>(link->ptr.data); Object *ob = base->object; /* The way object type conversion works currently (enforcing conversion of *all* objects @@ -2750,8 +2770,8 @@ static int object_convert_exec(bContext *C, wmOperator *op) } LISTBASE_FOREACH (CollectionPointerLink *, link, &selected_editable_bases) { - Object *newob = NULL; - Base *base = link->ptr.data; + Object *newob = nullptr; + Base *base = static_cast<Base *>(link->ptr.data); Object *ob = base->object; if (ob->flag & OB_DONE || !IS_TAGGED(ob->data)) { @@ -2773,15 +2793,15 @@ static int object_convert_exec(bContext *C, wmOperator *op) } } } - else if (ob->type == OB_MESH && target == OB_CURVE) { + else if (ob->type == OB_MESH && target == OB_CURVES_LEGACY) { ob->flag |= OB_DONE; if (keep_original) { - basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, NULL); + basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, nullptr); newob = basen->object; /* Decrement original mesh's usage count. */ - Mesh *me = newob->data; + Mesh *me = static_cast<Mesh *>(newob->data); id_us_min(&me->id); /* Make a new copy of the mesh. */ @@ -2793,9 +2813,9 @@ static int object_convert_exec(bContext *C, wmOperator *op) BKE_mesh_to_curve(bmain, depsgraph, scene, newob); - if (newob->type == OB_CURVE) { + if (newob->type == OB_CURVES_LEGACY) { BKE_object_free_modifiers(newob, 0); /* after derivedmesh calls! */ - if (newob->rigidbody_object != NULL) { + if (newob->rigidbody_object != nullptr) { ED_rigidbody_object_remove(bmain, scene, newob); } } @@ -2851,11 +2871,11 @@ static int object_convert_exec(bContext *C, wmOperator *op) ob->flag |= OB_DONE; if (keep_original) { - basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, NULL); + basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, nullptr); newob = basen->object; /* Decrement original mesh's usage count. */ - Mesh *me = newob->data; + Mesh *me = static_cast<Mesh *>(newob->data); id_us_min(&me->id); /* Make a new copy of the mesh. */ @@ -2876,11 +2896,11 @@ static int object_convert_exec(bContext *C, wmOperator *op) ob->flag |= OB_DONE; if (keep_original) { - basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, NULL); + basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, nullptr); newob = basen->object; /* Decrement original mesh's usage count. */ - Mesh *me = newob->data; + Mesh *me = static_cast<Mesh *>(newob->data); id_us_min(&me->id); /* Make a new copy of the mesh. */ @@ -2912,50 +2932,58 @@ static int object_convert_exec(bContext *C, wmOperator *op) ob->flag |= OB_DONE; if (keep_original) { - basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, NULL); + basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, nullptr); newob = basen->object; /* Decrement original curve's usage count. */ id_us_min(&((Curve *)newob->data)->id); /* Make a new copy of the curve. */ - newob->data = BKE_id_copy(bmain, ob->data); + newob->data = BKE_id_copy(bmain, static_cast<ID *>(ob->data)); } else { newob = ob; } - Curve *cu = newob->data; + Curve *cu = static_cast<Curve *>(newob->data); Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); - BKE_vfont_to_curve_ex(ob_eval, ob_eval->data, FO_EDIT, &cu->nurb, NULL, NULL, NULL, NULL); - - newob->type = OB_CURVE; - cu->type = OB_CURVE; + BKE_vfont_to_curve_ex(ob_eval, + static_cast<Curve *>(ob_eval->data), + FO_EDIT, + &cu->nurb, + nullptr, + nullptr, + nullptr, + nullptr); + + newob->type = OB_CURVES_LEGACY; + cu->type = OB_CURVES_LEGACY; if (cu->vfont) { id_us_min(&cu->vfont->id); - cu->vfont = NULL; + cu->vfont = nullptr; } if (cu->vfontb) { id_us_min(&cu->vfontb->id); - cu->vfontb = NULL; + cu->vfontb = nullptr; } if (cu->vfonti) { id_us_min(&cu->vfonti->id); - cu->vfonti = NULL; + cu->vfonti = nullptr; } if (cu->vfontbi) { id_us_min(&cu->vfontbi->id); - cu->vfontbi = NULL; + cu->vfontbi = nullptr; } if (!keep_original) { /* other users */ if (ID_REAL_USERS(&cu->id) > 1) { - for (ob1 = bmain->objects.first; ob1; ob1 = ob1->id.next) { + for (ob1 = static_cast<Object *>(bmain->objects.first); ob1; + ob1 = static_cast<Object *>(ob1->id.next)) { if (ob1->data == ob->data) { - ob1->type = OB_CURVE; + ob1->type = OB_CURVES_LEGACY; DEG_id_tag_update(&ob1->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); } @@ -2985,22 +3013,22 @@ static int object_convert_exec(bContext *C, wmOperator *op) BKE_gpencil_convert_curve(bmain, scene, ob_gpencil, newob, false, 1.0f, 0.0f); gpencilConverted = true; gpencilCurveConverted = true; - basen = NULL; + basen = nullptr; } } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { ob->flag |= OB_DONE; if (target == OB_MESH) { if (keep_original) { - basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, NULL); + basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, nullptr); newob = basen->object; /* Decrement original curve's usage count. */ id_us_min(&((Curve *)newob->data)->id); /* make a new copy of the curve */ - newob->data = BKE_id_copy(bmain, ob->data); + newob->data = BKE_id_copy(bmain, static_cast<ID *>(ob->data)); } else { newob = ob; @@ -3013,7 +3041,7 @@ static int object_convert_exec(bContext *C, wmOperator *op) BKE_object_free_curve_cache(newob); } else if (target == OB_GPENCIL) { - if (ob->type != OB_CURVE) { + if (ob->type != OB_CURVES_LEGACY) { ob->flag &= ~OB_DONE; BKE_report(op->reports, RPT_ERROR, "Convert Surfaces to Grease Pencil is not supported"); } @@ -3047,23 +3075,24 @@ static int object_convert_exec(bContext *C, wmOperator *op) basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, baseob); newob = basen->object; - MetaBall *mb = newob->data; + MetaBall *mb = static_cast<MetaBall *>(newob->data); id_us_min(&mb->id); newob->data = BKE_mesh_add(bmain, "Mesh"); newob->type = OB_MESH; - Mesh *me = newob->data; + Mesh *me = static_cast<Mesh *>(newob->data); me->totcol = mb->totcol; if (newob->totcol) { - me->mat = MEM_dupallocN(mb->mat); + me->mat = static_cast<Material **>(MEM_dupallocN(mb->mat)); for (a = 0; a < newob->totcol; a++) { id_us_plus((ID *)me->mat[a]); } } object_data_convert_ensure_curve_cache(depsgraph, scene, baseob); - BKE_mesh_from_metaball(&baseob->runtime.curve_cache->disp, newob->data); + BKE_mesh_from_metaball(&baseob->runtime.curve_cache->disp, + static_cast<Mesh *>(newob->data)); if (obact->type == OB_MBALL) { basact = basen; @@ -3077,11 +3106,11 @@ static int object_convert_exec(bContext *C, wmOperator *op) ob->flag |= OB_DONE; if (keep_original) { - basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, NULL); + basen = duplibase_for_convert(bmain, depsgraph, scene, view_layer, base, nullptr); newob = basen->object; /* Decrement original point cloud's usage count. */ - PointCloud *pointcloud = newob->data; + PointCloud *pointcloud = static_cast<PointCloud *>(newob->data); id_us_min(&pointcloud->id); /* Make a new copy of the point cloud. */ @@ -3104,7 +3133,7 @@ static int object_convert_exec(bContext *C, wmOperator *op) /* Ensure new object has consistent material data with its new obdata. */ if (newob) { - BKE_object_materials_test(bmain, newob, newob->data); + BKE_object_materials_test(bmain, newob, static_cast<ID *>(newob->data)); } /* tag obdata if it was been changed */ @@ -3116,7 +3145,7 @@ static int object_convert_exec(bContext *C, wmOperator *op) basact = basen; } - basen = NULL; + basen = nullptr; } if (!keep_original && (ob->flag & OB_DONE)) { @@ -3137,7 +3166,7 @@ static int object_convert_exec(bContext *C, wmOperator *op) * their basis happens to be removed first. */ FOREACH_SCENE_OBJECT_BEGIN (scene, ob_mball) { if (ob_mball->type == OB_MBALL) { - Object *ob_basis = NULL; + Object *ob_basis = nullptr; if (!BKE_mball_is_basis(ob_mball) && ((ob_basis = BKE_mball_basis_find(scene, ob_mball)) && (ob_basis->flag & OB_DONE))) { ED_object_base_free_and_unlink(bmain, scene, ob_mball); @@ -3159,7 +3188,7 @@ static int object_convert_exec(bContext *C, wmOperator *op) /* Remove curves and meshes converted to Grease Pencil object. */ if (gpencilConverted) { FOREACH_SCENE_OBJECT_BEGIN (scene, ob_delete) { - if (ELEM(ob_delete->type, OB_CURVE, OB_MESH)) { + if (ELEM(ob_delete->type, OB_CURVES_LEGACY, OB_MESH)) { if (ob_delete->flag & OB_DONE) { ED_object_base_free_and_unlink(bmain, scene, ob_delete); } @@ -3172,7 +3201,7 @@ static int object_convert_exec(bContext *C, wmOperator *op) /* Remove Text curves converted to Grease Pencil object to avoid duplicated curves. */ if (gpencilCurveConverted) { FOREACH_SCENE_OBJECT_BEGIN (scene, ob_delete) { - if (ELEM(ob_delete->type, OB_CURVE) && (ob_delete->flag & OB_DONE)) { + if (ELEM(ob_delete->type, OB_CURVES_LEGACY) && (ob_delete->flag & OB_DONE)) { ED_object_base_free_and_unlink(bmain, scene, ob_delete); } } @@ -3181,7 +3210,7 @@ static int object_convert_exec(bContext *C, wmOperator *op) } // XXX ED_object_editmode_enter(C, 0); - // XXX exit_editmode(C, EM_FREEDATA|); /* freedata, but no undo */ + // XXX exit_editmode(C, EM_FREEDATA|); /* free data, but no undo */ if (basact) { /* active base was changed */ @@ -3208,15 +3237,15 @@ static void object_convert_ui(bContext *UNUSED(C), wmOperator *op) uiLayoutSetPropSep(layout, true); - uiItemR(layout, op->ptr, "target", 0, NULL, ICON_NONE); - uiItemR(layout, op->ptr, "keep_original", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "target", 0, nullptr, ICON_NONE); + uiItemR(layout, op->ptr, "keep_original", 0, nullptr, ICON_NONE); if (RNA_enum_get(op->ptr, "target") == OB_GPENCIL) { - uiItemR(layout, op->ptr, "thickness", 0, NULL, ICON_NONE); - uiItemR(layout, op->ptr, "angle", 0, NULL, ICON_NONE); - uiItemR(layout, op->ptr, "offset", 0, NULL, ICON_NONE); - uiItemR(layout, op->ptr, "seams", 0, NULL, ICON_NONE); - uiItemR(layout, op->ptr, "faces", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "thickness", 0, nullptr, ICON_NONE); + uiItemR(layout, op->ptr, "angle", 0, nullptr, ICON_NONE); + uiItemR(layout, op->ptr, "offset", 0, nullptr, ICON_NONE); + uiItemR(layout, op->ptr, "seams", 0, nullptr, ICON_NONE); + uiItemR(layout, op->ptr, "faces", 0, nullptr, ICON_NONE); } } @@ -3243,14 +3272,14 @@ void OBJECT_OT_convert(wmOperatorType *ot) ot->srna, "target", convert_target_items, OB_MESH, "Target", "Type of object to convert to"); RNA_def_boolean(ot->srna, "keep_original", - 0, + false, "Keep Original", "Keep original objects instead of replacing them"); prop = RNA_def_float_rotation(ot->srna, "angle", 0, - NULL, + nullptr, DEG2RADF(0.0f), DEG2RADF(180.0f), "Threshold Angle", @@ -3260,8 +3289,8 @@ void OBJECT_OT_convert(wmOperatorType *ot) RNA_def_property_float_default(prop, DEG2RADF(70.0f)); RNA_def_int(ot->srna, "thickness", 5, 1, 100, "Thickness", "", 1, 100); - RNA_def_boolean(ot->srna, "seams", 0, "Only Seam Edges", "Convert only seam edges"); - RNA_def_boolean(ot->srna, "faces", 1, "Export Faces", "Export faces as filled strokes"); + RNA_def_boolean(ot->srna, "seams", false, "Only Seam Edges", "Convert only seam edges"); + RNA_def_boolean(ot->srna, "faces", true, "Export Faces", "Export faces as filled strokes"); RNA_def_float_distance(ot->srna, "offset", 0.01f, @@ -3279,16 +3308,11 @@ void OBJECT_OT_convert(wmOperatorType *ot) /** \name Duplicate Object Operator * \{ */ -/* - * dupflag: a flag made from constants declared in DNA_userdef_types.h - * The flag tells adduplicate() whether to copy data linked to the object, - * or to reference the existing data. - * U.dupflag for default operations or you can construct a flag as python does - * if the dupflag is 0 then no data will be copied (linked duplicate). */ - -/* used below, assumes id.new is correct */ -/* leaves selection of base/object unaltered */ -/* Does set ID->newid pointers. */ +/** + * - Assumes `id.new` is correct. + * - Leaves selection of base/object unaltered. + * - Sets #ID.newid pointers. + */ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, ViewLayer *view_layer, @@ -3296,18 +3320,19 @@ static Base *object_add_duplicate_internal(Main *bmain, const eDupli_ID_Flags dupflag, const eLibIDDuplicateFlags duplicate_options) { - Base *base, *basen = NULL; + Base *base, *basen = nullptr; Object *obn; if (ob->mode & OB_MODE_POSE) { /* nothing? */ } else { - obn = ID_NEW_SET(ob, BKE_object_duplicate(bmain, ob, dupflag, duplicate_options)); + obn = static_cast<Object *>( + ID_NEW_SET(ob, BKE_object_duplicate(bmain, ob, dupflag, duplicate_options))); DEG_id_tag_update(&obn->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); base = BKE_view_layer_base_find(view_layer, ob); - if ((base != NULL) && (base->flag & BASE_VISIBLE_DEPSGRAPH)) { + if ((base != nullptr) && (base->flag & BASE_VISIBLE_DEPSGRAPH)) { BKE_collection_object_add_from(bmain, scene, ob, obn); } else { @@ -3316,7 +3341,7 @@ static Base *object_add_duplicate_internal(Main *bmain, } basen = BKE_view_layer_base_find(view_layer, obn); - if (base != NULL) { + if (base != nullptr) { basen->local_view_bits = base->local_view_bits; } @@ -3325,8 +3350,7 @@ static Base *object_add_duplicate_internal(Main *bmain, */ /* XXX: is 2) really a good measure here? */ if (ob->rigidbody_object || ob->rigidbody_constraint) { - Collection *collection; - for (collection = bmain->collections.first; collection; collection = collection->id.next) { + LISTBASE_FOREACH (Collection *, collection, &bmain->collections) { if (BKE_collection_has_object(collection, ob)) { BKE_collection_object_add(bmain, collection, obn); } @@ -3349,8 +3373,8 @@ Base *ED_object_add_duplicate( dupflag, LIB_ID_DUPLICATE_IS_SUBPROCESS | LIB_ID_DUPLICATE_IS_ROOT_ID); - if (basen == NULL) { - return NULL; + if (basen == nullptr) { + return nullptr; } ob = basen->object; @@ -3363,7 +3387,7 @@ Base *ED_object_add_duplicate( /* DAG_relations_tag_update(bmain); */ /* caller must do */ - if (ob->data != NULL) { + if (ob->data != nullptr) { DEG_id_tag_update_ex(bmain, (ID *)ob->data, ID_RECALC_EDITORS); } @@ -3379,7 +3403,7 @@ static int duplicate_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); const bool linked = RNA_boolean_get(op->ptr, "linked"); - const eDupli_ID_Flags dupflag = (linked) ? 0 : (eDupli_ID_Flags)U.dupflag; + const eDupli_ID_Flags dupflag = (linked) ? (eDupli_ID_Flags)0 : (eDupli_ID_Flags)U.dupflag; /* We need to handle that here ourselves, because we may duplicate several objects, in which case * we also want to remap pointers between those... */ @@ -3399,7 +3423,7 @@ static int duplicate_exec(bContext *C, wmOperator *op) ED_object_base_select(base, BA_DESELECT); ED_object_base_select(basen, BA_SELECT); - if (basen == NULL) { + if (basen == nullptr) { continue; } @@ -3409,7 +3433,7 @@ static int duplicate_exec(bContext *C, wmOperator *op) } if (basen->object->data) { - DEG_id_tag_update(basen->object->data, 0); + DEG_id_tag_update(static_cast<ID *>(basen->object->data), 0); } } CTX_DATA_END; @@ -3447,7 +3471,7 @@ void OBJECT_OT_duplicate(wmOperatorType *ot) /* to give to transform */ prop = RNA_def_boolean(ot->srna, "linked", - 0, + false, "Linked", "Duplicate object but not object data, linking to the original data"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); @@ -3473,14 +3497,14 @@ static int object_add_named_exec(bContext *C, wmOperator *op) Base *basen; Object *ob; const bool linked = RNA_boolean_get(op->ptr, "linked"); - const eDupli_ID_Flags dupflag = (linked) ? 0 : (eDupli_ID_Flags)U.dupflag; + const eDupli_ID_Flags dupflag = (linked) ? (eDupli_ID_Flags)0 : (eDupli_ID_Flags)U.dupflag; char name[MAX_ID_NAME - 2]; /* find object, create fake base */ RNA_string_get(op->ptr, "name", name); ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, name); - if (ob == NULL) { + if (ob == nullptr) { BKE_report(op->reports, RPT_ERROR, "Object not found"); return OPERATOR_CANCELLED; } @@ -3499,7 +3523,7 @@ static int object_add_named_exec(bContext *C, wmOperator *op) */ LIB_ID_DUPLICATE_IS_SUBPROCESS | LIB_ID_DUPLICATE_IS_ROOT_ID); - if (basen == NULL) { + if (basen == nullptr) { BKE_report(op->reports, RPT_ERROR, "Object could not be duplicated"); return OPERATOR_CANCELLED; } @@ -3510,7 +3534,7 @@ static int object_add_named_exec(bContext *C, wmOperator *op) /* object_add_duplicate_internal() doesn't deselect other objects, unlike object_add_common() or * BKE_view_layer_base_deselect_all(). */ - ED_object_base_deselect_all(view_layer, NULL, SEL_DESELECT); + ED_object_base_deselect_all(view_layer, nullptr, SEL_DESELECT); ED_object_base_select(basen, BA_SELECT); ED_object_base_activate(C, basen); @@ -3566,11 +3590,11 @@ void OBJECT_OT_add_named(wmOperatorType *ot) "Linked", "Duplicate object but not object data, linking to the original data"); - RNA_def_string(ot->srna, "name", NULL, MAX_ID_NAME - 2, "Name", "Object name to add"); + RNA_def_string(ot->srna, "name", nullptr, MAX_ID_NAME - 2, "Name", "Object name to add"); prop = RNA_def_float_matrix( - ot->srna, "matrix", 4, 4, NULL, 0.0f, 0.0f, "Matrix", "", 0.0f, 0.0f); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + ot->srna, "matrix", 4, 4, nullptr, 0.0f, 0.0f, "Matrix", "", 0.0f, 0.0f); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); object_add_drop_xy_props(ot); } @@ -3599,7 +3623,7 @@ static int object_transform_to_mouse_exec(bContext *C, wmOperator *op) ob = OBACT(view_layer); } - if (ob == NULL) { + if (ob == nullptr) { BKE_report(op->reports, RPT_ERROR, "Object not found"); return OPERATOR_CANCELLED; } @@ -3615,8 +3639,10 @@ static int object_transform_to_mouse_exec(bContext *C, wmOperator *op) PropertyRNA *prop_matrix = RNA_struct_find_property(op->ptr, "matrix"); if (RNA_property_is_set(op->ptr, prop_matrix)) { + ObjectsInViewLayerParams params = {0}; uint objects_len; - Object **objects = BKE_view_layer_array_selected_objects(view_layer, NULL, &objects_len, {0}); + Object **objects = BKE_view_layer_array_selected_objects_params( + view_layer, nullptr, &objects_len, ¶ms); float matrix[4][4]; RNA_property_float_get_array(op->ptr, prop_matrix, &matrix[0][0]); @@ -3674,14 +3700,14 @@ void OBJECT_OT_transform_to_mouse(wmOperatorType *ot) PropertyRNA *prop; RNA_def_string(ot->srna, "name", - NULL, + nullptr, MAX_ID_NAME - 2, "Name", "Object name to place (when unset use the active object)"); prop = RNA_def_float_matrix( - ot->srna, "matrix", 4, 4, NULL, 0.0f, 0.0f, "Matrix", "", 0.0f, 0.0f); - RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + ot->srna, "matrix", 4, 4, nullptr, 0.0f, 0.0f, "Matrix", "", 0.0f, 0.0f); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE)); object_add_drop_xy_props(ot); } @@ -3696,12 +3722,12 @@ static bool object_join_poll(bContext *C) { Object *ob = CTX_data_active_object(C); - if (ob == NULL || ob->data == NULL || ID_IS_LINKED(ob) || ID_IS_OVERRIDE_LIBRARY(ob) || + if (ob == nullptr || ob->data == nullptr || ID_IS_LINKED(ob) || ID_IS_OVERRIDE_LIBRARY(ob) || ID_IS_OVERRIDE_LIBRARY(ob->data)) { return false; } - if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_ARMATURE, OB_GPENCIL)) { + if (ELEM(ob->type, OB_MESH, OB_CURVES_LEGACY, OB_SURF, OB_ARMATURE, OB_GPENCIL)) { return ED_operator_screenactive(C); } return false; @@ -3740,7 +3766,7 @@ static int object_join_exec(bContext *C, wmOperator *op) if (ob->type == OB_MESH) { ret = ED_mesh_join_objects_exec(C, op); } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { ret = ED_curve_join_objects_exec(C, op); } else if (ob->type == OB_ARMATURE) { @@ -3802,7 +3828,7 @@ static bool join_shapes_poll(bContext *C) { Object *ob = CTX_data_active_object(C); - if (ob == NULL || ob->data == NULL || ID_IS_LINKED(ob) || ID_IS_OVERRIDE_LIBRARY(ob) || + if (ob == nullptr || ob->data == nullptr || ID_IS_LINKED(ob) || ID_IS_OVERRIDE_LIBRARY(ob) || ID_IS_OVERRIDE_LIBRARY(ob->data)) { return false; } diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index 30e3f2b0c69..3b40a10eb2a 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -301,6 +301,7 @@ static void bake_targets_refresh(BakeTargets *targets) Image *ima = targets->images[i].image; if (ima) { + BKE_image_partial_update_mark_full_update(ima); LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { BKE_image_free_gputextures(ima); DEG_id_tag_update(&ima->id, 0); @@ -605,7 +606,7 @@ static bool bake_objects_check(Main *bmain, continue; } - if (ELEM(ob_iter->type, OB_MESH, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL) == false) { + if (ELEM(ob_iter->type, OB_MESH, OB_FONT, OB_CURVES_LEGACY, OB_SURF, OB_MBALL) == false) { BKE_reportf(reports, RPT_ERROR, "Object \"%s\" is not a mesh or can't be converted to a mesh (Curve, Text, " @@ -1038,19 +1039,18 @@ static void bake_targets_populate_pixels_vertex_colors(BakeTargets *targets, * materials and UVs. */ pixel->seed = v; - /* Barycentric coordinates, nudged a bit to avoid precision issues that - * may happen when exactly at the vertex coordinate. */ + /* Barycentric coordinates. */ if (j == 0) { - pixel->uv[0] = 1.0f - FLT_EPSILON; - pixel->uv[1] = FLT_EPSILON / 2.0f; + pixel->uv[0] = 1.0f; + pixel->uv[1] = 0.0f; } else if (j == 1) { - pixel->uv[0] = FLT_EPSILON / 2.0f; - pixel->uv[1] = 1.0f - FLT_EPSILON; + pixel->uv[0] = 0.0f; + pixel->uv[1] = 1.0f; } else if (j == 2) { - pixel->uv[0] = FLT_EPSILON / 2.0f; - pixel->uv[1] = FLT_EPSILON / 2.0f; + pixel->uv[0] = 0.0f; + pixel->uv[1] = 0.0f; } } } diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c index 4ccd35b512b..3f4ed27a175 100644 --- a/source/blender/editors/object/object_constraint.c +++ b/source/blender/editors/object/object_constraint.c @@ -511,7 +511,7 @@ static void test_constraint( * * In other cases it should be impossible to have a type mismatch. */ - if (ct->tar->type != OB_CURVE) { + if (ct->tar->type != OB_CURVES_LEGACY) { con->flag |= CONSTRAINT_DISABLE; } else { @@ -1443,6 +1443,11 @@ static int constraint_delete_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); + + if (con == NULL) { + return OPERATOR_CANCELLED; + } + ListBase *lb = ED_object_constraint_list_from_constraint(ob, con, NULL); /* Store name temporarily for report. */ @@ -1510,6 +1515,11 @@ static int constraint_apply_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); + + if (con == NULL) { + return OPERATOR_CANCELLED; + } + bPoseChannel *pchan; ListBase *constraints = ED_object_constraint_list_from_constraint(ob, con, &pchan); @@ -1602,6 +1612,11 @@ 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); + + if (con == NULL) { + return OPERATOR_CANCELLED; + } + bPoseChannel *pchan; ListBase *constraints = ED_object_constraint_list_from_constraint(ob, con, &pchan); @@ -1682,6 +1697,11 @@ 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); + + if (con == NULL) { + return OPERATOR_CANCELLED; + } + bPoseChannel *pchan; ED_object_constraint_list_from_constraint(obact, con, &pchan); @@ -2275,7 +2295,8 @@ static bool get_new_constraint_target( break; } - if (((!only_curve) || (ob->type == OB_CURVE)) && ((!only_mesh) || (ob->type == OB_MESH))) { + if (((!only_curve) || (ob->type == OB_CURVES_LEGACY)) && + ((!only_mesh) || (ob->type == OB_MESH))) { /* set target */ *tar_ob = ob; found = true; diff --git a/source/blender/editors/object/object_data_transform.c b/source/blender/editors/object/object_data_transform.c index f74556e3639..63513eac965 100644 --- a/source/blender/editors/object/object_data_transform.c +++ b/source/blender/editors/object/object_data_transform.c @@ -382,7 +382,7 @@ struct XFormObjectData *ED_object_data_xform_create_ex(ID *id, bool is_edit_mode break; } - case ID_CU: { + case ID_CU_LEGACY: { Curve *cu = (Curve *)id; struct Key *key = cu->key; @@ -505,7 +505,7 @@ void ED_object_data_xform_destroy(struct XFormObjectData *xod_base) } break; } - case ID_CU: { + case ID_CU_LEGACY: { struct XFormObjectData_Curve *xod = (struct XFormObjectData_Curve *)xod_base; if (xod->key_data != NULL) { MEM_freeN(xod->key_data); @@ -565,7 +565,7 @@ void ED_object_data_xform_by_mat4(struct XFormObjectData *xod_base, const float break; } - case ID_CU: { + case ID_CU_LEGACY: { BLI_assert(xod_base->is_edit_mode == false); /* Not used currently. */ Curve *cu = (Curve *)xod_base->id; @@ -670,7 +670,7 @@ void ED_object_data_xform_restore(struct XFormObjectData *xod_base) break; } - case ID_CU: { + case ID_CU_LEGACY: { Curve *cu = (Curve *)xod_base->id; struct Key *key = cu->key; @@ -745,7 +745,7 @@ void ED_object_data_xform_tag_update(struct XFormObjectData *xod_base) DEG_id_tag_update(<->id, ID_RECALC_GEOMETRY); break; } - case ID_CU: { + case ID_CU_LEGACY: { /* Generic update. */ Curve *cu = (Curve *)xod_base->id; DEG_id_tag_update(&cu->id, ID_RECALC_GEOMETRY); diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index fd40ac7bc7c..82b14787d9b 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -21,6 +21,7 @@ #include "BLT_translation.h" +#include "DNA_anim_types.h" #include "DNA_armature_types.h" #include "DNA_collection_types.h" #include "DNA_curve_types.h" @@ -73,6 +74,7 @@ #include "ED_curve.h" #include "ED_gpencil.h" #include "ED_image.h" +#include "ED_keyframes_keylist.h" #include "ED_lattice.h" #include "ED_mball.h" #include "ED_mesh.h" @@ -89,7 +91,7 @@ #include "CLG_log.h" -/* For menu/popup icons etc etc. */ +/* For menu/popup icons etc. */ #include "UI_interface.h" #include "UI_resources.h" @@ -340,10 +342,10 @@ static int object_hide_collection_exec(bContext *C, wmOperator *op) View3D *v3d = CTX_wm_view3d(C); int index = RNA_int_get(op->ptr, "collection_index"); - const bool extend = (win->eventstate->shift != 0); + const bool extend = (win->eventstate->modifier & KM_SHIFT) != 0; const bool toggle = RNA_boolean_get(op->ptr, "toggle"); - if (win->eventstate->alt != 0) { + if (win->eventstate->modifier & KM_ALT) { index += 10; } @@ -568,7 +570,7 @@ static bool ED_object_editmode_load_free_ex(Main *bmain, */ DEG_relations_tag_update(bmain); } - else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { + else if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) { const Curve *cu = obedit->data; if (cu->editnurb == NULL) { return false; @@ -799,12 +801,16 @@ bool ED_object_editmode_enter_ex(Main *bmain, Scene *scene, Object *ob, int flag WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_LATTICE, scene); } - else if (ELEM(ob->type, OB_SURF, OB_CURVE)) { + else if (ELEM(ob->type, OB_SURF, OB_CURVES_LEGACY)) { ok = true; ED_curve_editnurb_make(ob); WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_CURVE, scene); } + else if (ob->type == OB_CURVES) { + ok = true; + WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_CURVES, scene); + } if (ok) { DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); @@ -1019,7 +1025,7 @@ void ED_object_check_force_modifiers(Main *bmain, Scene *scene, Object *object) if (!md) { if (pd && (pd->shape == PFIELD_SHAPE_SURFACE) && !ELEM(pd->forcefield, 0, PFIELD_GUIDE, PFIELD_TEXTURE)) { - if (ELEM(object->type, OB_MESH, OB_SURF, OB_FONT, OB_CURVE)) { + if (ELEM(object->type, OB_MESH, OB_SURF, OB_FONT, OB_CURVES_LEGACY)) { ED_object_modifier_add(NULL, bmain, scene, object, NULL, eModifierType_Surface); } } @@ -1205,30 +1211,32 @@ static int object_calculate_paths_invoke(bContext *C, wmOperator *op, const wmEv /* set default settings from existing/stored settings */ { bAnimVizSettings *avs = &ob->avs; - - RNA_int_set(op->ptr, "start_frame", avs->path_sf); - RNA_int_set(op->ptr, "end_frame", avs->path_ef); + RNA_enum_set(op->ptr, "display_type", avs->path_type); + RNA_enum_set(op->ptr, "range", avs->path_range); } /* show popup dialog to allow editing of range... */ /* FIXME: hard-coded dimensions here are just arbitrary. */ - return WM_operator_props_dialog_popup(C, op, 200); + return WM_operator_props_dialog_popup(C, op, 270); } /* Calculate/recalculate whole paths (avs.path_sf to avs.path_ef) */ static int object_calculate_paths_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - int start = RNA_int_get(op->ptr, "start_frame"); - int end = RNA_int_get(op->ptr, "end_frame"); + short path_type = RNA_enum_get(op->ptr, "display_type"); + short path_range = RNA_enum_get(op->ptr, "range"); - /* set up path data for bones being calculated */ + /* set up path data for objects being calculated */ CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { - bAnimVizSettings *avs = &ob->avs; - - /* grab baking settings from operator settings */ - avs->path_sf = start; - avs->path_ef = end; + /* When operator is not invoked, dismiss the operator settings */ + if (op->flag & OP_IS_INVOKE) { + bAnimVizSettings *avs = &ob->avs; + /* grab baking settings from operator settings */ + avs->path_type = path_type; + avs->path_range = path_range; + } + animviz_motionpath_compute_range(ob, scene); /* verify that the selected object has the appropriate settings */ animviz_verify_motionpaths(op->reports, scene, ob, NULL); @@ -1247,9 +1255,9 @@ static int object_calculate_paths_exec(bContext *C, wmOperator *op) void OBJECT_OT_paths_calculate(wmOperatorType *ot) { /* identifiers */ - ot->name = "Calculate Object Paths"; + ot->name = "Calculate Object Motion Paths"; ot->idname = "OBJECT_OT_paths_calculate"; - ot->description = "Calculate motion paths for the selected objects"; + ot->description = "Generate motion paths for the selected objects"; /* api callbacks */ ot->invoke = object_calculate_paths_invoke; @@ -1260,24 +1268,18 @@ void OBJECT_OT_paths_calculate(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - RNA_def_int(ot->srna, - "start_frame", - 1, - MINAFRAME, - MAXFRAME, - "Start", - "First frame to calculate object paths on", - MINFRAME, - MAXFRAME / 2.0); - RNA_def_int(ot->srna, - "end_frame", - 250, - MINAFRAME, - MAXFRAME, - "End", - "Last frame to calculate object paths on", - MINFRAME, - MAXFRAME / 2.0); + RNA_def_enum(ot->srna, + "display_type", + rna_enum_motionpath_display_type_items, + MOTIONPATH_TYPE_RANGE, + "Display type", + ""); + RNA_def_enum(ot->srna, + "range", + rna_enum_motionpath_range_items, + MOTIONPATH_RANGE_SCENE, + "Computation Range", + ""); } /** \} */ @@ -1296,13 +1298,19 @@ static bool object_update_paths_poll(bContext *C) return false; } -static int object_update_paths_exec(bContext *C, wmOperator *UNUSED(op)) +static int object_update_paths_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); if (scene == NULL) { return OPERATOR_CANCELLED; } + CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { + animviz_motionpath_compute_range(ob, scene); + /* verify that the selected object has the appropriate settings */ + animviz_verify_motionpaths(op->reports, scene, ob, NULL); + } + CTX_DATA_END; /* calculate the paths for objects that have them (and are tagged to get refreshed) */ ED_objects_recalculate_paths_selected(C, scene, OBJECT_PATH_CALC_RANGE_FULL); @@ -1423,7 +1431,7 @@ static int object_clear_paths_exec(bContext *C, wmOperator *op) /* operator callback/wrapper */ static int object_clear_paths_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - if ((event->shift) && !RNA_struct_property_is_set(op->ptr, "only_selected")) { + if ((event->modifier & KM_SHIFT) && !RNA_struct_property_is_set(op->ptr, "only_selected")) { RNA_boolean_set(op->ptr, "only_selected", true); } return object_clear_paths_exec(C, op); @@ -1453,46 +1461,6 @@ void OBJECT_OT_paths_clear(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Update Motion Paths Range from Scene Operator - * \{ */ - -static int object_update_paths_range_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Scene *scene = CTX_data_scene(C); - - /* Loop over all editable objects in scene. */ - CTX_DATA_BEGIN (C, Object *, ob, editable_objects) { - /* use Preview Range or Full Frame Range - whichever is in use */ - ob->avs.path_sf = PSFRA; - ob->avs.path_ef = PEFRA; - - /* tag for updates */ - DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); - } - CTX_DATA_END; - - return OPERATOR_FINISHED; -} - -void OBJECT_OT_paths_range_update(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Update Range from Scene"; - ot->idname = "OBJECT_OT_paths_range_update"; - ot->description = "Update frame range for motion paths from the Scene's current frame range"; - - /* callbacks */ - ot->exec = object_update_paths_range_exec; - ot->poll = ED_operator_object_active_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Object Shade Smooth/Flat Operator * \{ */ @@ -1548,7 +1516,7 @@ static int shade_smooth_exec(bContext *C, wmOperator *op) BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); changed = true; } - else if (ELEM(ob->type, OB_SURF, OB_CURVE)) { + else if (ELEM(ob->type, OB_SURF, OB_CURVES_LEGACY)) { BKE_curve_smooth_flag_set(ob->data, use_smooth); changed = true; } diff --git a/source/blender/editors/object/object_hook.c b/source/blender/editors/object/object_hook.c index 338307dc8ca..dffbb3bedd5 100644 --- a/source/blender/editors/object/object_hook.c +++ b/source/blender/editors/object/object_hook.c @@ -342,7 +342,7 @@ static bool object_hook_index_array(Main *bmain, } return true; } - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: ED_curve_editnurb_load(bmain, obedit); ED_curve_editnurb_make(obedit); @@ -447,7 +447,7 @@ static void object_hook_select(Object *ob, HookModifierData *hmd) else if (ob->type == OB_LATTICE) { select_editlattice_hook(ob, hmd); } - else if (ob->type == OB_CURVE) { + else if (ob->type == OB_CURVES_LEGACY) { select_editcurve_hook(ob, hmd); } else if (ob->type == OB_SURF) { diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index 0aac5957c9d..135c76140c1 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -76,7 +76,6 @@ void OBJECT_OT_shade_flat(struct wmOperatorType *ot); void OBJECT_OT_paths_calculate(struct wmOperatorType *ot); void OBJECT_OT_paths_update(struct wmOperatorType *ot); void OBJECT_OT_paths_clear(struct wmOperatorType *ot); -void OBJECT_OT_paths_range_update(struct wmOperatorType *ot); void OBJECT_OT_paths_update_visible(struct wmOperatorType *ot); void OBJECT_OT_forcefield_toggle(struct wmOperatorType *ot); @@ -97,7 +96,7 @@ void OBJECT_OT_select_more(struct wmOperatorType *ot); void OBJECT_OT_select_less(struct wmOperatorType *ot); void OBJECT_OT_select_same_collection(struct wmOperatorType *ot); -/* object_add.c */ +/* object_add.cc */ void OBJECT_OT_add(struct wmOperatorType *ot); void OBJECT_OT_add_named(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_modes.c b/source/blender/editors/object/object_modes.c index 509e496c39a..8e9e8558016 100644 --- a/source/blender/editors/object/object_modes.c +++ b/source/blender/editors/object/object_modes.c @@ -118,7 +118,7 @@ bool ED_object_mode_compat_test(const Object *ob, eObjectMode mode) } } break; - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: case OB_FONT: case OB_MBALL: @@ -143,7 +143,7 @@ bool ED_object_mode_compat_test(const Object *ob, eObjectMode mode) } break; case OB_CURVES: - if (mode & (OB_MODE_SCULPT_CURVES)) { + if (mode & (OB_MODE_EDIT | OB_MODE_SCULPT_CURVES)) { return true; } break; diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index 38a955dca0a..e0d25baec16 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -110,7 +110,7 @@ static void object_force_modifier_update_for_bind(Depsgraph *depsgraph, Object * else if (ob->type == OB_MBALL) { BKE_displist_make_mball(depsgraph, scene_eval, ob_eval); } - else if (ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF, OB_FONT)) { BKE_displist_make_curveTypes(depsgraph, scene_eval, ob_eval, false); } else if (ob->type == OB_GPENCIL) { @@ -757,7 +757,7 @@ static bool modifier_apply_obdata( } } } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { Object *object_eval = DEG_get_evaluated_object(depsgraph, ob); Curve *curve = ob->data; Curve *curve_eval = (Curve *)object_eval->data; diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index 35f5ede270d..45ee4daa441 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -45,7 +45,6 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_paths_calculate); WM_operatortype_append(OBJECT_OT_paths_update); WM_operatortype_append(OBJECT_OT_paths_clear); - WM_operatortype_append(OBJECT_OT_paths_range_update); WM_operatortype_append(OBJECT_OT_paths_update_visible); WM_operatortype_append(OBJECT_OT_forcefield_toggle); diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 8834941d083..3ecf86d14ed 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -169,7 +169,7 @@ static int vertex_parent_set_exec(bContext *C, wmOperator *op) } } } - else if (ELEM(obedit->type, OB_SURF, OB_CURVE)) { + else if (ELEM(obedit->type, OB_SURF, OB_CURVES_LEGACY)) { ListBase *editnurb = object_editcurve_get(obedit); for (Nurb *nu = editnurb->first; nu != NULL; nu = nu->next) { @@ -341,7 +341,7 @@ EnumPropertyItem prop_clear_parent_types[] = { /* Helper for ED_object_parent_clear() - Remove deform-modifiers associated with parent */ static void object_remove_parent_deform_modifiers(Object *ob, const Object *par) { - if (ELEM(par->type, OB_ARMATURE, OB_LATTICE, OB_CURVE)) { + if (ELEM(par->type, OB_ARMATURE, OB_LATTICE, OB_CURVES_LEGACY)) { ModifierData *md, *mdn; /* assume that we only need to remove the first instance of matching deform modifier here */ @@ -363,7 +363,7 @@ static void object_remove_parent_deform_modifiers(Object *ob, const Object *par) free = true; } } - else if ((md->type == eModifierType_Curve) && (par->type == OB_CURVE)) { + else if ((md->type == eModifierType_Curve) && (par->type == OB_CURVES_LEGACY)) { CurveModifierData *cmd = (CurveModifierData *)md; if (cmd->object == par) { free = true; @@ -531,7 +531,7 @@ bool ED_object_parent_set(ReportList *reports, switch (partype) { case PAR_FOLLOW: case PAR_PATH_CONST: { - if (par->type != OB_CURVE) { + if (par->type != OB_CURVES_LEGACY) { return false; } Curve *cu = par->data; @@ -626,7 +626,7 @@ bool ED_object_parent_set(ReportList *reports, */ /* XXX currently this should only happen for meshes, curves, surfaces, * and lattices - this stuff isn't available for meta-balls yet. */ - if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) { + if (ELEM(ob->type, OB_MESH, OB_CURVES_LEGACY, OB_SURF, OB_FONT, OB_LATTICE)) { ModifierData *md; switch (partype) { @@ -968,7 +968,7 @@ static int parent_set_invoke_menu(bContext *C, wmOperatorType *ot) uiItemEnumO_ptr(layout, ot, NULL, 0, "type", PAR_BONE); uiItemEnumO_ptr(layout, ot, NULL, 0, "type", PAR_BONE_RELATIVE); } - else if (parent->type == OB_CURVE) { + else if (parent->type == OB_CURVES_LEGACY) { uiItemEnumO_ptr(layout, ot, NULL, 0, "type", PAR_CURVE); uiItemEnumO_ptr(layout, ot, NULL, 0, "type", PAR_FOLLOW); uiItemEnumO_ptr(layout, ot, NULL, 0, "type", PAR_PATH_CONST); @@ -1820,7 +1820,7 @@ static void single_obdata_users( ob->data, BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS)); break; - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: case OB_FONT: ob->data = cu = ID_NEW_SET( @@ -2318,7 +2318,7 @@ static int make_override_library_exec(bContext *C, wmOperator *op) BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); const bool success = BKE_lib_override_library_create( - bmain, scene, view_layer, NULL, id_root, &obact->id, NULL); + bmain, scene, view_layer, NULL, id_root, id_root, &obact->id, NULL); /* Remove the instance empty from this scene, the items now have an overridden collection * instead. */ diff --git a/source/blender/editors/object/object_remesh.cc b/source/blender/editors/object/object_remesh.cc index 44e6fb10528..2e495ac6147 100644 --- a/source/blender/editors/object/object_remesh.cc +++ b/source/blender/editors/object/object_remesh.cc @@ -406,7 +406,7 @@ static int voxel_size_edit_modal(bContext *C, wmOperator *op, const wmEvent *eve d = cd->slow_mval[0] - mval[0]; } - if (event->ctrl) { + if (event->modifier & KM_CTRL) { /* Linear mode, enables jumping to any voxel size. */ d = d * 0.0005f; } diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c index f017fea7cdf..9e82abf4d07 100644 --- a/source/blender/editors/object/object_transform.c +++ b/source/blender/editors/object/object_transform.c @@ -613,7 +613,7 @@ static int apply_objects_internal(bContext *C, OB_ARMATURE, OB_LATTICE, OB_MBALL, - OB_CURVE, + OB_CURVES_LEGACY, OB_SURF, OB_FONT, OB_GPENCIL)) { @@ -639,13 +639,13 @@ static int apply_objects_internal(bContext *C, } } - if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { ID *obdata = ob->data; Curve *cu; cu = ob->data; - if (((ob->type == OB_CURVE) && !(cu->flag & CU_3D)) && (apply_rot || apply_loc)) { + if (((ob->type == OB_CURVES_LEGACY) && !(cu->flag & CU_3D)) && (apply_rot || apply_loc)) { BKE_reportf( reports, RPT_ERROR, @@ -811,7 +811,7 @@ static int apply_objects_internal(bContext *C, MetaBall *mb = ob->data; BKE_mball_transform(mb, mat, do_props); } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { Curve *cu = ob->data; scale = mat3_to_scale(rsmat); BKE_curve_transform_ex(cu, mat, true, do_props, scale); @@ -1209,7 +1209,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) do_inverse_offset = true; } } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { Curve *cu = ob->data; if (centermode == ORIGIN_TO_CURSOR) { @@ -1223,7 +1223,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) } /* don't allow Z change if curve is 2D */ - if ((ob->type == OB_CURVE) && !(cu->flag & CU_3D)) { + if ((ob->type == OB_CURVES_LEGACY) && !(cu->flag & CU_3D)) { cent[2] = 0.0; } @@ -1861,7 +1861,7 @@ static int object_transform_axis_target_modal(bContext *C, wmOperator *op, const view3d_operator_needs_opengl(C); - const bool is_translate = (event->ctrl != 0); + const bool is_translate = event->modifier & KM_CTRL; const bool is_translate_init = is_translate && (xfd->is_translate != is_translate); if (event->type == MOUSEMOVE || is_translate_init) { diff --git a/source/blender/editors/object/object_utils.c b/source/blender/editors/object/object_utils.c index 19940fbb0fc..cb9c8a92abe 100644 --- a/source/blender/editors/object/object_utils.c +++ b/source/blender/editors/object/object_utils.c @@ -64,7 +64,7 @@ bool ED_object_calc_active_center_for_editmode(Object *obedit, break; } - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: { Curve *cu = obedit->data; diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c index af9496263e7..fc815ebe682 100644 --- a/source/blender/editors/physics/particle_edit.c +++ b/source/blender/editors/physics/particle_edit.c @@ -4684,7 +4684,7 @@ static int brush_edit_init(bContext *C, wmOperator *op) bedit->ob = ob; bedit->edit = edit; - bedit->zfac = ED_view3d_calc_zfac(region->regiondata, min, NULL); + bedit->zfac = ED_view3d_calc_zfac(region->regiondata, min); /* cache view depths and settings for re-use */ PE_set_view3d_data(C, &bedit->data); @@ -4757,7 +4757,7 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr) switch (pset->brushtype) { case PE_BRUSH_COMB: { - const float mval_f[2] = {dx, dy}; + const float xy_delta[2] = {dx, dy}; data.mval = mval; data.rad = pe_brush_size_get(scene, brush); @@ -4771,7 +4771,7 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr) invert_m4_m4(ob->imat, ob->obmat); - ED_view3d_win_to_delta(region, mval_f, vec, bedit->zfac); + ED_view3d_win_to_delta(region, xy_delta, bedit->zfac, vec); data.dvec = vec; foreach_mouse_hit_key(&data, brush_comb, selected); @@ -4963,7 +4963,7 @@ static void brush_edit_apply_event(bContext *C, wmOperator *op, const wmEvent *e RNA_collection_add(op->ptr, "stroke", &itemptr); RNA_float_set_array(&itemptr, "mouse", mouse); - RNA_boolean_set(&itemptr, "pen_flip", event->shift != false); /* XXX hardcoded */ + RNA_boolean_set(&itemptr, "pen_flip", event->modifier & KM_SHIFT); /* XXX hardcoded */ /* apply */ brush_edit_apply(C, op, &itemptr); diff --git a/source/blender/editors/render/render_internal.cc b/source/blender/editors/render/render_internal.cc index 5308d4611e7..03c0f3977b7 100644 --- a/source/blender/editors/render/render_internal.cc +++ b/source/blender/editors/render/render_internal.cc @@ -618,6 +618,12 @@ static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrec static void current_scene_update(void *rjv, Scene *scene) { RenderJob *rj = static_cast<RenderJob *>(rjv); + + if (rj->current_scene != scene) { + /* Image must be updated when rendered scene changes. */ + BKE_image_partial_update_mark_full_update(rj->image); + } + rj->current_scene = scene; rj->iuser.scene = scene; } @@ -1188,5 +1194,6 @@ void RENDER_OT_shutter_curve_preset(wmOperatorType *ot) ot->exec = render_shutter_curve_preset_exec; prop = RNA_def_enum(ot->srna, "shape", prop_shape_items, CURVE_PRESET_SMOOTH, "Mode", ""); - RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */ + RNA_def_property_translation_context(prop, + BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */ } diff --git a/source/blender/editors/render/render_opengl.cc b/source/blender/editors/render/render_opengl.cc index 1ca2b01a2cb..be66e87f2e5 100644 --- a/source/blender/editors/render/render_opengl.cc +++ b/source/blender/editors/render/render_opengl.cc @@ -599,30 +599,30 @@ static int gather_frames_to_render_for_id(LibraryIDLinkCallbackData *cb_data) const ID_Type id_type = GS(id->name); switch (id_type) { /* Whitelist: */ - case ID_ME: /* Mesh */ - case ID_CU: /* Curve */ - case ID_MB: /* MetaBall */ - case ID_MA: /* Material */ - case ID_TE: /* Tex (Texture) */ - case ID_IM: /* Image */ - case ID_LT: /* Lattice */ - case ID_LA: /* Light */ - case ID_CA: /* Camera */ - case ID_KE: /* Key (shape key) */ - case ID_VF: /* VFont (Vector Font) */ - case ID_TXT: /* Text */ - case ID_SPK: /* Speaker */ - case ID_SO: /* Sound */ - case ID_AR: /* bArmature */ - case ID_NT: /* bNodeTree */ - case ID_PA: /* ParticleSettings */ - case ID_MC: /* MovieClip */ - case ID_MSK: /* Mask */ - case ID_LP: /* LightProbe */ - case ID_CV: /* Curves */ - case ID_PT: /* PointCloud */ - case ID_VO: /* Volume */ - case ID_SIM: /* Simulation */ + case ID_ME: /* Mesh */ + case ID_CU_LEGACY: /* Curve */ + case ID_MB: /* MetaBall */ + case ID_MA: /* Material */ + case ID_TE: /* Tex (Texture) */ + case ID_IM: /* Image */ + case ID_LT: /* Lattice */ + case ID_LA: /* Light */ + case ID_CA: /* Camera */ + case ID_KE: /* Key (shape key) */ + case ID_VF: /* VFont (Vector Font) */ + case ID_TXT: /* Text */ + case ID_SPK: /* Speaker */ + case ID_SO: /* Sound */ + case ID_AR: /* bArmature */ + case ID_NT: /* bNodeTree */ + case ID_PA: /* ParticleSettings */ + case ID_MC: /* MovieClip */ + case ID_MSK: /* Mask */ + case ID_LP: /* LightProbe */ + case ID_CV: /* Curves */ + case ID_PT: /* PointCloud */ + case ID_VO: /* Volume */ + case ID_SIM: /* Simulation */ break; /* Blacklist: */ diff --git a/source/blender/editors/render/render_preview.cc b/source/blender/editors/render/render_preview.cc index 6aaf551a88a..eca30a6ac25 100644 --- a/source/blender/editors/render/render_preview.cc +++ b/source/blender/editors/render/render_preview.cc @@ -10,6 +10,7 @@ #include <cmath> #include <cstdlib> #include <cstring> +#include <list> #ifndef WIN32 # include <unistd.h> @@ -1370,89 +1371,73 @@ static void icon_preview_startjob(void *customdata, short *stop, short *do_updat ShaderPreview *sp = static_cast<ShaderPreview *>(customdata); if (sp->pr_method == PR_ICON_DEFERRED) { - PreviewImage *prv = static_cast<PreviewImage *>(sp->owner); - ImBuf *thumb; - char *deferred_data = static_cast<char *>(PRV_DEFERRED_DATA(prv)); - ThumbSource source = static_cast<ThumbSource>(deferred_data[0]); - char *path = &deferred_data[1]; - - // printf("generating deferred %d×%d preview for %s\n", sp->sizex, sp->sizey, path); - - thumb = IMB_thumb_manage(path, THB_LARGE, source); - - if (thumb) { - /* PreviewImage assumes premultiplied alhpa... */ - IMB_premultiply_alpha(thumb); - - icon_copy_rect(thumb, sp->sizex, sp->sizey, sp->pr_rect); - IMB_freeImBuf(thumb); - } + BLI_assert_unreachable(); + return; } - else { - ID *id = sp->id; - short idtype = GS(id->name); - BLI_assert(id != nullptr); - - if (idtype == ID_IM) { - Image *ima = (Image *)id; - ImBuf *ibuf = nullptr; - ImageUser iuser; - BKE_imageuser_default(&iuser); + ID *id = sp->id; + short idtype = GS(id->name); - if (ima == nullptr) { - return; - } + BLI_assert(id != nullptr); - /* setup dummy image user */ - iuser.framenr = 1; - iuser.scene = sp->scene; - - /* NOTE(@elubie): this needs to be changed: here image is always loaded if not - * already there. Very expensive for large images. Need to find a way to - * only get existing `ibuf`. */ - ibuf = BKE_image_acquire_ibuf(ima, &iuser, nullptr); - if (ibuf == nullptr || (ibuf->rect == nullptr && ibuf->rect_float == nullptr)) { - BKE_image_release_ibuf(ima, ibuf, nullptr); - return; - } + if (idtype == ID_IM) { + Image *ima = (Image *)id; + ImBuf *ibuf = nullptr; + ImageUser iuser; + BKE_imageuser_default(&iuser); - icon_copy_rect(ibuf, sp->sizex, sp->sizey, sp->pr_rect); + if (ima == nullptr) { + return; + } - *do_update = true; + /* setup dummy image user */ + iuser.framenr = 1; + iuser.scene = sp->scene; + /* NOTE(@elubie): this needs to be changed: here image is always loaded if not + * already there. Very expensive for large images. Need to find a way to + * only get existing `ibuf`. */ + ibuf = BKE_image_acquire_ibuf(ima, &iuser, nullptr); + if (ibuf == nullptr || (ibuf->rect == nullptr && ibuf->rect_float == nullptr)) { BKE_image_release_ibuf(ima, ibuf, nullptr); + return; } - else if (idtype == ID_BR) { - Brush *br = (Brush *)id; - br->icon_imbuf = icon_preview_imbuf_from_brush(br); + icon_copy_rect(ibuf, sp->sizex, sp->sizey, sp->pr_rect); - memset(sp->pr_rect, 0x88, sp->sizex * sp->sizey * sizeof(uint)); + *do_update = true; - if (!(br->icon_imbuf) || !(br->icon_imbuf->rect)) { - return; - } + BKE_image_release_ibuf(ima, ibuf, nullptr); + } + else if (idtype == ID_BR) { + Brush *br = (Brush *)id; - icon_copy_rect(br->icon_imbuf, sp->sizex, sp->sizey, sp->pr_rect); + br->icon_imbuf = icon_preview_imbuf_from_brush(br); - *do_update = true; - } - else if (idtype == ID_SCR) { - bScreen *screen = (bScreen *)id; + memset(sp->pr_rect, 0x88, sp->sizex * sp->sizey * sizeof(uint)); - ED_screen_preview_render(screen, sp->sizex, sp->sizey, sp->pr_rect); - *do_update = true; + if (!(br->icon_imbuf) || !(br->icon_imbuf->rect)) { + return; } - else { - /* re-use shader job */ - shader_preview_startjob(customdata, stop, do_update); - /* world is rendered with alpha=0, so it wasn't displayed - * this could be render option for sky to, for later */ - if (idtype == ID_WO) { - set_alpha((char *)sp->pr_rect, sp->sizex, sp->sizey, 255); - } + icon_copy_rect(br->icon_imbuf, sp->sizex, sp->sizey, sp->pr_rect); + + *do_update = true; + } + else if (idtype == ID_SCR) { + bScreen *screen = (bScreen *)id; + + ED_screen_preview_render(screen, sp->sizex, sp->sizey, sp->pr_rect); + *do_update = true; + } + else { + /* re-use shader job */ + shader_preview_startjob(customdata, stop, do_update); + + /* world is rendered with alpha=0, so it wasn't displayed + * this could be render option for sky to, for later */ + if (idtype == ID_WO) { + set_alpha((char *)sp->pr_rect, sp->sizex, sp->sizey, 255); } } } @@ -1670,6 +1655,197 @@ static void icon_preview_endjob(void *customdata) } } +/** + * Background job to manage requests for deferred loading of previews from the hard drive. + * + * Launches a single job to manage all incoming preview requests. The job is kept running until all + * preview requests are done loading (or it's otherwise aborted, e.g. by closing Blender). + * + * Note that this will use the OS thumbnail cache, i.e. load a preview from there or add it if not + * there yet. These two cases may lead to different performance. + */ +class PreviewLoadJob { + struct RequestedPreview { + PreviewImage *preview; + /** Requested size. */ + eIconSizes icon_size; + }; + + /** The previews that are still to be loaded. */ + ThreadQueue *todo_queue_; /* RequestedPreview * */ + /** All unfinished preview requests, #update_fn() calls #finish_preview_request() on loaded + * previews and removes them from this list. Only access from the main thread! */ + std::list<struct RequestedPreview> requested_previews_; + + public: + PreviewLoadJob(); + ~PreviewLoadJob(); + + static PreviewLoadJob &ensure_job(wmWindowManager *, wmWindow *); + static void load_jobless(PreviewImage *, eIconSizes); + + void push_load_request(PreviewImage *, eIconSizes); + + private: + static void run_fn(void *, short *, short *, float *); + static void update_fn(void *); + static void end_fn(void *); + static void free_fn(void *); + + /** Mark a single requested preview as being done, remove the request. */ + static void finish_request(RequestedPreview &); +}; + +PreviewLoadJob::PreviewLoadJob() : todo_queue_(BLI_thread_queue_init()) +{ +} + +PreviewLoadJob::~PreviewLoadJob() +{ + BLI_thread_queue_free(todo_queue_); +} + +PreviewLoadJob &PreviewLoadJob::ensure_job(wmWindowManager *wm, wmWindow *win) +{ + wmJob *wm_job = WM_jobs_get(wm, win, nullptr, "Load Previews", 0, WM_JOB_TYPE_LOAD_PREVIEW); + + if (!WM_jobs_is_running(wm_job)) { + PreviewLoadJob *job_data = MEM_new<PreviewLoadJob>("PreviewLoadJobData"); + + WM_jobs_customdata_set(wm_job, job_data, free_fn); + WM_jobs_timer(wm_job, 0.1, NC_WINDOW, NC_WINDOW); + WM_jobs_callbacks(wm_job, run_fn, nullptr, update_fn, end_fn); + + WM_jobs_start(wm, wm_job); + } + + return *reinterpret_cast<PreviewLoadJob *>(WM_jobs_customdata_get(wm_job)); +} + +void PreviewLoadJob::load_jobless(PreviewImage *preview, const eIconSizes icon_size) +{ + PreviewLoadJob job_data{}; + + job_data.push_load_request(preview, icon_size); + + short stop = 0, do_update = 0; + float progress = 0; + run_fn(&job_data, &stop, &do_update, &progress); + update_fn(&job_data); + end_fn(&job_data); +} + +void PreviewLoadJob::push_load_request(PreviewImage *preview, const eIconSizes icon_size) +{ + BLI_assert(preview->tag & PRV_TAG_DEFFERED); + RequestedPreview requested_preview{}; + requested_preview.preview = preview; + requested_preview.icon_size = icon_size; + + preview->flag[icon_size] |= PRV_RENDERING; + /* Warn main thread code that this preview is being rendered and cannot be freed. */ + preview->tag |= PRV_TAG_DEFFERED_RENDERING; + + requested_previews_.push_back(requested_preview); + BLI_thread_queue_push(todo_queue_, &requested_previews_.back()); +} + +void PreviewLoadJob::run_fn(void *customdata, + short *stop, + short *do_update, + float *UNUSED(progress)) +{ + PreviewLoadJob *job_data = reinterpret_cast<PreviewLoadJob *>(customdata); + + IMB_thumb_locks_acquire(); + + while (RequestedPreview *request = reinterpret_cast<RequestedPreview *>( + BLI_thread_queue_pop_timeout(job_data->todo_queue_, 100))) { + if (*stop) { + break; + } + + PreviewImage *preview = request->preview; + + const char *deferred_data = static_cast<char *>(PRV_DEFERRED_DATA(preview)); + const ThumbSource source = static_cast<ThumbSource>(deferred_data[0]); + const char *path = &deferred_data[1]; + + // printf("loading deferred %d×%d preview for %s\n", request->sizex, request->sizey, path); + + IMB_thumb_path_lock(path); + ImBuf *thumb = IMB_thumb_manage(path, THB_LARGE, source); + IMB_thumb_path_unlock(path); + + if (thumb) { + /* PreviewImage assumes premultiplied alpha... */ + IMB_premultiply_alpha(thumb); + + icon_copy_rect(thumb, + preview->w[request->icon_size], + preview->h[request->icon_size], + preview->rect[request->icon_size]); + IMB_freeImBuf(thumb); + } + + *do_update = true; + } + + IMB_thumb_locks_release(); +} + +/* Only execute on the main thread! */ +void PreviewLoadJob::finish_request(RequestedPreview &request) +{ + PreviewImage *preview = request.preview; + + preview->tag &= ~PRV_TAG_DEFFERED_RENDERING; + BKE_previewimg_finish(preview, request.icon_size); + + BLI_assert_msg(BLI_thread_is_main(), + "Deferred releasing of preview images should only run on the main thread"); + if (preview->tag & PRV_TAG_DEFFERED_DELETE) { + BLI_assert(preview->tag & PRV_TAG_DEFFERED); + BKE_previewimg_deferred_release(preview); + } +} + +void PreviewLoadJob::update_fn(void *customdata) +{ + PreviewLoadJob *job_data = reinterpret_cast<PreviewLoadJob *>(customdata); + + for (auto request_it = job_data->requested_previews_.begin(); + request_it != job_data->requested_previews_.end();) { + RequestedPreview &requested = *request_it; + /* Skip items that are not done loading yet. */ + if (requested.preview->tag & PRV_TAG_DEFFERED_RENDERING) { + ++request_it; + continue; + } + finish_request(requested); + + /* Remove properly finished previews from the job data. */ + auto next_it = job_data->requested_previews_.erase(request_it); + request_it = next_it; + } +} + +void PreviewLoadJob::end_fn(void *customdata) +{ + PreviewLoadJob *job_data = reinterpret_cast<PreviewLoadJob *>(customdata); + + /* Finish any possibly remaining queued previews. */ + for (RequestedPreview &request : job_data->requested_previews_) { + finish_request(request); + } + job_data->requested_previews_.clear(); +} + +void PreviewLoadJob::free_fn(void *customdata) +{ + MEM_delete(reinterpret_cast<PreviewLoadJob *>(customdata)); +} + static void icon_preview_free(void *customdata) { IconPreview *ip = (IconPreview *)customdata; @@ -1698,8 +1874,19 @@ bool ED_preview_id_is_supported(const ID *id) } void ED_preview_icon_render( - const bContext *C, Scene *scene, ID *id, uint *rect, int sizex, int sizey) + const bContext *C, Scene *scene, PreviewImage *prv_img, ID *id, eIconSizes icon_size) { + /* Deferred loading of previews from the file system. */ + if (prv_img->tag & PRV_TAG_DEFFERED) { + if (prv_img->flag[icon_size] & PRV_RENDERING) { + /* Already in the queue, don't add it again. */ + return; + } + + PreviewLoadJob::load_jobless(prv_img, icon_size); + return; + } + IconPreview ip = {nullptr}; short stop = false, update = false; float progress = 0.0f; @@ -1716,7 +1903,10 @@ void ED_preview_icon_render( ip.id_copy = duplicate_ids(id, true); ip.active_object = CTX_data_active_object(C); - icon_preview_add_size(&ip, rect, sizex, sizey); + prv_img->flag[icon_size] |= PRV_RENDERING; + + icon_preview_add_size( + &ip, prv_img->rect[icon_size], prv_img->w[icon_size], prv_img->h[icon_size]); icon_preview_startjob_all_sizes(&ip, &stop, &update, &progress); @@ -1729,20 +1919,31 @@ void ED_preview_icon_render( } void ED_preview_icon_job( - const bContext *C, void *owner, ID *id, uint *rect, int sizex, int sizey, const bool delay) + const bContext *C, PreviewImage *prv_img, ID *id, eIconSizes icon_size, const bool delay) { - wmJob *wm_job; + /* Deferred loading of previews from the file system. */ + if (prv_img->tag & PRV_TAG_DEFFERED) { + if (prv_img->flag[icon_size] & PRV_RENDERING) { + /* Already in the queue, don't add it again. */ + return; + } + PreviewLoadJob &load_job = PreviewLoadJob::ensure_job(CTX_wm_manager(C), CTX_wm_window(C)); + load_job.push_load_request(prv_img, icon_size); + + return; + } + IconPreview *ip, *old_ip; ED_preview_ensure_dbase(); /* suspended start means it starts after 1 timer step, see WM_jobs_timer below */ - wm_job = WM_jobs_get(CTX_wm_manager(C), - CTX_wm_window(C), - owner, - "Icon Preview", - WM_JOB_EXCL_RENDER, - WM_JOB_TYPE_RENDER_PREVIEW); + wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), + CTX_wm_window(C), + prv_img, + "Icon Preview", + WM_JOB_EXCL_RENDER, + WM_JOB_TYPE_RENDER_PREVIEW); ip = MEM_cnew<IconPreview>("icon preview"); @@ -1757,20 +1958,14 @@ void ED_preview_icon_job( ip->depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ip->scene = DEG_get_input_scene(ip->depsgraph); ip->active_object = CTX_data_active_object(C); - ip->owner = owner; + ip->owner = prv_img; ip->id = id; ip->id_copy = duplicate_ids(id, false); - icon_preview_add_size(ip, rect, sizex, sizey); + prv_img->flag[icon_size] |= PRV_RENDERING; - /* Special threading hack: - * warn main code that this preview is being rendered and cannot be freed... */ - { - PreviewImage *prv_img = static_cast<PreviewImage *>(owner); - if (prv_img->tag & PRV_TAG_DEFFERED) { - prv_img->tag |= PRV_TAG_DEFFERED_RENDERING; - } - } + icon_preview_add_size( + ip, prv_img->rect[icon_size], prv_img->w[icon_size], prv_img->h[icon_size]); /* setup job */ WM_jobs_customdata_set(wm_job, ip, icon_preview_free); diff --git a/source/blender/editors/render/render_shading.cc b/source/blender/editors/render/render_shading.cc index bad4208cf19..cd395674177 100644 --- a/source/blender/editors/render/render_shading.cc +++ b/source/blender/editors/render/render_shading.cc @@ -310,7 +310,7 @@ static int material_slot_assign_exec(bContext *C, wmOperator *UNUSED(op)) } } } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { Nurb *nu; ListBase *nurbs = BKE_curve_editNurbs_get((Curve *)ob->data); @@ -411,7 +411,7 @@ static int material_slot_de_select(bContext *C, bool select) changed = EDBM_deselect_by_material(em, mat_nr_active, select); } } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { ListBase *nurbs = BKE_curve_editNurbs_get((Curve *)ob->data); Nurb *nu; BPoint *bp; diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 95534d2a036..af84f6f99a9 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -3438,6 +3438,37 @@ bool ED_area_is_global(const ScrArea *area) return area->global != NULL; } +ScrArea *ED_area_find_under_cursor(const bContext *C, int spacetype, const int xy[2]) +{ + bScreen *screen = CTX_wm_screen(C); + wmWindow *win = CTX_wm_window(C); + + ScrArea *area = NULL; + + if (win->parent) { + /* If active window is a child, check itself first. */ + area = BKE_screen_find_area_xy(screen, spacetype, xy); + } + + if (!area) { + /* Check all windows except the active one. */ + int scr_pos[2]; + wmWindow *r_win = WM_window_find_under_cursor(win, xy, scr_pos); + if (r_win && r_win != win) { + win = r_win; + screen = WM_window_get_active_screen(win); + area = BKE_screen_find_area_xy(screen, spacetype, scr_pos); + } + } + + if (!area && !win->parent) { + /* If active window is a parent window, check itself last. */ + area = BKE_screen_find_area_xy(screen, spacetype, xy); + } + + return area; +} + ScrArea *ED_screen_areas_iter_first(const wmWindow *win, const bScreen *screen) { ScrArea *global_area = win->global_areas.areabase.first; diff --git a/source/blender/editors/screen/glutil.c b/source/blender/editors/screen/glutil.c index f43bc08151a..8a84f4cf079 100644 --- a/source/blender/editors/screen/glutil.c +++ b/source/blender/editors/screen/glutil.c @@ -77,8 +77,9 @@ void immDrawPixelsTexScaledFullSize(const IMMDrawPixelsTexState *state, * filtering results. Mipmaps can be used to get better results (i.e. #GL_LINEAR_MIPMAP_LINEAR), * so always use mipmaps when filtering. */ const bool use_mipmap = use_filter && ((draw_width < img_w) || (draw_height < img_h)); + const int mips = use_mipmap ? 9999 : 1; - GPUTexture *tex = GPU_texture_create_2d("immDrawPixels", img_w, img_h, 1, gpu_format, NULL); + GPUTexture *tex = GPU_texture_create_2d("immDrawPixels", img_w, img_h, mips, gpu_format, NULL); const bool use_float_data = ELEM(gpu_format, GPU_RGBA16F, GPU_RGB16F, GPU_R16F); eGPUDataFormat gpu_data_format = (use_float_data) ? GPU_DATA_FLOAT : GPU_DATA_UBYTE; diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 7171aa7ac3b..ee3bc3cba76 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -585,7 +585,7 @@ bool ED_operator_uvmap(bContext *C) bool ED_operator_editsurfcurve(bContext *C) { Object *obedit = CTX_data_edit_object(C); - if (obedit && ELEM(obedit->type, OB_CURVE, OB_SURF)) { + if (obedit && ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) { return NULL != ((Curve *)obedit->data)->editnurb; } return false; @@ -604,7 +604,7 @@ bool ED_operator_editsurfcurve_region_view3d(bContext *C) bool ED_operator_editcurve(bContext *C) { Object *obedit = CTX_data_edit_object(C); - if (obedit && obedit->type == OB_CURVE) { + if (obedit && obedit->type == OB_CURVES_LEGACY) { return NULL != ((Curve *)obedit->data)->editnurb; } return false; @@ -613,7 +613,7 @@ bool ED_operator_editcurve(bContext *C) bool ED_operator_editcurve_3d(bContext *C) { Object *obedit = CTX_data_edit_object(C); - if (obedit && obedit->type == OB_CURVE) { + if (obedit && obedit->type == OB_CURVES_LEGACY) { Curve *cu = (Curve *)obedit->data; return (cu->flag & CU_3D) && (NULL != cu->editnurb); @@ -1008,9 +1008,6 @@ static void actionzone_exit(wmOperator *op) static void actionzone_apply(bContext *C, wmOperator *op, int type) { wmWindow *win = CTX_wm_window(C); - sActionzoneData *sad = op->customdata; - - sad->modifier = RNA_int_get(op->ptr, "modifier"); wmEvent event; wm_event_init_from_window(win, &event); @@ -1026,7 +1023,7 @@ static void actionzone_apply(bContext *C, wmOperator *op, int type) } event.val = KM_NOTHING; - event.is_repeat = false; + event.flag = 0; event.customdata = op->customdata; event.customdata_free = true; op->customdata = NULL; @@ -1051,6 +1048,7 @@ static int actionzone_invoke(bContext *C, wmOperator *op, const wmEvent *event) sad->az = az; sad->x = event->xy[0]; sad->y = event->xy[1]; + sad->modifier = RNA_int_get(op->ptr, "modifier"); /* region azone directly reacts on mouse clicks */ if (ELEM(sad->az->type, AZONE_REGION, AZONE_FULLSCREEN)) { @@ -1114,7 +1112,17 @@ static int actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event) /* What area are we now in? */ ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->xy); - if (area == sad->sa1) { + if (sad->modifier == 1) { + /* Duplicate area into new window. */ + WM_cursor_set(win, WM_CURSOR_EDIT); + is_gesture = (delta_max > area_threshold); + } + else if (sad->modifier == 2) { + /* Swap areas. */ + WM_cursor_set(win, WM_CURSOR_SWAP_AREA); + is_gesture = true; + } + else if (area == sad->sa1) { /* Same area, so possible split. */ WM_cursor_set(win, SCREEN_DIR_IS_VERTICAL(sad->gesture_dir) ? WM_CURSOR_H_SPLIT : @@ -1320,8 +1328,9 @@ static int area_swap_modal(bContext *C, wmOperator *op, const wmEvent *event) switch (event->type) { case MOUSEMOVE: - /* second area, for join */ - sad->sa2 = BKE_screen_find_area_xy(CTX_wm_screen(C), SPACE_TYPE_ANY, event->xy); + /* Second area to swap with. */ + sad->sa2 = ED_area_find_under_cursor(C, SPACE_TYPE_ANY, event->xy); + WM_cursor_set(CTX_wm_window(C), (sad->sa2) ? WM_CURSOR_SWAP_AREA : WM_CURSOR_STOP); break; case LEFTMOUSE: /* release LMB */ if (event->val == KM_RELEASE) { diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index 55099e2e750..59fbc3a64fb 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -9,6 +9,7 @@ set(INC ../../bmesh ../../depsgraph ../../draw + ../../functions ../../gpu ../../imbuf ../../makesdna @@ -24,11 +25,13 @@ set(INC ) set(SRC + curves_sculpt_ops.cc paint_cursor.c paint_curve.c paint_curve_undo.c paint_hide.c - paint_image.c + paint_image.cc + paint_image_ops_paint.cc paint_image_2d.c paint_image_2d_curve_mask.cc paint_image_proj.c @@ -66,6 +69,7 @@ set(SRC sculpt_undo.c sculpt_uv.c + curves_sculpt_intern.h paint_intern.h sculpt_intern.h ) @@ -75,5 +79,16 @@ set(LIB bf_blenlib ) +if(WITH_TBB) + list(APPEND INC_SYS + ${TBB_INCLUDE_DIRS} + ) + add_definitions(-DWITH_TBB) + if(WIN32) + # TBB includes Windows.h which will define min/max macros + # that will collide with the stl versions. + add_definitions(-DNOMINMAX) + endif() +endif() blender_add_lib(bf_editor_sculpt_paint "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_intern.h b/source/blender/editors/sculpt_paint/curves_sculpt_intern.h new file mode 100644 index 00000000000..0d99e61192f --- /dev/null +++ b/source/blender/editors/sculpt_paint/curves_sculpt_intern.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +struct bContext; + +#ifdef __cplusplus +extern "C" { +#endif + +bool CURVES_SCULPT_mode_poll(struct bContext *C); +bool CURVES_SCULPT_mode_poll_view3d(struct bContext *C); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc new file mode 100644 index 00000000000..936226a03ed --- /dev/null +++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc @@ -0,0 +1,411 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_utildefines.h" + +#include "BKE_brush.h" +#include "BKE_context.h" +#include "BKE_curves.hh" +#include "BKE_paint.h" + +#include "WM_api.h" +#include "WM_toolsystem.h" + +#include "ED_curves_sculpt.h" +#include "ED_object.h" +#include "ED_screen.h" +#include "ED_view3d.h" + +#include "DEG_depsgraph.h" + +#include "DNA_brush_types.h" +#include "DNA_curves_types.h" +#include "DNA_screen_types.h" + +#include "RNA_access.h" + +#include "BLI_index_mask_ops.hh" +#include "BLI_math_vector.hh" + +#include "curves_sculpt_intern.h" +#include "paint_intern.h" + +/* -------------------------------------------------------------------- */ +/** \name Poll Functions + * \{ */ + +bool CURVES_SCULPT_mode_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + return ob && ob->mode & OB_MODE_SCULPT_CURVES; +} + +bool CURVES_SCULPT_mode_poll_view3d(bContext *C) +{ + if (!CURVES_SCULPT_mode_poll(C)) { + return false; + } + if (CTX_wm_region_view3d(C) == nullptr) { + return false; + } + return true; +} + +/** \} */ + +namespace blender::ed::sculpt_paint { + +using blender::bke::CurvesGeometry; + +/* -------------------------------------------------------------------- */ +/** \name * SCULPT_CURVES_OT_brush_stroke + * \{ */ + +struct StrokeExtension { + bool is_first; + float2 mouse_position; +}; + +/** + * Base class for stroke based operations in curves sculpt mode. + */ +class CurvesSculptStrokeOperation { + public: + virtual ~CurvesSculptStrokeOperation() = default; + virtual void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) = 0; +}; + +class DeleteOperation : public CurvesSculptStrokeOperation { + private: + float2 last_mouse_position_; + + public: + void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) + { + Scene &scene = *CTX_data_scene(C); + Object &object = *CTX_data_active_object(C); + ARegion *region = CTX_wm_region(C); + RegionView3D *rv3d = CTX_wm_region_view3d(C); + + CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt; + Brush &brush = *BKE_paint_brush(&curves_sculpt.paint); + const float brush_radius = BKE_brush_size_get(&scene, &brush); + + float4x4 projection; + ED_view3d_ob_project_mat_get(rv3d, &object, projection.values); + + Curves &curves_id = *static_cast<Curves *>(object.data); + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); + MutableSpan<float3> positions = curves.positions(); + + const float2 mouse_start = stroke_extension.is_first ? stroke_extension.mouse_position : + last_mouse_position_; + const float2 mouse_end = stroke_extension.mouse_position; + + /* Find indices of curves that have to be removed. */ + Vector<int64_t> indices; + const IndexMask curves_to_remove = index_mask_ops::find_indices_based_on_predicate( + curves.curves_range(), 512, indices, [&](const int curve_i) { + const IndexRange point_range = curves.range_for_curve(curve_i); + for (const int segment_i : IndexRange(point_range.size() - 1)) { + const float3 pos1 = positions[point_range[segment_i]]; + const float3 pos2 = positions[point_range[segment_i + 1]]; + + float2 pos1_proj, pos2_proj; + ED_view3d_project_float_v2_m4(region, pos1, pos1_proj, projection.values); + ED_view3d_project_float_v2_m4(region, pos2, pos2_proj, projection.values); + + const float dist = dist_seg_seg_v2(pos1_proj, pos2_proj, mouse_start, mouse_end); + if (dist <= brush_radius) { + return true; + } + } + return false; + }); + + /* Just reset positions instead of actually removing the curves. This is just a prototype. */ + threading::parallel_for(curves_to_remove.index_range(), 512, [&](const IndexRange range) { + for (const int curve_i : curves_to_remove.slice(range)) { + for (const int point_i : curves.range_for_curve(curve_i)) { + positions[point_i] = {0.0f, 0.0f, 0.0f}; + } + } + }); + + curves.tag_positions_changed(); + DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY); + ED_region_tag_redraw(region); + + last_mouse_position_ = stroke_extension.mouse_position; + } +}; + +class MoveOperation : public CurvesSculptStrokeOperation { + private: + Vector<int64_t> points_to_move_indices_; + IndexMask points_to_move_; + float2 last_mouse_position_; + + public: + void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) + { + Scene &scene = *CTX_data_scene(C); + Object &object = *CTX_data_active_object(C); + ARegion *region = CTX_wm_region(C); + View3D *v3d = CTX_wm_view3d(C); + RegionView3D *rv3d = CTX_wm_region_view3d(C); + + CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt; + Brush &brush = *BKE_paint_brush(&curves_sculpt.paint); + const float brush_radius = BKE_brush_size_get(&scene, &brush); + + float4x4 projection; + ED_view3d_ob_project_mat_get(rv3d, &object, projection.values); + + Curves &curves_id = *static_cast<Curves *>(object.data); + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); + MutableSpan<float3> positions = curves.positions(); + + if (stroke_extension.is_first) { + /* Find point indices to move. */ + points_to_move_ = index_mask_ops::find_indices_based_on_predicate( + curves.points_range(), 512, points_to_move_indices_, [&](const int64_t point_i) { + const float3 position = positions[point_i]; + float2 screen_position; + ED_view3d_project_float_v2_m4(region, position, screen_position, projection.values); + const float distance = len_v2v2(screen_position, stroke_extension.mouse_position); + return distance <= brush_radius; + }); + } + else { + /* Move points based on mouse movement. */ + const float2 mouse_diff = stroke_extension.mouse_position - last_mouse_position_; + threading::parallel_for(points_to_move_.index_range(), 512, [&](const IndexRange range) { + for (const int point_i : points_to_move_.slice(range)) { + const float3 old_position = positions[point_i]; + float2 old_position_screen; + ED_view3d_project_float_v2_m4( + region, old_position, old_position_screen, projection.values); + const float2 new_position_screen = old_position_screen + mouse_diff; + float3 new_position; + ED_view3d_win_to_3d(v3d, region, old_position, new_position_screen, new_position); + positions[point_i] = new_position; + } + }); + + curves.tag_positions_changed(); + DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY); + ED_region_tag_redraw(region); + } + + last_mouse_position_ = stroke_extension.mouse_position; + } +}; + +static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(bContext *C, + wmOperator *UNUSED(op)) +{ + Scene &scene = *CTX_data_scene(C); + CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt; + Brush &brush = *BKE_paint_brush(&curves_sculpt.paint); + switch (brush.curves_sculpt_tool) { + case CURVES_SCULPT_TOOL_TEST1: + return std::make_unique<MoveOperation>(); + case CURVES_SCULPT_TOOL_TEST2: + return std::make_unique<DeleteOperation>(); + } + BLI_assert_unreachable(); + return {}; +} + +struct SculptCurvesBrushStrokeData { + std::unique_ptr<CurvesSculptStrokeOperation> operation; + PaintStroke *stroke; +}; + +static bool stroke_get_location(bContext *C, float out[3], const float mouse[2]) +{ + out[0] = mouse[0]; + out[1] = mouse[1]; + out[2] = 0; + UNUSED_VARS(C); + return true; +} + +static bool stroke_test_start(bContext *C, struct wmOperator *op, const float mouse[2]) +{ + UNUSED_VARS(C, op, mouse); + return true; +} + +static void stroke_update_step(bContext *C, + wmOperator *op, + PaintStroke *UNUSED(stroke), + PointerRNA *stroke_element) +{ + SculptCurvesBrushStrokeData *op_data = static_cast<SculptCurvesBrushStrokeData *>( + op->customdata); + + StrokeExtension stroke_extension; + RNA_float_get_array(stroke_element, "mouse", stroke_extension.mouse_position); + + if (!op_data->operation) { + stroke_extension.is_first = true; + op_data->operation = start_brush_operation(C, op); + } + else { + stroke_extension.is_first = false; + } + + op_data->operation->on_stroke_extended(C, stroke_extension); +} + +static void stroke_done(const bContext *C, PaintStroke *stroke) +{ + UNUSED_VARS(C, stroke); +} + +static int sculpt_curves_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + SculptCurvesBrushStrokeData *op_data = MEM_new<SculptCurvesBrushStrokeData>(__func__); + op_data->stroke = paint_stroke_new(C, + op, + stroke_get_location, + stroke_test_start, + stroke_update_step, + nullptr, + stroke_done, + event->type); + op->customdata = op_data; + + int return_value = op->type->modal(C, op, event); + if (return_value == OPERATOR_FINISHED) { + paint_stroke_free(C, op, op_data->stroke); + MEM_delete(op_data); + return OPERATOR_FINISHED; + } + + WM_event_add_modal_handler(C, op); + return OPERATOR_RUNNING_MODAL; +} + +static int sculpt_curves_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + SculptCurvesBrushStrokeData *op_data = static_cast<SculptCurvesBrushStrokeData *>( + op->customdata); + int return_value = paint_stroke_modal(C, op, event, op_data->stroke); + if (ELEM(return_value, OPERATOR_FINISHED, OPERATOR_CANCELLED)) { + MEM_delete(op_data); + } + return return_value; +} + +static void sculpt_curves_stroke_cancel(bContext *C, wmOperator *op) +{ + SculptCurvesBrushStrokeData *op_data = static_cast<SculptCurvesBrushStrokeData *>( + op->customdata); + paint_stroke_cancel(C, op, op_data->stroke); + MEM_delete(op_data); +} + +static void SCULPT_CURVES_OT_brush_stroke(struct wmOperatorType *ot) +{ + ot->name = "Stroke Curves Sculpt"; + ot->idname = "SCULPT_CURVES_OT_brush_stroke"; + ot->description = "Sculpt curves using a brush"; + + ot->invoke = sculpt_curves_stroke_invoke; + ot->modal = sculpt_curves_stroke_modal; + ot->cancel = sculpt_curves_stroke_cancel; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + paint_stroke_operator_properties(ot); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name * CURVES_OT_sculptmode_toggle + * \{ */ + +static bool curves_sculptmode_toggle_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + if (ob == nullptr) { + return false; + } + if (ob->type != OB_CURVES) { + return false; + } + return true; +} + +static void curves_sculptmode_enter(bContext *C) +{ + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->curves_sculpt); + CurvesSculpt *curves_sculpt = scene->toolsettings->curves_sculpt; + + ob->mode = OB_MODE_SCULPT_CURVES; + + paint_cursor_start(&curves_sculpt->paint, CURVES_SCULPT_mode_poll_view3d); +} + +static void curves_sculptmode_exit(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + ob->mode = OB_MODE_OBJECT; +} + +static int curves_sculptmode_toggle_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + const bool is_mode_set = ob->mode == OB_MODE_SCULPT_CURVES; + + if (is_mode_set) { + if (!ED_object_mode_compat_set(C, ob, OB_MODE_SCULPT_CURVES, op->reports)) { + return OPERATOR_CANCELLED; + } + } + + if (is_mode_set) { + curves_sculptmode_exit(C); + } + else { + curves_sculptmode_enter(C); + } + + WM_toolsystem_update_from_context_view3d(C); + WM_event_add_notifier(C, NC_SCENE | ND_MODE, nullptr); + return OPERATOR_CANCELLED; +} + +static void CURVES_OT_sculptmode_toggle(wmOperatorType *ot) +{ + ot->name = "Curve Sculpt Mode Toggle"; + ot->idname = "CURVES_OT_sculptmode_toggle"; + ot->description = "Enter/Exit sculpt mode for curves"; + + ot->exec = curves_sculptmode_toggle_exec; + ot->poll = curves_sculptmode_toggle_poll; + + ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; +} + +} // namespace blender::ed::sculpt_paint + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name * Registration + * \{ */ + +void ED_operatortypes_sculpt_curves() +{ + using namespace blender::ed::sculpt_paint; + WM_operatortype_append(SCULPT_CURVES_OT_brush_stroke); + WM_operatortype_append(CURVES_OT_sculptmode_toggle); +} + +/** \} */ diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.cc index df9e2cf136f..0c73c2e1f43 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.cc @@ -6,10 +6,10 @@ * \brief Functions to paint images in 2D and 3D. */ -#include <float.h> -#include <math.h> -#include <stdio.h> -#include <string.h> +#include <cfloat> +#include <cmath> +#include <cstdio> +#include <cstring> #include "MEM_guardedalloc.h" @@ -67,6 +67,8 @@ #include "paint_intern.h" +extern "C" { + /** * This is a static resource for non-global access. * Maybe it should be exposed as part of the paint operation, @@ -84,7 +86,7 @@ void set_imapaintpartial(struct ImagePaintPartialRedraw *ippr) imapaintpartial = *ippr; } -/* Imagepaint Partial Redraw & Dirty Region */ +/* Image paint Partial Redraw & Dirty Region. */ void ED_imapaint_clear_partial_redraw(void) { @@ -96,7 +98,7 @@ void imapaint_region_tiles( { int srcx = 0, srcy = 0; - IMB_rectclip(ibuf, NULL, &x, &y, &srcx, &srcy, &w, &h); + IMB_rectclip(ibuf, nullptr, &x, &y, &srcx, &srcy, &w, &h); *tw = ((x + w - 1) >> ED_IMAGE_UNDO_TILE_BITS); *th = ((y + h - 1) >> ED_IMAGE_UNDO_TILE_BITS); @@ -107,11 +109,11 @@ void imapaint_region_tiles( void ED_imapaint_dirty_region( Image *ima, ImBuf *ibuf, ImageUser *iuser, int x, int y, int w, int h, bool find_old) { - ImBuf *tmpibuf = NULL; + ImBuf *tmpibuf = nullptr; int tilex, tiley, tilew, tileh, tx, ty; int srcx = 0, srcy = 0; - IMB_rectclip(ibuf, NULL, &x, &y, &srcx, &srcy, &w, &h); + IMB_rectclip(ibuf, nullptr, &x, &y, &srcx, &srcy, &w, &h); if (w == 0 || h == 0) { return; @@ -128,7 +130,7 @@ void ED_imapaint_dirty_region( for (ty = tiley; ty <= tileh; ty++) { for (tx = tilex; tx <= tilew; tx++) { ED_image_paint_tile_push( - undo_tiles, ima, ibuf, &tmpibuf, iuser, tx, ty, NULL, NULL, false, find_old); + undo_tiles, ima, ibuf, &tmpibuf, iuser, tx, ty, nullptr, nullptr, false, find_old); } } @@ -169,17 +171,19 @@ void imapaint_image_update( BlurKernel *paint_new_blur_kernel(Brush *br, bool proj) { int i, j; - BlurKernel *kernel = MEM_mallocN(sizeof(BlurKernel), "blur kernel"); + BlurKernel *kernel = MEM_new<BlurKernel>("BlurKernel"); + float radius; int side; - eBlurKernelType type = br->blur_mode; + eBlurKernelType type = static_cast<eBlurKernelType>(br->blur_mode); if (proj) { radius = 0.5f; side = kernel->side = 2; kernel->side_squared = kernel->side * kernel->side; - kernel->wdata = MEM_mallocN(sizeof(float) * kernel->side_squared, "blur kernel data"); + kernel->wdata = static_cast<float *>( + MEM_mallocN(sizeof(float) * kernel->side_squared, "blur kernel data")); kernel->pixel_len = radius; } else { @@ -191,7 +195,8 @@ BlurKernel *paint_new_blur_kernel(Brush *br, bool proj) side = kernel->side = radius * 2 + 1; kernel->side_squared = kernel->side * kernel->side; - kernel->wdata = MEM_mallocN(sizeof(float) * kernel->side_squared, "blur kernel data"); + kernel->wdata = static_cast<float *>( + MEM_mallocN(sizeof(float) * kernel->side_squared, "blur kernel data")); kernel->pixel_len = br->blur_kernel_radius; } @@ -224,9 +229,9 @@ BlurKernel *paint_new_blur_kernel(Brush *br, bool proj) default: printf("unidentified kernel type, aborting\n"); - MEM_freeN(kernel->wdata); - MEM_freeN(kernel); - return NULL; + paint_delete_blur_kernel(kernel); + MEM_delete(kernel); + return nullptr; } return kernel; @@ -267,7 +272,7 @@ static bool image_paint_poll_ex(bContext *C, bool check_tool) SpaceImage *sima = CTX_wm_space_image(C); if (sima) { - if (sima->image != NULL && ID_IS_LINKED(sima->image)) { + if (sima->image != nullptr && ID_IS_LINKED(sima->image)) { return false; } ARegion *region = CTX_wm_region(C); @@ -281,7 +286,7 @@ static bool image_paint_poll_ex(bContext *C, bool check_tool) return false; } -static bool image_paint_poll(bContext *C) +bool image_paint_poll(bContext *C) { return image_paint_poll_ex(C, true); } @@ -307,24 +312,6 @@ static bool image_paint_2d_clone_poll(bContext *C) } /************************ paint operator ************************/ -typedef enum eTexPaintMode { - PAINT_MODE_2D, - PAINT_MODE_3D_PROJECT, -} eTexPaintMode; - -typedef struct PaintOperation { - eTexPaintMode mode; - - void *custom_paint; - - float prevmouse[2]; - float startmouse[2]; - double starttime; - - void *cursor; - ViewContext vc; -} PaintOperation; - bool paint_use_opacity_masking(Brush *brush) { return ((brush->flag & BRUSH_AIRBRUSH) || (brush->flag & BRUSH_DRAG_DOT) || @@ -369,7 +356,7 @@ void paint_brush_color_get(struct Scene *scene, break; } } - /* Gradient / Colorband colors are not considered PROP_COLOR_GAMMA. + /* Gradient / Color-band colors are not considered #PROP_COLOR_GAMMA. * Brush colors are expected to be in sRGB though. */ IMB_colormanagement_scene_linear_to_srgb_v3(color_gr); @@ -414,340 +401,11 @@ void paint_brush_exit_tex(Brush *brush) } } -static void gradient_draw_line(bContext *UNUSED(C), int x, int y, void *customdata) -{ - PaintOperation *pop = (PaintOperation *)customdata; - - if (pop) { - GPU_line_smooth(true); - GPU_blend(GPU_BLEND_ALPHA); - - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); - - ARegion *region = pop->vc.region; - - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - - GPU_line_width(4.0); - immUniformColor4ub(0, 0, 0, 255); - - immBegin(GPU_PRIM_LINES, 2); - immVertex2i(pos, x, y); - immVertex2i( - pos, pop->startmouse[0] + region->winrct.xmin, pop->startmouse[1] + region->winrct.ymin); - immEnd(); - - GPU_line_width(2.0); - immUniformColor4ub(255, 255, 255, 255); - - immBegin(GPU_PRIM_LINES, 2); - immVertex2i(pos, x, y); - immVertex2i( - pos, pop->startmouse[0] + region->winrct.xmin, pop->startmouse[1] + region->winrct.ymin); - immEnd(); - - immUnbindProgram(); - - GPU_blend(GPU_BLEND_NONE); - GPU_line_smooth(false); - } -} - -static PaintOperation *texture_paint_init(bContext *C, wmOperator *op, const float mouse[2]) -{ - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - Scene *scene = CTX_data_scene(C); - ToolSettings *settings = scene->toolsettings; - PaintOperation *pop = MEM_callocN(sizeof(PaintOperation), "PaintOperation"); /* caller frees */ - Brush *brush = BKE_paint_brush(&settings->imapaint.paint); - int mode = RNA_enum_get(op->ptr, "mode"); - ED_view3d_viewcontext_init(C, &pop->vc, depsgraph); - - copy_v2_v2(pop->prevmouse, mouse); - copy_v2_v2(pop->startmouse, mouse); - - /* initialize from context */ - if (CTX_wm_region_view3d(C)) { - ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob = OBACT(view_layer); - bool uvs, mat, tex, stencil; - if (!ED_paint_proj_mesh_data_check(scene, ob, &uvs, &mat, &tex, &stencil)) { - ED_paint_data_warning(op->reports, uvs, mat, tex, stencil); - MEM_freeN(pop); - WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); - return NULL; - } - pop->mode = PAINT_MODE_3D_PROJECT; - pop->custom_paint = paint_proj_new_stroke(C, ob, mouse, mode); - } - else { - pop->mode = PAINT_MODE_2D; - pop->custom_paint = paint_2d_new_stroke(C, op, mode); - } - - if (!pop->custom_paint) { - MEM_freeN(pop); - return NULL; - } - - if ((brush->imagepaint_tool == PAINT_TOOL_FILL) && (brush->flag & BRUSH_USE_GRADIENT)) { - pop->cursor = WM_paint_cursor_activate( - SPACE_TYPE_ANY, RGN_TYPE_ANY, image_paint_poll, gradient_draw_line, pop); - } - - settings->imapaint.flag |= IMAGEPAINT_DRAWING; - ED_image_undo_push_begin(op->type->name, PAINT_MODE_TEXTURE_2D); - - return pop; -} - -static void paint_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr) -{ - PaintOperation *pop = paint_stroke_mode_data(stroke); - Scene *scene = CTX_data_scene(C); - ToolSettings *toolsettings = CTX_data_tool_settings(C); - UnifiedPaintSettings *ups = &toolsettings->unified_paint_settings; - Brush *brush = BKE_paint_brush(&toolsettings->imapaint.paint); - - float alphafac = (brush->flag & BRUSH_ACCUMULATE) ? ups->overlap_factor : 1.0f; - - /* initial brush values. Maybe it should be considered moving these to stroke system */ - float startalpha = BKE_brush_alpha_get(scene, brush); - - float mouse[2]; - float pressure; - float size; - float distance = paint_stroke_distance_get(stroke); - int eraser; - - RNA_float_get_array(itemptr, "mouse", mouse); - pressure = RNA_float_get(itemptr, "pressure"); - eraser = RNA_boolean_get(itemptr, "pen_flip"); - size = RNA_float_get(itemptr, "size"); - - /* stroking with fill tool only acts on stroke end */ - if (brush->imagepaint_tool == PAINT_TOOL_FILL) { - copy_v2_v2(pop->prevmouse, mouse); - return; - } - - if (BKE_brush_use_alpha_pressure(brush)) { - BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * pressure * alphafac)); - } - else { - BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * alphafac)); - } - - if ((brush->flag & BRUSH_DRAG_DOT) || (brush->flag & BRUSH_ANCHORED)) { - UndoStack *ustack = CTX_wm_manager(C)->undo_stack; - ED_image_undo_restore(ustack->step_init); - } - - if (pop->mode == PAINT_MODE_3D_PROJECT) { - paint_proj_stroke( - C, pop->custom_paint, pop->prevmouse, mouse, eraser, pressure, distance, size); - } - else { - paint_2d_stroke(pop->custom_paint, pop->prevmouse, mouse, eraser, pressure, distance, size); - } - - copy_v2_v2(pop->prevmouse, mouse); - - /* restore brush values */ - BKE_brush_alpha_set(scene, brush, startalpha); -} - -static void paint_stroke_redraw(const bContext *C, struct PaintStroke *stroke, bool final) -{ - PaintOperation *pop = paint_stroke_mode_data(stroke); - - if (pop->mode == PAINT_MODE_3D_PROJECT) { - paint_proj_redraw(C, pop->custom_paint, final); - } - else { - paint_2d_redraw(C, pop->custom_paint, final); - } -} - -static void paint_stroke_done(const bContext *C, struct PaintStroke *stroke) -{ - Scene *scene = CTX_data_scene(C); - ToolSettings *toolsettings = scene->toolsettings; - PaintOperation *pop = paint_stroke_mode_data(stroke); - Brush *brush = BKE_paint_brush(&toolsettings->imapaint.paint); - - toolsettings->imapaint.flag &= ~IMAGEPAINT_DRAWING; - - if (brush->imagepaint_tool == PAINT_TOOL_FILL) { - if (brush->flag & BRUSH_USE_GRADIENT) { - if (pop->mode == PAINT_MODE_2D) { - paint_2d_gradient_fill(C, brush, pop->startmouse, pop->prevmouse, pop->custom_paint); - } - else { - paint_proj_stroke(C, - pop->custom_paint, - pop->startmouse, - pop->prevmouse, - paint_stroke_flipped(stroke), - 1.0, - 0.0, - BKE_brush_size_get(scene, brush)); - /* two redraws, one for GPU update, one for notification */ - paint_proj_redraw(C, pop->custom_paint, false); - paint_proj_redraw(C, pop->custom_paint, true); - } - } - else { - if (pop->mode == PAINT_MODE_2D) { - float color[3]; - if (paint_stroke_inverted(stroke)) { - srgb_to_linearrgb_v3_v3(color, BKE_brush_secondary_color_get(scene, brush)); - } - else { - srgb_to_linearrgb_v3_v3(color, BKE_brush_color_get(scene, brush)); - } - paint_2d_bucket_fill(C, color, brush, pop->startmouse, pop->prevmouse, pop->custom_paint); - } - else { - paint_proj_stroke(C, - pop->custom_paint, - pop->startmouse, - pop->prevmouse, - paint_stroke_flipped(stroke), - 1.0, - 0.0, - BKE_brush_size_get(scene, brush)); - /* two redraws, one for GPU update, one for notification */ - paint_proj_redraw(C, pop->custom_paint, false); - paint_proj_redraw(C, pop->custom_paint, true); - } - } - } - if (pop->mode == PAINT_MODE_3D_PROJECT) { - paint_proj_stroke_done(pop->custom_paint); - } - else { - paint_2d_stroke_done(pop->custom_paint); - } - - if (pop->cursor) { - WM_paint_cursor_end(pop->cursor); - } - - ED_image_undo_push_end(); - - /* duplicate warning, see texpaint_init */ -#if 0 - if (pop->s.warnmultifile) { - BKE_reportf(op->reports, - RPT_WARNING, - "Image requires 4 color channels to paint: %s", - pop->s.warnmultifile); - } - if (pop->s.warnpackedfile) { - BKE_reportf(op->reports, - RPT_WARNING, - "Packed MultiLayer files cannot be painted: %s", - pop->s.warnpackedfile); - } -#endif - MEM_freeN(pop); -} - -static bool paint_stroke_test_start(bContext *C, wmOperator *op, const float mouse[2]) -{ - PaintOperation *pop; - - /* TODO: Should avoid putting this here. Instead, last position should be requested - * from stroke system. */ - - if (!(pop = texture_paint_init(C, op, mouse))) { - return false; - } - - paint_stroke_set_mode_data(op->customdata, pop); - - return true; -} - -static int paint_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - int retval; - - op->customdata = paint_stroke_new(C, - op, - NULL, - paint_stroke_test_start, - paint_stroke_update_step, - paint_stroke_redraw, - paint_stroke_done, - event->type); - - if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { - paint_stroke_free(C, op); - return OPERATOR_FINISHED; - } - /* add modal handler */ - WM_event_add_modal_handler(C, op); - - OPERATOR_RETVAL_CHECK(retval); - BLI_assert(retval == OPERATOR_RUNNING_MODAL); - - return OPERATOR_RUNNING_MODAL; -} - -static int paint_exec(bContext *C, wmOperator *op) -{ - PropertyRNA *strokeprop; - PointerRNA firstpoint; - float mouse[2]; - - strokeprop = RNA_struct_find_property(op->ptr, "stroke"); - - if (!RNA_property_collection_lookup_int(op->ptr, strokeprop, 0, &firstpoint)) { - return OPERATOR_CANCELLED; - } - - RNA_float_get_array(&firstpoint, "mouse", mouse); - - op->customdata = paint_stroke_new(C, - op, - NULL, - paint_stroke_test_start, - paint_stroke_update_step, - paint_stroke_redraw, - paint_stroke_done, - 0); - /* frees op->customdata */ - return paint_stroke_exec(C, op); -} - -void PAINT_OT_image_paint(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Image Paint"; - ot->idname = "PAINT_OT_image_paint"; - ot->description = "Paint a stroke into the image"; - - /* api callbacks */ - ot->invoke = paint_invoke; - ot->modal = paint_stroke_modal; - ot->exec = paint_exec; - ot->poll = image_paint_poll; - ot->cancel = paint_stroke_cancel; - - /* flags */ - ot->flag = OPTYPE_BLOCKING; - - paint_stroke_operator_properties(ot); -} - bool get_imapaint_zoom(bContext *C, float *zoomx, float *zoomy) { ScrArea *area = CTX_wm_area(C); if (area && area->spacetype == SPACE_IMAGE) { - SpaceImage *sima = area->spacedata.first; + SpaceImage *sima = static_cast<SpaceImage *>(area->spacedata.first); if (sima->mode == SI_MODE_PAINT) { ARegion *region = CTX_wm_region(C); ED_space_image_get_zoom(sima, region, zoomx, zoomy); @@ -768,8 +426,8 @@ static void toggle_paint_cursor(Scene *scene, bool enable) Paint *p = &settings->imapaint.paint; if (p->paint_cursor && !enable) { - WM_paint_cursor_end(p->paint_cursor); - p->paint_cursor = NULL; + WM_paint_cursor_end(static_cast<wmPaintCursor *>(p->paint_cursor)); + p->paint_cursor = nullptr; paint_cursor_delete_textures(); } else if (enable) { @@ -807,10 +465,10 @@ void ED_space_image_paint_update(Main *bmain, wmWindowManager *wm, Scene *scene) /************************ grab clone operator ************************/ -typedef struct GrabClone { +struct GrabClone { float startoffset[2]; int startx, starty; -} GrabClone; +}; static void grab_clone_apply(bContext *C, wmOperator *op) { @@ -834,7 +492,7 @@ static int grab_clone_invoke(bContext *C, wmOperator *op, const wmEvent *event) Brush *brush = image_paint_brush(C); GrabClone *cmv; - cmv = MEM_callocN(sizeof(GrabClone), "GrabClone"); + cmv = MEM_new<GrabClone>("GrabClone"); copy_v2_v2(cmv->startoffset, brush->clone.offset); cmv->startx = event->xy[0]; cmv->starty = event->xy[1]; @@ -849,7 +507,7 @@ static int grab_clone_modal(bContext *C, wmOperator *op, const wmEvent *event) { Brush *brush = image_paint_brush(C); ARegion *region = CTX_wm_region(C); - GrabClone *cmv = op->customdata; + GrabClone *cmv = static_cast<GrabClone *>(op->customdata); float startfx, startfy, fx, fy, delta[2]; int xmin = region->winrct.xmin, ymin = region->winrct.ymin; @@ -880,7 +538,8 @@ static int grab_clone_modal(bContext *C, wmOperator *op, const wmEvent *event) static void grab_clone_cancel(bContext *UNUSED(C), wmOperator *op) { - MEM_freeN(op->customdata); + GrabClone *cmv = static_cast<GrabClone *>(op->customdata); + MEM_delete(cmv); } void PAINT_OT_grab_clone(wmOperatorType *ot) @@ -904,7 +563,7 @@ void PAINT_OT_grab_clone(wmOperatorType *ot) RNA_def_float_vector(ot->srna, "delta", 2, - NULL, + nullptr, -FLT_MAX, FLT_MAX, "Delta", @@ -914,12 +573,12 @@ void PAINT_OT_grab_clone(wmOperatorType *ot) } /******************** sample color operator ********************/ -typedef struct { +struct SampleColorData { bool show_cursor; short launch_event; float initcolor[3]; bool sample_palette; -} SampleColorData; +}; static void sample_color_update_header(SampleColorData *data, bContext *C) { @@ -973,7 +632,7 @@ static int sample_color_invoke(bContext *C, wmOperator *op, const wmEvent *event Scene *scene = CTX_data_scene(C); Paint *paint = BKE_paint_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); - SampleColorData *data = MEM_mallocN(sizeof(SampleColorData), "sample color custom data"); + SampleColorData *data = MEM_new<SampleColorData>("sample color custom data"); ARegion *region = CTX_wm_region(C); wmWindow *win = CTX_wm_window(C); @@ -1009,7 +668,7 @@ static int sample_color_invoke(bContext *C, wmOperator *op, const wmEvent *event static int sample_color_modal(bContext *C, wmOperator *op, const wmEvent *event) { Scene *scene = CTX_data_scene(C); - SampleColorData *data = op->customdata; + SampleColorData *data = static_cast<SampleColorData *>(op->customdata); Paint *paint = BKE_paint_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); @@ -1023,8 +682,8 @@ static int sample_color_modal(bContext *C, wmOperator *op, const wmEvent *event) RNA_boolean_set(op->ptr, "palette", true); } WM_cursor_modal_restore(CTX_wm_window(C)); - MEM_freeN(data); - ED_workspace_status_text(C, NULL); + MEM_delete(data); + ED_workspace_status_text(C, nullptr); return OPERATOR_FINISHED; } @@ -1083,25 +742,26 @@ void PAINT_OT_sample_color(wmOperatorType *ot) /* properties */ PropertyRNA *prop; - prop = RNA_def_int_vector(ot->srna, "location", 2, NULL, 0, INT_MAX, "Location", "", 0, 16384); - RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); + prop = RNA_def_int_vector( + ot->srna, "location", 2, nullptr, 0, INT_MAX, "Location", "", 0, 16384); + RNA_def_property_flag(prop, static_cast<PropertyFlag>(PROP_SKIP_SAVE | PROP_HIDDEN)); - RNA_def_boolean(ot->srna, "merged", 0, "Sample Merged", "Sample the output display color"); - RNA_def_boolean(ot->srna, "palette", 0, "Add to Palette", ""); + RNA_def_boolean(ot->srna, "merged", false, "Sample Merged", "Sample the output display color"); + RNA_def_boolean(ot->srna, "palette", false, "Add to Palette", ""); } /******************** texture paint toggle operator ********************/ void ED_object_texture_paint_mode_enter_ex(Main *bmain, Scene *scene, Object *ob) { - Image *ima = NULL; + Image *ima = nullptr; ImagePaintSettings *imapaint = &scene->toolsettings->imapaint; /* This has to stay here to regenerate the texture paint * cache in case we are loading a file */ BKE_texpaint_slots_refresh_object(scene, ob); - ED_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL); + ED_paint_proj_mesh_data_check(scene, ob, nullptr, nullptr, nullptr, nullptr); /* entering paint mode also sets image to editors */ if (imapaint->mode == IMAGEPAINT_MODE_MATERIAL) { @@ -1117,11 +777,11 @@ void ED_object_texture_paint_mode_enter_ex(Main *bmain, Scene *scene, Object *ob } if (ima) { - wmWindowManager *wm = bmain->wm.first; - for (wmWindow *win = wm->windows.first; win; win = win->next) { + wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first); + LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { const bScreen *screen = WM_window_get_active_screen(win); - for (ScrArea *area = screen->areabase.first; area; area = area->next) { - SpaceLink *sl = area->spacedata.first; + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + SpaceLink *sl = static_cast<SpaceLink *>(area->spacedata.first); if (sl->spacetype == SPACE_IMAGE) { SpaceImage *sima = (SpaceImage *)sl; @@ -1142,12 +802,12 @@ void ED_object_texture_paint_mode_enter_ex(Main *bmain, Scene *scene, Object *ob if (U.glreslimit != 0) { BKE_image_free_all_gputextures(bmain); } - BKE_image_paint_set_mipmap(bmain, 0); + BKE_image_paint_set_mipmap(bmain, false); toggle_paint_cursor(scene, true); Mesh *me = BKE_mesh_from_object(ob); - BLI_assert(me != NULL); + BLI_assert(me != nullptr); DEG_id_tag_update(&me->id, ID_RECALC_COPY_ON_WRITE); WM_main_add_notifier(NC_SCENE | ND_MODE, scene); } @@ -1167,11 +827,11 @@ void ED_object_texture_paint_mode_exit_ex(Main *bmain, Scene *scene, Object *ob) if (U.glreslimit != 0) { BKE_image_free_all_gputextures(bmain); } - BKE_image_paint_set_mipmap(bmain, 1); + BKE_image_paint_set_mipmap(bmain, true); toggle_paint_cursor(scene, false); Mesh *me = BKE_mesh_from_object(ob); - BLI_assert(me != NULL); + BLI_assert(me != nullptr); DEG_id_tag_update(&me->id, ID_RECALC_COPY_ON_WRITE); WM_main_add_notifier(NC_SCENE | ND_MODE, scene); } @@ -1187,7 +847,7 @@ void ED_object_texture_paint_mode_exit(bContext *C) static bool texture_paint_toggle_poll(bContext *C) { Object *ob = CTX_data_active_object(C); - if (ob == NULL || ob->type != OB_MESH) { + if (ob == nullptr || ob->type != OB_MESH) { return false; } if (!ob->data || ID_IS_LINKED(ob->data)) { @@ -1207,7 +867,7 @@ static int texture_paint_toggle_exec(bContext *C, wmOperator *op) const bool is_mode_set = (ob->mode & mode_flag) != 0; if (!is_mode_set) { - if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) { + if (!ED_object_mode_compat_set(C, ob, static_cast<eObjectMode>(mode_flag), op->reports)) { return OPERATOR_CANCELLED; } } @@ -1274,7 +934,7 @@ static bool brush_colors_flip_poll(bContext *C) } else { Object *ob = CTX_data_active_object(C); - if (ob != NULL) { + if (ob != nullptr) { if (ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_TEXTURE_PAINT | OB_MODE_SCULPT)) { return true; } @@ -1310,8 +970,8 @@ void ED_imapaint_bucket_fill(struct bContext *C, ED_image_undo_push_begin(op->type->name, PAINT_MODE_TEXTURE_2D); - const float mouse_init[2] = {mouse[0], mouse[1]}; - paint_2d_bucket_fill(C, color, NULL, mouse_init, NULL, NULL); + const float mouse_init[2] = {static_cast<float>(mouse[0]), static_cast<float>(mouse[1])}; + paint_2d_bucket_fill(C, color, nullptr, mouse_init, nullptr, nullptr); ED_image_undo_push_end(); @@ -1349,3 +1009,4 @@ bool mask_paint_poll(bContext *C) { return BKE_paint_select_elem_test(CTX_data_active_object(C)); } +} diff --git a/source/blender/editors/sculpt_paint/paint_image_2d_curve_mask.cc b/source/blender/editors/sculpt_paint/paint_image_2d_curve_mask.cc index 64115f7c6eb..f5657b004e2 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d_curve_mask.cc +++ b/source/blender/editors/sculpt_paint/paint_image_2d_curve_mask.cc @@ -53,6 +53,7 @@ static void update_curve_mask(CurveMaskCache *curve_mask_cache, { BLI_assert(curve_mask_cache->curve_mask != nullptr); int offset = (int)floorf(diameter / 2.0f); + int clamped_radius = max_ff(radius, 1.0); unsigned short *m = curve_mask_cache->curve_mask; @@ -76,7 +77,7 @@ static void update_curve_mask(CurveMaskCache *curve_mask_cache, pixel_xy[1] = static_cast<float>(y) + aa_offset; for (int j = 0; j < aa_samples; j++) { const float len = len_v2v2(pixel_xy, bpos); - const int sample_index = min_ii((len / radius) * CurveSamplesBaseLen, + const int sample_index = min_ii((len / clamped_radius) * CurveSamplesBaseLen, CurveSamplesLen - 1); const float sample_weight = curve_mask_cache->sampled_curve[sample_index]; diff --git a/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc b/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc new file mode 100644 index 00000000000..786fcc47526 --- /dev/null +++ b/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc @@ -0,0 +1,531 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2001-2002 NaN Holding BV. All rights reserved. */ + +/** \file + * \ingroup edsculpt + * \brief Painting operator to paint in 2D and 3D. + */ + +#include "DNA_brush_types.h" +#include "DNA_color_types.h" +#include "DNA_scene_types.h" +#include "DNA_space_types.h" + +#include "BKE_brush.h" +#include "BKE_context.h" +#include "BKE_paint.h" +#include "BKE_undo_system.h" + +#include "DEG_depsgraph.h" + +#include "ED_paint.h" +#include "ED_view3d.h" + +#include "GPU_immediate.h" +#include "GPU_state.h" + +#include "MEM_guardedalloc.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_message.h" +#include "WM_toolsystem.h" +#include "WM_types.h" + +#include "paint_intern.h" + +namespace blender::ed::sculpt_paint::image::ops::paint { + +/** + * Interface to use the same painting operator for 3D and 2D painting. Interface removes the + * differences between the actual calls that are being performed. + */ +class AbstractPaintMode { + public: + virtual ~AbstractPaintMode() = default; + virtual void *paint_new_stroke( + bContext *C, wmOperator *op, Object *ob, const float mouse[2], int mode) = 0; + virtual void paint_stroke(bContext *C, + void *stroke_handle, + float prev_mouse[2], + float mouse[2], + int eraser, + float pressure, + float distance, + float size) = 0; + + virtual void paint_stroke_redraw(const bContext *C, void *stroke_handle, bool final) = 0; + virtual void paint_stroke_done(void *stroke_handle) = 0; + virtual void paint_gradient_fill(const bContext *C, + const Scene *scene, + Brush *brush, + struct PaintStroke *stroke, + void *stroke_handle, + float mouse_start[2], + float mouse_end[2]) = 0; + virtual void paint_bucket_fill(const bContext *C, + const Scene *scene, + Brush *brush, + struct PaintStroke *stroke, + void *stroke_handle, + float mouse_start[2], + float mouse_end[2]) = 0; +}; + +class ImagePaintMode : public AbstractPaintMode { + public: + void *paint_new_stroke(bContext *C, + wmOperator *op, + Object *UNUSED(ob), + const float UNUSED(mouse[2]), + int mode) override + { + return paint_2d_new_stroke(C, op, mode); + } + + void paint_stroke(bContext *UNUSED(C), + void *stroke_handle, + float prev_mouse[2], + float mouse[2], + int eraser, + float pressure, + float distance, + float size) override + { + paint_2d_stroke(stroke_handle, prev_mouse, mouse, eraser, pressure, distance, size); + } + + void paint_stroke_redraw(const bContext *C, void *stroke_handle, bool final) override + { + paint_2d_redraw(C, stroke_handle, final); + } + + void paint_stroke_done(void *stroke_handle) override + { + paint_2d_stroke_done(stroke_handle); + } + + void paint_gradient_fill(const bContext *C, + const Scene *UNUSED(scene), + Brush *brush, + struct PaintStroke *UNUSED(stroke), + void *stroke_handle, + float mouse_start[2], + float mouse_end[2]) override + { + paint_2d_gradient_fill(C, brush, mouse_start, mouse_end, stroke_handle); + } + + void paint_bucket_fill(const bContext *C, + const Scene *scene, + Brush *brush, + struct PaintStroke *stroke, + void *stroke_handle, + float mouse_start[2], + float mouse_end[2]) override + { + float color[3]; + if (paint_stroke_inverted(stroke)) { + srgb_to_linearrgb_v3_v3(color, BKE_brush_secondary_color_get(scene, brush)); + } + else { + srgb_to_linearrgb_v3_v3(color, BKE_brush_color_get(scene, brush)); + } + paint_2d_bucket_fill(C, color, brush, mouse_start, mouse_end, stroke_handle); + } +}; + +class ProjectionPaintMode : public AbstractPaintMode { + public: + void *paint_new_stroke( + bContext *C, wmOperator *UNUSED(op), Object *ob, const float mouse[2], int mode) override + { + return paint_proj_new_stroke(C, ob, mouse, mode); + } + + void paint_stroke(bContext *C, + void *stroke_handle, + float prev_mouse[2], + float mouse[2], + int eraser, + float pressure, + float distance, + float size) override + { + paint_proj_stroke(C, stroke_handle, prev_mouse, mouse, eraser, pressure, distance, size); + }; + + void paint_stroke_redraw(const bContext *C, void *stroke_handle, bool final) override + { + paint_proj_redraw(C, stroke_handle, final); + } + + void paint_stroke_done(void *stroke_handle) override + { + paint_proj_stroke_done(stroke_handle); + } + + void paint_gradient_fill(const bContext *C, + const Scene *scene, + Brush *brush, + struct PaintStroke *stroke, + void *stroke_handle, + float mouse_start[2], + float mouse_end[2]) override + { + paint_fill(C, scene, brush, stroke, stroke_handle, mouse_start, mouse_end); + } + + void paint_bucket_fill(const bContext *C, + const Scene *scene, + Brush *brush, + struct PaintStroke *stroke, + void *stroke_handle, + float mouse_start[2], + float mouse_end[2]) override + { + paint_fill(C, scene, brush, stroke, stroke_handle, mouse_start, mouse_end); + } + + private: + void paint_fill(const bContext *C, + const Scene *scene, + Brush *brush, + struct PaintStroke *stroke, + void *stroke_handle, + float mouse_start[2], + float mouse_end[2]) + { + paint_proj_stroke(C, + stroke_handle, + mouse_start, + mouse_end, + paint_stroke_flipped(stroke), + 1.0, + 0.0, + BKE_brush_size_get(scene, brush)); + /* two redraws, one for GPU update, one for notification */ + paint_proj_redraw(C, stroke_handle, false); + paint_proj_redraw(C, stroke_handle, true); + } +}; + +struct PaintOperation { + AbstractPaintMode *mode = nullptr; + + void *stroke_handle = nullptr; + + float prevmouse[2] = {0.0f, 0.0f}; + float startmouse[2] = {0.0f, 0.0f}; + double starttime = 0.0; + + wmPaintCursor *cursor = nullptr; + ViewContext vc = {nullptr}; + + PaintOperation() = default; + ~PaintOperation() + { + MEM_delete(mode); + mode = nullptr; + + if (cursor) { + WM_paint_cursor_end(cursor); + cursor = nullptr; + } + } +}; + +static void gradient_draw_line(bContext *UNUSED(C), int x, int y, void *customdata) +{ + PaintOperation *pop = (PaintOperation *)customdata; + + if (pop) { + GPU_line_smooth(true); + GPU_blend(GPU_BLEND_ALPHA); + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); + + ARegion *region = pop->vc.region; + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + GPU_line_width(4.0); + immUniformColor4ub(0, 0, 0, 255); + + immBegin(GPU_PRIM_LINES, 2); + immVertex2i(pos, x, y); + immVertex2i( + pos, pop->startmouse[0] + region->winrct.xmin, pop->startmouse[1] + region->winrct.ymin); + immEnd(); + + GPU_line_width(2.0); + immUniformColor4ub(255, 255, 255, 255); + + immBegin(GPU_PRIM_LINES, 2); + immVertex2i(pos, x, y); + immVertex2i( + pos, pop->startmouse[0] + region->winrct.xmin, pop->startmouse[1] + region->winrct.ymin); + immEnd(); + + immUnbindProgram(); + + GPU_blend(GPU_BLEND_NONE); + GPU_line_smooth(false); + } +} + +static PaintOperation *texture_paint_init(bContext *C, wmOperator *op, const float mouse[2]) +{ + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Scene *scene = CTX_data_scene(C); + ToolSettings *settings = scene->toolsettings; + PaintOperation *pop = MEM_new<PaintOperation>("PaintOperation"); /* caller frees */ + Brush *brush = BKE_paint_brush(&settings->imapaint.paint); + int mode = RNA_enum_get(op->ptr, "mode"); + ED_view3d_viewcontext_init(C, &pop->vc, depsgraph); + + copy_v2_v2(pop->prevmouse, mouse); + copy_v2_v2(pop->startmouse, mouse); + + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob = OBACT(view_layer); + + /* initialize from context */ + if (CTX_wm_region_view3d(C)) { + bool uvs, mat, tex, stencil; + if (!ED_paint_proj_mesh_data_check(scene, ob, &uvs, &mat, &tex, &stencil)) { + ED_paint_data_warning(op->reports, uvs, mat, tex, stencil); + MEM_delete(pop); + WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, nullptr); + return nullptr; + } + pop->mode = MEM_new<ProjectionPaintMode>("ProjectionPaintMode"); + } + else { + pop->mode = MEM_new<ImagePaintMode>("ImagePaintMode"); + } + + pop->stroke_handle = pop->mode->paint_new_stroke(C, op, ob, mouse, mode); + if (!pop->stroke_handle) { + MEM_delete(pop); + return nullptr; + } + + if ((brush->imagepaint_tool == PAINT_TOOL_FILL) && (brush->flag & BRUSH_USE_GRADIENT)) { + pop->cursor = WM_paint_cursor_activate( + SPACE_TYPE_ANY, RGN_TYPE_ANY, image_paint_poll, gradient_draw_line, pop); + } + + settings->imapaint.flag |= IMAGEPAINT_DRAWING; + ED_image_undo_push_begin(op->type->name, PAINT_MODE_TEXTURE_2D); + + return pop; +} + +static void paint_stroke_update_step(bContext *C, + wmOperator *UNUSED(op), + struct PaintStroke *stroke, + PointerRNA *itemptr) +{ + PaintOperation *pop = static_cast<PaintOperation *>(paint_stroke_mode_data(stroke)); + Scene *scene = CTX_data_scene(C); + ToolSettings *toolsettings = CTX_data_tool_settings(C); + UnifiedPaintSettings *ups = &toolsettings->unified_paint_settings; + Brush *brush = BKE_paint_brush(&toolsettings->imapaint.paint); + + float alphafac = (brush->flag & BRUSH_ACCUMULATE) ? ups->overlap_factor : 1.0f; + + /* initial brush values. Maybe it should be considered moving these to stroke system */ + float startalpha = BKE_brush_alpha_get(scene, brush); + + float mouse[2]; + float pressure; + float size; + float distance = paint_stroke_distance_get(stroke); + int eraser; + + RNA_float_get_array(itemptr, "mouse", mouse); + pressure = RNA_float_get(itemptr, "pressure"); + eraser = RNA_boolean_get(itemptr, "pen_flip"); + size = RNA_float_get(itemptr, "size"); + + /* stroking with fill tool only acts on stroke end */ + if (brush->imagepaint_tool == PAINT_TOOL_FILL) { + copy_v2_v2(pop->prevmouse, mouse); + return; + } + + if (BKE_brush_use_alpha_pressure(brush)) { + BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * pressure * alphafac)); + } + else { + BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * alphafac)); + } + + if ((brush->flag & BRUSH_DRAG_DOT) || (brush->flag & BRUSH_ANCHORED)) { + UndoStack *ustack = CTX_wm_manager(C)->undo_stack; + ED_image_undo_restore(ustack->step_init); + } + + pop->mode->paint_stroke( + C, pop->stroke_handle, pop->prevmouse, mouse, eraser, pressure, distance, size); + + copy_v2_v2(pop->prevmouse, mouse); + + /* restore brush values */ + BKE_brush_alpha_set(scene, brush, startalpha); +} + +static void paint_stroke_redraw(const bContext *C, struct PaintStroke *stroke, bool final) +{ + PaintOperation *pop = static_cast<PaintOperation *>(paint_stroke_mode_data(stroke)); + pop->mode->paint_stroke_redraw(C, pop->stroke_handle, final); +} + +static void paint_stroke_done(const bContext *C, struct PaintStroke *stroke) +{ + Scene *scene = CTX_data_scene(C); + ToolSettings *toolsettings = scene->toolsettings; + PaintOperation *pop = static_cast<PaintOperation *>(paint_stroke_mode_data(stroke)); + Brush *brush = BKE_paint_brush(&toolsettings->imapaint.paint); + + toolsettings->imapaint.flag &= ~IMAGEPAINT_DRAWING; + + if (brush->imagepaint_tool == PAINT_TOOL_FILL) { + if (brush->flag & BRUSH_USE_GRADIENT) { + pop->mode->paint_gradient_fill( + C, scene, brush, stroke, pop->stroke_handle, pop->startmouse, pop->prevmouse); + } + else { + pop->mode->paint_bucket_fill( + C, scene, brush, stroke, pop->stroke_handle, pop->startmouse, pop->prevmouse); + } + } + pop->mode->paint_stroke_done(pop->stroke_handle); + pop->stroke_handle = nullptr; + + ED_image_undo_push_end(); + +/* duplicate warning, see texpaint_init */ +#if 0 + if (pop->s.warnmultifile) { + BKE_reportf(op->reports, + RPT_WARNING, + "Image requires 4 color channels to paint: %s", + pop->s.warnmultifile); + } + if (pop->s.warnpackedfile) { + BKE_reportf(op->reports, + RPT_WARNING, + "Packed MultiLayer files cannot be painted: %s", + pop->s.warnpackedfile); + } +#endif + MEM_delete(pop); +} + +static bool paint_stroke_test_start(bContext *C, wmOperator *op, const float mouse[2]) +{ + PaintOperation *pop; + + /* TODO: Should avoid putting this here. Instead, last position should be requested + * from stroke system. */ + + if (!(pop = texture_paint_init(C, op, mouse))) { + return false; + } + + paint_stroke_set_mode_data(static_cast<PaintStroke *>(op->customdata), pop); + + return true; +} + +static int paint_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + int retval; + + op->customdata = paint_stroke_new(C, + op, + nullptr, + paint_stroke_test_start, + paint_stroke_update_step, + paint_stroke_redraw, + paint_stroke_done, + event->type); + + if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { + paint_stroke_free(C, op, static_cast<PaintStroke *>(op->customdata)); + return OPERATOR_FINISHED; + } + /* add modal handler */ + WM_event_add_modal_handler(C, op); + + OPERATOR_RETVAL_CHECK(retval); + BLI_assert(retval == OPERATOR_RUNNING_MODAL); + + return OPERATOR_RUNNING_MODAL; +} + +static int paint_exec(bContext *C, wmOperator *op) +{ + PropertyRNA *strokeprop; + PointerRNA firstpoint; + float mouse[2]; + + strokeprop = RNA_struct_find_property(op->ptr, "stroke"); + + if (!RNA_property_collection_lookup_int(op->ptr, strokeprop, 0, &firstpoint)) { + return OPERATOR_CANCELLED; + } + + RNA_float_get_array(&firstpoint, "mouse", mouse); + + op->customdata = paint_stroke_new(C, + op, + nullptr, + paint_stroke_test_start, + paint_stroke_update_step, + paint_stroke_redraw, + paint_stroke_done, + 0); + /* frees op->customdata */ + return paint_stroke_exec(C, op, static_cast<PaintStroke *>(op->customdata)); +} + +static int paint_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + return paint_stroke_modal(C, op, event, static_cast<PaintStroke *>(op->customdata)); +} + +static void paint_cancel(bContext *C, wmOperator *op) +{ + paint_stroke_cancel(C, op, static_cast<PaintStroke *>(op->customdata)); +} +} // namespace blender::ed::sculpt_paint::image::ops::paint + +extern "C" { +void PAINT_OT_image_paint(wmOperatorType *ot) +{ + using namespace blender::ed::sculpt_paint::image::ops::paint; + + /* identifiers */ + ot->name = "Image Paint"; + ot->idname = "PAINT_OT_image_paint"; + ot->description = "Paint a stroke into the image"; + + /* api callbacks */ + ot->invoke = paint_invoke; + ot->modal = paint_modal; + ot->exec = paint_exec; + ot->poll = image_paint_poll; + ot->cancel = paint_cancel; + + /* flags */ + ot->flag = OPTYPE_BLOCKING; + + paint_stroke_operator_properties(ot); +} +} diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index f1e79b98b83..82fdf49c28e 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -49,6 +49,7 @@ typedef struct CoNo { typedef bool (*StrokeGetLocation)(struct bContext *C, float location[3], const float mouse[2]); typedef bool (*StrokeTestStart)(struct bContext *C, struct wmOperator *op, const float mouse[2]); typedef void (*StrokeUpdateStep)(struct bContext *C, + struct wmOperator *op, struct PaintStroke *stroke, struct PointerRNA *itemptr); typedef void (*StrokeRedraw)(const struct bContext *C, struct PaintStroke *stroke, bool final); @@ -62,7 +63,7 @@ struct PaintStroke *paint_stroke_new(struct bContext *C, StrokeRedraw redraw, StrokeDone done, int event_type); -void paint_stroke_free(struct bContext *C, struct wmOperator *op); +void paint_stroke_free(struct bContext *C, struct wmOperator *op, struct PaintStroke *stroke); /** * Returns zero if the stroke dots should not be spaced, non-zero otherwise. @@ -84,9 +85,12 @@ bool paint_supports_jitter(enum ePaintMode mode); * Called in paint_ops.c, on each regeneration of key-maps. */ struct wmKeyMap *paint_stroke_modal_keymap(struct wmKeyConfig *keyconf); -int paint_stroke_modal(struct bContext *C, struct wmOperator *op, const struct wmEvent *event); -int paint_stroke_exec(struct bContext *C, struct wmOperator *op); -void paint_stroke_cancel(struct bContext *C, struct wmOperator *op); +int paint_stroke_modal(struct bContext *C, + struct wmOperator *op, + const struct wmEvent *event, + struct PaintStroke *stroke); +int paint_stroke_exec(struct bContext *C, struct wmOperator *op, struct PaintStroke *stroke); +void paint_stroke_cancel(struct bContext *C, struct wmOperator *op, struct PaintStroke *stroke); bool paint_stroke_flipped(struct PaintStroke *stroke); bool paint_stroke_inverted(struct PaintStroke *stroke); struct ViewContext *paint_stroke_view_context(struct PaintStroke *stroke); @@ -265,6 +269,7 @@ void paint_brush_color_get(struct Scene *scene, bool paint_use_opacity_masking(struct Brush *brush); void paint_brush_init_tex(struct Brush *brush); void paint_brush_exit_tex(struct Brush *brush); +bool image_paint_poll(struct bContext *C); void PAINT_OT_grab_clone(struct wmOperatorType *ot); void PAINT_OT_sample_color(struct wmOperatorType *ot); diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index 1b876235ad0..926a564184a 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -39,6 +39,7 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "curves_sculpt_intern.h" #include "paint_intern.h" #include "sculpt_intern.h" @@ -758,6 +759,7 @@ static const ePaintMode brush_select_paint_modes[] = { PAINT_MODE_VERTEX_GPENCIL, PAINT_MODE_SCULPT_GPENCIL, PAINT_MODE_WEIGHT_GPENCIL, + PAINT_MODE_SCULPT_CURVES, }; static int brush_select_exec(bContext *C, wmOperator *op) @@ -1388,6 +1390,10 @@ void ED_keymap_paint(wmKeyConfig *keyconf) keymap = paint_stroke_modal_keymap(keyconf); WM_modalkeymap_assign(keymap, "SCULPT_OT_brush_stroke"); + /* Curves Sculpt mode. */ + keymap = WM_keymap_ensure(keyconf, "Sculpt Curves", 0, 0); + keymap->poll = CURVES_SCULPT_mode_poll; + /* sculpt expand. */ sculpt_expand_modal_keymap(keyconf); } diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index 9bbfb81e08e..ae7570d21a1 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -505,16 +505,13 @@ static bool paint_stroke_use_jitter(ePaintMode mode, Brush *brush, bool invert) } /* Put the location of the next stroke dot into the stroke RNA and apply it to the mesh */ -static void paint_brush_stroke_add_step(bContext *C, - wmOperator *op, - const float mouse_in[2], - float pressure) +static void paint_brush_stroke_add_step( + bContext *C, wmOperator *op, PaintStroke *stroke, const float mouse_in[2], float pressure) { Scene *scene = CTX_data_scene(C); Paint *paint = BKE_paint_get_active_from_context(C); ePaintMode mode = BKE_paintmode_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); - PaintStroke *stroke = op->customdata; UnifiedPaintSettings *ups = stroke->ups; float mouse_out[2]; PointerRNA itemptr; @@ -614,7 +611,7 @@ static void paint_brush_stroke_add_step(bContext *C, RNA_float_set(&itemptr, "x_tilt", stroke->x_tilt); RNA_float_set(&itemptr, "y_tilt", stroke->y_tilt); - stroke->update_step(C, stroke, &itemptr); + stroke->update_step(C, op, stroke, &itemptr); /* don't record this for now, it takes up a lot of memory when doing long * strokes with small brush size, and operators have register disabled */ @@ -785,12 +782,12 @@ static float paint_space_stroke_spacing_variable(bContext *C, * towards the final mouse location. */ static int paint_space_stroke(bContext *C, wmOperator *op, + PaintStroke *stroke, const float final_mouse[2], float final_pressure) { const Scene *scene = CTX_data_scene(C); ARegion *region = CTX_wm_region(C); - PaintStroke *stroke = op->customdata; UnifiedPaintSettings *ups = stroke->ups; Paint *paint = BKE_paint_get_active_from_context(C); ePaintMode mode = BKE_paintmode_get_active_from_context(C); @@ -852,7 +849,7 @@ static int paint_space_stroke(bContext *C, spacing / no_pressure_spacing); stroke->stroke_distance += spacing / stroke->zoom_2d; - paint_brush_stroke_add_step(C, op, mouse, pressure); + paint_brush_stroke_add_step(C, op, stroke, mouse, pressure); length -= spacing; pressure = stroke->last_pressure; @@ -929,7 +926,7 @@ PaintStroke *paint_stroke_new(bContext *C, return stroke; } -void paint_stroke_free(bContext *C, wmOperator *op) +void paint_stroke_free(bContext *C, wmOperator *UNUSED(op), PaintStroke *stroke) { RegionView3D *rv3d = CTX_wm_region_view3d(C); if (rv3d) { @@ -938,7 +935,6 @@ void paint_stroke_free(bContext *C, wmOperator *op) BKE_paint_set_overlay_override(0); - PaintStroke *stroke = op->customdata; if (stroke == NULL) { return; } @@ -961,12 +957,11 @@ void paint_stroke_free(bContext *C, wmOperator *op) BLI_freelistN(&stroke->line); - MEM_SAFE_FREE(op->customdata); + MEM_SAFE_FREE(stroke); } -static void stroke_done(bContext *C, wmOperator *op) +static void stroke_done(bContext *C, wmOperator *op, PaintStroke *stroke) { - PaintStroke *stroke = op->customdata; UnifiedPaintSettings *ups = stroke->ups; /* reset rotation here to avoid doing so in cursor display */ @@ -988,7 +983,7 @@ static void stroke_done(bContext *C, wmOperator *op) } } - paint_stroke_free(C, op); + paint_stroke_free(C, op, stroke); } bool paint_space_stroke_enabled(Brush *br, ePaintMode mode) @@ -1230,7 +1225,7 @@ static void paint_line_strokes_spacing(bContext *C, ups->overlap_factor = paint_stroke_integrate_overlap(stroke->brush, 1.0); stroke->stroke_distance += spacing / stroke->zoom_2d; - paint_brush_stroke_add_step(C, op, mouse, 1.0); + paint_brush_stroke_add_step(C, op, stroke, mouse, 1.0); length -= spacing; spacing_final = spacing; @@ -1252,8 +1247,8 @@ static void paint_stroke_line_end(bContext *C, if (stroke->stroke_started && (br->flag & BRUSH_LINE)) { stroke->ups->overlap_factor = paint_stroke_integrate_overlap(br, 1.0); - paint_brush_stroke_add_step(C, op, stroke->last_mouse_position, 1.0); - paint_space_stroke(C, op, mouse, 1.0); + paint_brush_stroke_add_step(C, op, stroke, stroke->last_mouse_position, 1.0); + paint_space_stroke(C, op, stroke, mouse, 1.0); } } @@ -1331,7 +1326,7 @@ static bool paint_stroke_curve_end(bContext *C, wmOperator *op, PaintStroke *str stroke->stroke_started = stroke->test_start(C, op, stroke->last_mouse_position); if (stroke->stroke_started) { - paint_brush_stroke_add_step(C, op, data + 2 * j, 1.0); + paint_brush_stroke_add_step(C, op, stroke, data + 2 * j, 1.0); paint_line_strokes_spacing( C, op, stroke, spacing, &length_residue, data + 2 * j, data + 2 * (j + 1)); } @@ -1343,7 +1338,7 @@ static bool paint_stroke_curve_end(bContext *C, wmOperator *op, PaintStroke *str } } - stroke_done(C, op); + stroke_done(C, op, stroke); #ifdef DEBUG_TIME TIMEIT_END_AVERAGED(whole_stroke); @@ -1384,11 +1379,10 @@ static void paint_stroke_line_constrain(PaintStroke *stroke, float mouse[2]) } } -int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) +int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintStroke *stroke) { Paint *p = BKE_paint_get_active_from_context(C); ePaintMode mode = BKE_paintmode_get_active_from_context(C); - PaintStroke *stroke = op->customdata; Brush *br = stroke->brush = BKE_paint_brush(p); PaintSample sample_average; float mouse[2]; @@ -1482,7 +1476,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) op->type->cancel(C, op); } else { - paint_stroke_cancel(C, op); + paint_stroke_cancel(C, op, stroke); } return OPERATOR_CANCELLED; } @@ -1492,17 +1486,17 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) copy_v2_fl2(mouse, event->mval[0], event->mval[1]); paint_stroke_line_constrain(stroke, mouse); paint_stroke_line_end(C, op, stroke, mouse); - stroke_done(C, op); + stroke_done(C, op, stroke); return OPERATOR_FINISHED; } } else if (ELEM(event->type, EVT_RETKEY, EVT_SPACEKEY)) { paint_stroke_line_end(C, op, stroke, sample_average.mouse); - stroke_done(C, op); + stroke_done(C, op, stroke); return OPERATOR_FINISHED; } else if (br->flag & BRUSH_LINE) { - if (event->alt) { + if (event->modifier & KM_ALT) { stroke->constrain_line = true; } else { @@ -1530,7 +1524,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) if (paint_smooth_stroke(stroke, &sample_average, mode, mouse, &pressure)) { if (stroke->stroke_started) { if (paint_space_stroke_enabled(br, mode)) { - if (paint_space_stroke(C, op, mouse, pressure)) { + if (paint_space_stroke(C, op, stroke, mouse, pressure)) { redraw = true; } } @@ -1538,7 +1532,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) float dmouse[2]; sub_v2_v2v2(dmouse, mouse, stroke->last_mouse_position); stroke->stroke_distance += len_v2(dmouse); - paint_brush_stroke_add_step(C, op, mouse, pressure); + paint_brush_stroke_add_step(C, op, stroke, mouse, pressure); redraw = true; } } @@ -1549,7 +1543,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) * instead of waiting till we have moved the space distance */ if (first_dab && paint_space_stroke_enabled(br, mode) && !(br->flag & BRUSH_SMOOTH_STROKE)) { stroke->ups->overlap_factor = paint_stroke_integrate_overlap(br, 1.0); - paint_brush_stroke_add_step(C, op, sample_average.mouse, sample_average.pressure); + paint_brush_stroke_add_step(C, op, stroke, sample_average.mouse, sample_average.pressure); redraw = true; } @@ -1572,10 +1566,8 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_RUNNING_MODAL; } -int paint_stroke_exec(bContext *C, wmOperator *op) +int paint_stroke_exec(bContext *C, wmOperator *op, PaintStroke *stroke) { - PaintStroke *stroke = op->customdata; - /* only when executed for the first time */ if (stroke->stroke_started == 0) { PropertyRNA *strokeprop; @@ -1592,21 +1584,21 @@ int paint_stroke_exec(bContext *C, wmOperator *op) if (stroke->stroke_started) { RNA_BEGIN (op->ptr, itemptr, "stroke") { - stroke->update_step(C, stroke, &itemptr); + stroke->update_step(C, op, stroke, &itemptr); } RNA_END; } bool ok = (stroke->stroke_started != 0); - stroke_done(C, op); + stroke_done(C, op, stroke); return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } -void paint_stroke_cancel(bContext *C, wmOperator *op) +void paint_stroke_cancel(bContext *C, wmOperator *op, PaintStroke *stroke) { - stroke_done(C, op); + stroke_done(C, op, stroke); } ViewContext *paint_stroke_view_context(PaintStroke *stroke) diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c index f54b012b8dd..31b965c6a92 100644 --- a/source/blender/editors/sculpt_paint/paint_utils.c +++ b/source/blender/editors/sculpt_paint/paint_utils.c @@ -130,13 +130,12 @@ float paint_calc_object_space_radius(ViewContext *vc, const float center[3], flo { Object *ob = vc->obact; float delta[3], scale, loc[3]; - const float mval_f[2] = {pixel_radius, 0.0f}; - float zfac; + const float xy_delta[2] = {pixel_radius, 0.0f}; mul_v3_m4v3(loc, ob->obmat, center); - zfac = ED_view3d_calc_zfac(vc->rv3d, loc, NULL); - ED_view3d_win_to_delta(vc->region, mval_f, delta, zfac); + const float zfac = ED_view3d_calc_zfac(vc->rv3d, loc); + ED_view3d_win_to_delta(vc->region, xy_delta, zfac, delta); scale = fabsf(mat4_to_scale(ob->obmat)); scale = (scale == 0.0f) ? 1.0f : scale; @@ -585,7 +584,8 @@ void BRUSH_OT_curve_preset(wmOperatorType *ot) ot->poll = brush_curve_preset_poll; prop = RNA_def_enum(ot->srna, "shape", prop_shape_items, CURVE_PRESET_SMOOTH, "Mode", ""); - RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */ + RNA_def_property_translation_context(prop, + BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */ } /* face-select ops */ diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index a82636023f8..e2f8d81fe13 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -2370,7 +2370,10 @@ static void wpaint_do_symmetrical_brush_actions( cache->is_last_valid = true; } -static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr) +static void wpaint_stroke_update_step(bContext *C, + wmOperator *UNUSED(op), + struct PaintStroke *stroke, + PointerRNA *itemptr) { Scene *scene = CTX_data_scene(C); ToolSettings *ts = CTX_data_tool_settings(C); @@ -2551,7 +2554,7 @@ static int wpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event) event->type); if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { - paint_stroke_free(C, op); + paint_stroke_free(C, op, op->customdata); return OPERATOR_FINISHED; } /* add modal handler */ @@ -2575,7 +2578,7 @@ static int wpaint_exec(bContext *C, wmOperator *op) 0); /* frees op->customdata */ - paint_stroke_exec(C, op); + paint_stroke_exec(C, op, op->customdata); return OPERATOR_FINISHED; } @@ -2588,7 +2591,12 @@ static void wpaint_cancel(bContext *C, wmOperator *op) ob->sculpt->cache = NULL; } - paint_stroke_cancel(C, op); + paint_stroke_cancel(C, op, op->customdata); +} + +static int wpaint_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + return paint_stroke_modal(C, op, event, op->customdata); } void PAINT_OT_weight_paint(wmOperatorType *ot) @@ -2600,7 +2608,7 @@ void PAINT_OT_weight_paint(wmOperatorType *ot) /* api callbacks */ ot->invoke = wpaint_invoke; - ot->modal = paint_stroke_modal; + ot->modal = wpaint_modal; ot->exec = wpaint_exec; ot->poll = weight_paint_poll; ot->cancel = wpaint_cancel; @@ -3395,7 +3403,10 @@ static void vpaint_do_symmetrical_brush_actions( cache->is_last_valid = true; } -static void vpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr) +static void vpaint_stroke_update_step(bContext *C, + wmOperator *UNUSED(op), + struct PaintStroke *stroke, + PointerRNA *itemptr) { Scene *scene = CTX_data_scene(C); ToolSettings *ts = CTX_data_tool_settings(C); @@ -3497,7 +3508,7 @@ static int vpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event) event->type); if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { - paint_stroke_free(C, op); + paint_stroke_free(C, op, op->customdata); return OPERATOR_FINISHED; } @@ -3522,7 +3533,7 @@ static int vpaint_exec(bContext *C, wmOperator *op) 0); /* frees op->customdata */ - paint_stroke_exec(C, op); + paint_stroke_exec(C, op, op->customdata); return OPERATOR_FINISHED; } @@ -3535,7 +3546,12 @@ static void vpaint_cancel(bContext *C, wmOperator *op) ob->sculpt->cache = NULL; } - paint_stroke_cancel(C, op); + paint_stroke_cancel(C, op, op->customdata); +} + +static int vpaint_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + return paint_stroke_modal(C, op, event, op->customdata); } void PAINT_OT_vertex_paint(wmOperatorType *ot) @@ -3547,7 +3563,7 @@ void PAINT_OT_vertex_paint(wmOperatorType *ot) /* api callbacks */ ot->invoke = vpaint_invoke; - ot->modal = paint_stroke_modal; + ot->modal = vpaint_modal; ot->exec = vpaint_exec; ot->poll = vertex_paint_poll; ot->cancel = vpaint_cancel; diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 68edf9cd54a..70ff7596d6d 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -2648,13 +2648,13 @@ static void update_sculpt_normal(Sculpt *sd, Object *ob, PBVHNode **nodes, int t static void calc_local_y(ViewContext *vc, const float center[3], float y[3]) { Object *ob = vc->obact; - float loc[3], mval_f[2] = {0.0f, 1.0f}; - float zfac; + float loc[3]; + const float xy_delta[2] = {0.0f, 1.0f}; mul_v3_m4v3(loc, ob->imat, center); - zfac = ED_view3d_calc_zfac(vc->rv3d, loc, NULL); + const float zfac = ED_view3d_calc_zfac(vc->rv3d, loc); - ED_view3d_win_to_delta(vc->region, mval_f, y, zfac); + ED_view3d_win_to_delta(vc->region, xy_delta, zfac, y); normalize_v3(y); add_v3_v3(y, ob->loc); @@ -5186,6 +5186,7 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f } static void sculpt_stroke_update_step(bContext *C, + wmOperator *UNUSED(op), struct PaintStroke *UNUSED(stroke), PointerRNA *itemptr) { @@ -5333,12 +5334,12 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent ignore_background_click = RNA_boolean_get(op->ptr, "ignore_background_click"); if (ignore_background_click && !over_mesh(C, op, event->xy[0], event->xy[1])) { - paint_stroke_free(C, op); + paint_stroke_free(C, op, op->customdata); return OPERATOR_PASS_THROUGH; } if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { - paint_stroke_free(C, op); + paint_stroke_free(C, op, op->customdata); return OPERATOR_FINISHED; } /* Add modal handler. */ @@ -5364,7 +5365,7 @@ static int sculpt_brush_stroke_exec(bContext *C, wmOperator *op) 0); /* Frees op->customdata. */ - paint_stroke_exec(C, op); + paint_stroke_exec(C, op, op->customdata); return OPERATOR_FINISHED; } @@ -5382,7 +5383,7 @@ static void sculpt_brush_stroke_cancel(bContext *C, wmOperator *op) paint_mesh_restore_co(sd, ob); } - paint_stroke_cancel(C, op); + paint_stroke_cancel(C, op, op->customdata); if (ss->cache) { SCULPT_cache_free(ss->cache); @@ -5392,6 +5393,11 @@ static void sculpt_brush_stroke_cancel(bContext *C, wmOperator *op) sculpt_brush_exit_tex(sd); } +static int sculpt_brush_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + return paint_stroke_modal(C, op, event, op->customdata); +} + void SCULPT_OT_brush_stroke(wmOperatorType *ot) { /* Identifiers. */ @@ -5401,7 +5407,7 @@ void SCULPT_OT_brush_stroke(wmOperatorType *ot) /* API callbacks. */ ot->invoke = sculpt_brush_stroke_invoke; - ot->modal = paint_stroke_modal; + ot->modal = sculpt_brush_stroke_modal; ot->exec = sculpt_brush_stroke_exec; ot->poll = SCULPT_poll; ot->cancel = sculpt_brush_stroke_cancel; diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index a6b412b2b7e..6f9df4d8252 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -1105,10 +1105,10 @@ bool SCULPT_search_sphere_cb(PBVHNode *node, void *data_v); bool SCULPT_search_circle_cb(PBVHNode *node, void *data_v); /** - * Initialize a point-in-brush test with a given falloff shape + * Initialize a point-in-brush test with a given falloff shape. * - * \param falloff_shape PAINT_FALLOFF_SHAPE_SPHERE or PAINT_FALLOFF_SHAPE_TUBE - * \return The brush falloff function + * \param falloff_shape: #PAINT_FALLOFF_SHAPE_SPHERE or #PAINT_FALLOFF_SHAPE_TUBE. + * \return The brush falloff function. */ SculptBrushTestFn SCULPT_brush_test_init_with_falloff_shape(SculptSession *ss, SculptBrushTest *test, diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c index 72640893dea..8fc10061f83 100644 --- a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c @@ -245,7 +245,7 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent * /* When pressing Ctrl, expand directly to the max number of iterations. This allows to flood fill * mask and face sets by connectivity directly. */ - if (event->ctrl) { + if (event->modifier & KM_CTRL) { mask_expand_update_it = ss->filter_cache->mask_update_last_it - 1; } diff --git a/source/blender/editors/space_action/action_data.c b/source/blender/editors/space_action/action_data.c index e383ada1331..8e8902c2ea7 100644 --- a/source/blender/editors/space_action/action_data.c +++ b/source/blender/editors/space_action/action_data.c @@ -659,7 +659,7 @@ static int action_unlink_invoke(bContext *C, wmOperator *op, const wmEvent *even { /* NOTE: this is hardcoded to match the behavior for the unlink button * (in interface_templates.c). */ - RNA_boolean_set(op->ptr, "force_delete", event->shift != 0); + RNA_boolean_set(op->ptr, "force_delete", event->modifier & KM_SHIFT); return action_unlink_exec(C, op); } diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c index 897091731a4..d53fe2efb03 100644 --- a/source/blender/editors/space_api/spacetypes.c +++ b/source/blender/editors/space_api/spacetypes.c @@ -29,6 +29,7 @@ #include "ED_clip.h" #include "ED_curve.h" #include "ED_curves.h" +#include "ED_curves_sculpt.h" #include "ED_fileselect.h" #include "ED_geometry.h" #include "ED_gizmo_library.h" @@ -96,6 +97,7 @@ void ED_spacetypes_init(void) ED_operatortypes_mesh(); ED_operatortypes_geometry(); ED_operatortypes_sculpt(); + ED_operatortypes_sculpt_curves(); ED_operatortypes_uvedit(); ED_operatortypes_paint(); ED_operatortypes_physics(); diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c index 287aef178ae..4e80e7ea5c2 100644 --- a/source/blender/editors/space_buttons/buttons_context.c +++ b/source/blender/editors/space_buttons/buttons_context.c @@ -230,7 +230,7 @@ static bool buttons_context_path_data(ButsContextPath *path, int type) return true; } if (RNA_struct_is_a(ptr->type, &RNA_Curve) && - (type == -1 || ELEM(type, OB_CURVE, OB_SURF, OB_FONT))) { + (type == -1 || ELEM(type, OB_CURVES_LEGACY, OB_SURF, OB_FONT))) { return true; } if (RNA_struct_is_a(ptr->type, &RNA_Armature) && (ELEM(type, -1, OB_ARMATURE))) { @@ -293,7 +293,7 @@ static bool buttons_context_path_modifier(ButsContextPath *path) if (ELEM(ob->type, OB_MESH, - OB_CURVE, + OB_CURVES_LEGACY, OB_FONT, OB_SURF, OB_LATTICE, diff --git a/source/blender/editors/space_buttons/buttons_ops.c b/source/blender/editors/space_buttons/buttons_ops.c index cad6d34af24..e215b7c7992 100644 --- a/source/blender/editors/space_buttons/buttons_ops.c +++ b/source/blender/editors/space_buttons/buttons_ops.c @@ -282,11 +282,11 @@ static int file_browse_invoke(bContext *C, wmOperator *op, const wmEvent *event) /* Useful yet irritating feature, Shift+Click to open the file * Alt+Click to browse a folder in the OS's browser. */ - if (event->shift || event->alt) { + if (event->modifier & (KM_SHIFT | KM_ALT)) { wmOperatorType *ot = WM_operatortype_find("WM_OT_path_open", true); PointerRNA props_ptr; - if (event->alt) { + if (event->modifier & KM_ALT) { char *lslash = (char *)BLI_path_slash_rfind(str); if (lslash) { *lslash = '\0'; diff --git a/source/blender/editors/space_console/console_ops.c b/source/blender/editors/space_console/console_ops.c index ba0b3a59e24..a24eae6a0ce 100644 --- a/source/blender/editors/space_console/console_ops.c +++ b/source/blender/editors/space_console/console_ops.c @@ -408,7 +408,7 @@ static int console_insert_invoke(bContext *C, wmOperator *op, const wmEvent *eve * (when input method are used for utf8 inputs, the user may assign key event * including alt/ctrl/super like ctrl+m to commit utf8 string. in such case, * the modifiers in the utf8 character event make no sense.) */ - if ((event->ctrl || event->oskey) && !event->utf8_buf[0]) { + if ((event->modifier & (KM_CTRL | KM_OSKEY)) && !event->utf8_buf[0]) { return OPERATOR_PASS_THROUGH; } @@ -456,7 +456,18 @@ void CONSOLE_OT_insert(wmOperatorType *ot) static int console_indent_or_autocomplete_exec(bContext *C, wmOperator *UNUSED(op)) { ConsoleLine *ci = console_history_verify(C); - bool text_before_cursor = ci->cursor != 0 && !ELEM(ci->line[ci->cursor - 1], ' ', '\t'); + bool text_before_cursor = false; + + /* Check any text before cursor (not just the previous character) as is done for + * #TEXT_OT_indent_or_autocomplete because Python auto-complete operates on import + * statements such as completing possible sub-modules: `from bpy import `. */ + for (int i = 0; i < ci->cursor; i += BLI_str_utf8_size_safe(&ci->line[i])) { + if (!ELEM(ci->line[i], ' ', '\t')) { + text_before_cursor = true; + break; + } + } + if (text_before_cursor) { WM_operator_name_call(C, "CONSOLE_OT_autocomplete", WM_OP_INVOKE_DEFAULT, NULL); } diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 3b3d968aed6..daa4b53803f 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -2245,7 +2245,7 @@ FileDirEntry *filelist_file_ex(struct FileList *filelist, const int index, const cache->misc_entries_indices[cache->misc_cursor] = index; cache->misc_cursor = (cache->misc_cursor + 1) % cache_size; -#if 0 /* Actually no, only block cached entries should have preview imho. */ +#if 0 /* Actually no, only block cached entries should have preview IMHO. */ if (cache->previews_pool) { filelist_cache_previews_push(filelist, ret, index); } diff --git a/source/blender/editors/space_graph/graph_slider_ops.c b/source/blender/editors/space_graph/graph_slider_ops.c index 1006ba4b3f4..1a3355b0139 100644 --- a/source/blender/editors/space_graph/graph_slider_ops.c +++ b/source/blender/editors/space_graph/graph_slider_ops.c @@ -163,6 +163,18 @@ static void reset_bezts(tGraphSliderOp *gso) ANIM_animdata_freelist(&anim_data); } +/** + * Get factor value and store it in RNA property. + * Custom data of #wmOperator needs to contain #tGraphSliderOp. + */ +static float slider_factor_get_and_remember(wmOperator *op) +{ + tGraphSliderOp *gso = op->customdata; + const float factor = ED_slider_factor_get(gso->slider); + RNA_property_float_set(op->ptr, gso->factor_prop, factor); + return factor; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -378,8 +390,7 @@ static void decimate_modal_update(bContext *C, wmOperator *op) reset_bezts(gso); /* Apply... */ - float factor = ED_slider_factor_get(gso->slider); - RNA_property_float_set(op->ptr, gso->factor_prop, factor); + const float factor = slider_factor_get_and_remember(op); /* We don't want to limit the decimation to a certain error margin. */ const float error_sq_max = FLT_MAX; decimate_graph_keys(&gso->ac, factor, error_sq_max); @@ -598,8 +609,7 @@ static void blend_to_neighbor_modal_update(bContext *C, wmOperator *op) /* Reset keyframe data to the state at invoke. */ reset_bezts(gso); - const float factor = ED_slider_factor_get(gso->slider); - RNA_property_float_set(op->ptr, gso->factor_prop, factor); + const float factor = slider_factor_get_and_remember(op); blend_to_neighbor_graph_keys(&gso->ac, factor); WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); @@ -725,7 +735,8 @@ static void breakdown_modal_update(bContext *C, wmOperator *op) /* Reset keyframe data to the state at invoke. */ reset_bezts(gso); - breakdown_graph_keys(&gso->ac, ED_slider_factor_get(gso->slider)); + const float factor = slider_factor_get_and_remember(op); + breakdown_graph_keys(&gso->ac, factor); WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); } @@ -739,6 +750,7 @@ static int breakdown_invoke(bContext *C, wmOperator *op, const wmEvent *event) tGraphSliderOp *gso = op->customdata; gso->modal_update = breakdown_modal_update; + gso->factor_prop = RNA_struct_find_property(op->ptr, "factor"); breakdown_draw_status_header(C, gso); return invoke_result; diff --git a/source/blender/editors/space_graph/graph_utils.c b/source/blender/editors/space_graph/graph_utils.c index 4351186dc6f..9f934e47ebb 100644 --- a/source/blender/editors/space_graph/graph_utils.c +++ b/source/blender/editors/space_graph/graph_utils.c @@ -185,6 +185,7 @@ bool graphop_editable_keyframes_poll(bContext *C) filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_CURVE_VISIBLE); items = ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); if (items == 0) { + CTX_wm_operator_poll_msg_set(C, "There is no animation data to operate on"); return found; } diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c index 27ac34ba346..22427675ff3 100644 --- a/source/blender/editors/space_graph/space_graph.c +++ b/source/blender/editors/space_graph/space_graph.c @@ -242,7 +242,7 @@ static void graph_main_region_draw(const bContext *C, ARegion *region) GPU_blend(GPU_BLEND_NONE); - /* Vertical component of of the cursor. */ + /* Vertical component of the cursor. */ if (sipo->mode == SIPO_MODE_DRIVERS) { /* cursor x-value */ float x = sipo->cursorTime; diff --git a/source/blender/editors/space_info/info_stats.cc b/source/blender/editors/space_info/info_stats.cc index da899ef4c9a..6f0d5c2dbe9 100644 --- a/source/blender/editors/space_info/info_stats.cc +++ b/source/blender/editors/space_info/info_stats.cc @@ -152,7 +152,7 @@ static void stats_object(Object *ob, } break; case OB_SURF: - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_FONT: { const Mesh *me_eval = BKE_object_get_evaluated_mesh(ob); if ((me_eval != nullptr) && !BLI_gset_add(objects_gset, (void *)me_eval)) { @@ -260,7 +260,7 @@ static void stats_object_edit(Object *obedit, SceneStats *stats) stats->totvert += 2; } } - else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { /* OB_FONT has no cu->editnurb */ + else if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) { /* OB_FONT has no cu->editnurb */ /* Curve Edit */ Curve *cu = static_cast<Curve *>(obedit->data); BezTriple *bezt; diff --git a/source/blender/editors/space_nla/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c index 4a507aa3bf2..8b059b33a9a 100644 --- a/source/blender/editors/space_nla/nla_channels.c +++ b/source/blender/editors/space_nla/nla_channels.c @@ -583,7 +583,7 @@ static int nla_action_unlink_invoke(bContext *C, wmOperator *op, const wmEvent * { /* NOTE: this is hardcoded to match the behavior for the unlink button * (in interface_templates.c) */ - RNA_boolean_set(op->ptr, "force_delete", event->shift != 0); + RNA_boolean_set(op->ptr, "force_delete", event->modifier & KM_SHIFT); return nla_action_unlink_exec(C, op); } diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c index eda9f89b51c..d8a0fde6d07 100644 --- a/source/blender/editors/space_nla/nla_draw.c +++ b/source/blender/editors/space_nla/nla_draw.c @@ -110,7 +110,7 @@ static void nla_action_draw_keyframes( */ Range2f frame_range; - ED_keylist_frame_range(keylist, &frame_range); + ED_keylist_all_keys_frame_range(keylist, &frame_range); immRectf(pos_id, frame_range.min, ymin + 2, frame_range.max, ymax - 2); immUnbindProgram(); diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 28ac7a34fa8..afb205f9f9e 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -147,7 +147,7 @@ static void node_buts_curvefloat(uiLayout *layout, bContext *UNUSED(C), PointerR } // namespace blender::ed::space_node #define SAMPLE_FLT_ISNONE FLT_MAX -/* Bad bad, 2.5 will do better? ... no it won't! */ +/* Bad! 2.5 will do better? ... no it won't! */ static float _sample_col[4] = {SAMPLE_FLT_ISNONE}; void ED_node_sample_set(const float col[4]) { @@ -1967,9 +1967,10 @@ void node_draw_link_bezier(const bContext &C, const bNodeLink &link, const int th_col1, const int th_col2, - const int th_col3) + const int th_col3, + const bool selected) { - const float dim_factor = node_link_dim_factor(v2d, link); + const float dim_factor = selected ? 1.0f : node_link_dim_factor(v2d, link); float thickness = 1.5f; float dash_factor = 1.0f; @@ -2025,19 +2026,17 @@ void node_draw_link_bezier(const bContext &C, } /* Highlight links connected to selected nodes. */ - const bool is_fromnode_selected = link.fromnode && link.fromnode->flag & SELECT; - const bool is_tonode_selected = link.tonode && link.tonode->flag & SELECT; - if (is_fromnode_selected || is_tonode_selected) { + if (selected) { float color_selected[4]; UI_GetThemeColor4fv(TH_EDGE_SELECT, color_selected); const float alpha = color_selected[3]; /* Interpolate color if highlight color is not fully transparent. */ if (alpha != 0.0) { - if (is_fromnode_selected) { + if (link.fromsock) { interp_v3_v3v3(colors[1], colors[1], color_selected, alpha); } - if (is_tonode_selected) { + if (link.tosock) { interp_v3_v3v3(colors[2], colors[2], color_selected, alpha); } } @@ -2102,7 +2101,8 @@ void node_draw_link_bezier(const bContext &C, void node_draw_link(const bContext &C, const View2D &v2d, const SpaceNode &snode, - const bNodeLink &link) + const bNodeLink &link, + const bool selected) { int th_col1 = TH_WIRE_INNER, th_col2 = TH_WIRE_INNER, th_col3 = TH_WIRE; @@ -2146,7 +2146,7 @@ void node_draw_link(const bContext &C, } } - node_draw_link_bezier(C, v2d, snode, link, th_col1, th_col2, th_col3); + node_draw_link_bezier(C, v2d, snode, link, th_col1, th_col2, th_col3, selected); } } // namespace blender::ed::space_node diff --git a/source/blender/editors/space_node/node_context_path.cc b/source/blender/editors/space_node/node_context_path.cc index a76988a60d0..349fa92d06d 100644 --- a/source/blender/editors/space_node/node_context_path.cc +++ b/source/blender/editors/space_node/node_context_path.cc @@ -47,7 +47,7 @@ static void context_path_add_object_data(Vector<ui::ContextPathItem> &path, Obje Light *light = (Light *)object.data; ui::context_path_add_generic(path, RNA_Light, light); } - if (ELEM(object.type, OB_CURVE, OB_FONT, OB_SURF) && object.data) { + if (ELEM(object.type, OB_CURVES_LEGACY, OB_FONT, OB_SURF) && object.data) { Curve *curve = (Curve *)object.data; ui::context_path_add_generic(path, RNA_Curve, curve); } diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 455eceed293..1286f6a818c 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -660,7 +660,7 @@ static void node_draw_mute_line(const bContext &C, GPU_blend(GPU_BLEND_ALPHA); LISTBASE_FOREACH (const bNodeLink *, link, &node.internal_links) { - node_draw_link_bezier(C, v2d, snode, *link, TH_WIRE_INNER, TH_WIRE_INNER, TH_WIRE); + node_draw_link_bezier(C, v2d, snode, *link, TH_WIRE_INNER, TH_WIRE_INNER, TH_WIRE, false); } GPU_blend(GPU_BLEND_NONE); @@ -718,7 +718,12 @@ static void node_socket_draw_multi_input(const float color[4], const int locx, const int locy) { - const float outline_width = 1.0f; + /* The other sockets are drawn with the keyframe shader. There, the outline has a base thickness + * that can be varied but always scales with the size the socket is drawn at. Using `U.dpi_fac` + * has the the same effect here. It scales the outline correctly across different screen DPIs + * and UI scales without being affected by the 'line-width'. */ + const float outline_width = NODE_SOCK_OUTLINE_SCALE * U.dpi_fac; + /* UI_draw_roundbox draws the outline on the outer side, so compensate for the outline width. */ const rctf rect = { locx - width + outline_width * 0.5f, @@ -1049,7 +1054,7 @@ static void node_socket_draw_nested(const bContext &C, }, data, MEM_freeN); - /* Disable the button so that clicks on it are ignored the the link operator still works. */ + /* Disable the button so that clicks on it are ignored the link operator still works. */ UI_but_flag_enable(but, UI_BUT_DISABLED); UI_block_emboss_set(&block, old_emboss); } @@ -1060,7 +1065,7 @@ void ED_node_socket_draw(bNodeSocket *sock, const rcti *rect, const float color[ { using namespace blender::ed::space_node; - const float size = 2.25f * NODE_SOCKSIZE * scale; + const float size = NODE_SOCKSIZE_DRAW_MULIPLIER * NODE_SOCKSIZE * scale; rcti draw_rect = *rect; float outline_color[4] = {0}; @@ -1081,7 +1086,7 @@ void ED_node_socket_draw(bNodeSocket *sock, const rcti *rect, const float color[ GPU_program_point_size(true); immBindBuiltinProgram(GPU_SHADER_KEYFRAME_SHAPE); - immUniform1f("outline_scale", 1.0f); + immUniform1f("outline_scale", NODE_SOCK_OUTLINE_SCALE); immUniform2f("ViewportSize", -1.0f, -1.0f); /* Single point. */ @@ -1232,13 +1237,14 @@ static void node_draw_sockets(const View2D &v2d, GPU_blend(GPU_BLEND_ALPHA); GPU_program_point_size(true); immBindBuiltinProgram(GPU_SHADER_KEYFRAME_SHAPE); - immUniform1f("outline_scale", 1.0f); + immUniform1f("outline_scale", NODE_SOCK_OUTLINE_SCALE); immUniform2f("ViewportSize", -1.0f, -1.0f); /* Set handle size. */ + const float socket_draw_size = NODE_SOCKSIZE * NODE_SOCKSIZE_DRAW_MULIPLIER; float scale; UI_view2d_scale_get(&v2d, &scale, nullptr); - scale *= 2.25f * NODE_SOCKSIZE; + scale *= socket_draw_size; if (!select_all) { immBeginAtMost(GPU_PRIM_POINTS, total_input_len + total_output_len); @@ -1251,7 +1257,10 @@ static void node_draw_sockets(const View2D &v2d, continue; } if (select_all || (sock->flag & SELECT)) { - selected_input_len++; + if (!(sock->flag & SOCK_MULTI_INPUT)) { + /* Don't add multi-input sockets here since they are drawn in a different batch. */ + selected_input_len++; + } continue; } /* Don't draw multi-input sockets here since they are drawn in a different batch. */ @@ -1318,6 +1327,10 @@ static void node_draw_sockets(const View2D &v2d, if (nodeSocketIsHidden(sock)) { continue; } + /* Don't draw multi-input sockets here since they are drawn in a different batch. */ + if (sock->flag & SOCK_MULTI_INPUT) { + continue; + } if (select_all || (sock->flag & SELECT)) { node_socket_draw_nested(C, ntree, @@ -1383,13 +1396,13 @@ static void node_draw_sockets(const View2D &v2d, } const bool is_node_hidden = (node.flag & NODE_HIDDEN); - const float width = NODE_SOCKSIZE; + const float width = 0.5f * socket_draw_size; float height = is_node_hidden ? width : node_socket_calculate_height(*socket) - width; float color[4]; float outline_color[4]; node_socket_color_get(C, ntree, node_ptr, *socket, color); - node_socket_outline_color_get(selected, socket->type, outline_color); + node_socket_outline_color_get(socket->flag & SELECT, socket->type, outline_color); node_socket_draw_multi_input(color, outline_color, width, height, socket->locx, socket->locy); } @@ -2650,10 +2663,18 @@ static void node_draw_nodetree(const bContext &C, nodelink_batch_start(snode); LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) { - if (!nodeLinkIsHidden(link)) { - node_draw_link(C, region.v2d, snode, *link); + if (!nodeLinkIsHidden(link) && !nodeLinkIsSelected(link)) { + node_draw_link(C, region.v2d, snode, *link, false); } } + + /* Draw selected node links after the unselected ones, so they are shown on top. */ + LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) { + if (!nodeLinkIsHidden(link) && nodeLinkIsSelected(link)) { + node_draw_link(C, region.v2d, snode, *link, true); + } + } + nodelink_batch_end(snode); GPU_blend(GPU_BLEND_NONE); @@ -2838,7 +2859,7 @@ void node_draw_space(const bContext &C, ARegion ®ion) GPU_line_smooth(true); if (snode.runtime->linkdrag) { for (const bNodeLink *link : snode.runtime->linkdrag->links) { - node_draw_link(C, v2d, snode, *link); + node_draw_link(C, v2d, snode, *link, true); } } GPU_line_smooth(false); diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index 7b7aaef518b..b30be6ae0af 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -89,7 +89,7 @@ struct CompoJob { float node_socket_calculate_height(const bNodeSocket &socket) { - float sock_height = NODE_SOCKSIZE * 2.0f; + float sock_height = NODE_SOCKSIZE * NODE_SOCKSIZE_DRAW_MULIPLIER; if (socket.flag & SOCK_MULTI_INPUT) { sock_height += max_ii(NODE_MULTI_INPUT_LINK_GAP * 0.5f * socket.total_inputs, NODE_SOCKSIZE); } @@ -1160,12 +1160,16 @@ bool node_find_indicated_socket(SpaceNode &snode, { rctf rect; + const float size_sock_padded = NODE_SOCKSIZE + 4; + *nodep = nullptr; *sockp = nullptr; /* check if we click in a socket */ LISTBASE_FOREACH (bNode *, node, &snode.edittree->nodes) { - BLI_rctf_init_pt_radius(&rect, cursor, NODE_SOCKSIZE + 4); + BLI_rctf_init_pt_radius(&rect, cursor, size_sock_padded); + rctf node_visible; + BLI_rctf_init_pt_radius(&node_visible, cursor, size_sock_padded); if (!(node->flag & NODE_HIDDEN)) { /* extra padding inside and out - allow dragging on the text areas too */ @@ -1184,7 +1188,7 @@ bool node_find_indicated_socket(SpaceNode &snode, if (!nodeSocketIsHidden(sock)) { if (sock->flag & SOCK_MULTI_INPUT && !(node->flag & NODE_HIDDEN)) { if (cursor_isect_multi_input_socket(cursor, *sock)) { - if (node == visible_node(snode, rect)) { + if (node == visible_node(snode, node_visible)) { *nodep = node; *sockp = sock; return true; @@ -1192,7 +1196,7 @@ bool node_find_indicated_socket(SpaceNode &snode, } } else if (BLI_rctf_isect_pt(&rect, sock->locx, sock->locy)) { - if (node == visible_node(snode, rect)) { + if (node == visible_node(snode, node_visible)) { *nodep = node; *sockp = sock; return true; @@ -1205,7 +1209,7 @@ bool node_find_indicated_socket(SpaceNode &snode, LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { if (!nodeSocketIsHidden(sock)) { if (BLI_rctf_isect_pt(&rect, sock->locx, sock->locy)) { - if (node == visible_node(snode, rect)) { + if (node == visible_node(snode, node_visible)) { *nodep = node; *sockp = sock; return true; diff --git a/source/blender/editors/space_node/node_intern.hh b/source/blender/editors/space_node/node_intern.hh index 592db3f7877..319f97e57f5 100644 --- a/source/blender/editors/space_node/node_intern.hh +++ b/source/blender/editors/space_node/node_intern.hh @@ -104,6 +104,8 @@ ENUM_OPERATORS(NodeResizeDirection, NODE_RESIZE_LEFT); #define NODE_HEIGHT(node) (node.height * UI_DPI_FAC) #define NODE_MARGIN_X (1.2f * U.widget_unit) #define NODE_SOCKSIZE (0.25f * U.widget_unit) +#define NODE_SOCKSIZE_DRAW_MULIPLIER 2.25f +#define NODE_SOCK_OUTLINE_SCALE 1.0f #define NODE_MULTI_INPUT_LINK_GAP (0.25f * U.widget_unit) #define NODE_RESIZE_MARGIN (0.20f * U.widget_unit) #define NODE_LINK_RESOL 12 @@ -196,7 +198,8 @@ void nodelink_batch_end(SpaceNode &snode); void node_draw_link(const bContext &C, const View2D &v2d, const SpaceNode &snode, - const bNodeLink &link); + const bNodeLink &link, + bool selected); /** * Don't do shadows if th_col3 is -1. */ @@ -206,7 +209,8 @@ void node_draw_link_bezier(const bContext &C, const bNodeLink &link, int th_col1, int th_col2, - int th_col3); + int th_col3, + bool selected); /** If v2d not nullptr, it clips and returns 0 if not visible. */ bool node_link_bezier_points(const View2D *v2d, const SpaceNode *snode, diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index 1cfa932356b..299e7e5658c 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -2436,16 +2436,18 @@ void ED_node_link_insert(Main *bmain, ScrArea *area) bNodeSocket *best_input = get_main_socket(ntree, *node_to_insert, SOCK_IN); bNodeSocket *best_output = get_main_socket(ntree, *node_to_insert, SOCK_OUT); - /* Ignore main sockets when the types don't match. */ - if (best_input != nullptr && ntree.typeinfo->validate_link != nullptr && - !ntree.typeinfo->validate_link(static_cast<eNodeSocketDatatype>(old_link->fromsock->type), - static_cast<eNodeSocketDatatype>(best_input->type))) { - best_input = nullptr; - } - if (best_output != nullptr && ntree.typeinfo->validate_link != nullptr && - !ntree.typeinfo->validate_link(static_cast<eNodeSocketDatatype>(best_output->type), - static_cast<eNodeSocketDatatype>(old_link->tosock->type))) { - best_output = nullptr; + if (node_to_insert->type != NODE_REROUTE) { + /* Ignore main sockets when the types don't match. */ + if (best_input != nullptr && ntree.typeinfo->validate_link != nullptr && + !ntree.typeinfo->validate_link(static_cast<eNodeSocketDatatype>(old_link->fromsock->type), + static_cast<eNodeSocketDatatype>(best_input->type))) { + best_input = nullptr; + } + if (best_output != nullptr && ntree.typeinfo->validate_link != nullptr && + !ntree.typeinfo->validate_link(static_cast<eNodeSocketDatatype>(best_output->type), + static_cast<eNodeSocketDatatype>(old_link->tosock->type))) { + best_output = nullptr; + } } bNode *from_node = old_link->fromnode; diff --git a/source/blender/editors/space_outliner/outliner_collections.cc b/source/blender/editors/space_outliner/outliner_collections.cc index 765661aa9d5..f38f6c2855d 100644 --- a/source/blender/editors/space_outliner/outliner_collections.cc +++ b/source/blender/editors/space_outliner/outliner_collections.cc @@ -1071,7 +1071,7 @@ static int collection_isolate_exec(bContext *C, wmOperator *op) static int collection_isolate_invoke(bContext *C, wmOperator *op, const wmEvent *event) { PropertyRNA *prop = RNA_struct_find_property(op->ptr, "extend"); - if (!RNA_property_is_set(op->ptr, prop) && (event->shift)) { + if (!RNA_property_is_set(op->ptr, prop) && (event->modifier & KM_SHIFT)) { RNA_property_boolean_set(op->ptr, prop, true); } return collection_isolate_exec(C, op); diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.cc b/source/blender/editors/space_outliner/outliner_dragdrop.cc index 0d8ee76d2f0..edd2e5f304f 100644 --- a/source/blender/editors/space_outliner/outliner_dragdrop.cc +++ b/source/blender/editors/space_outliner/outliner_dragdrop.cc @@ -319,7 +319,7 @@ static bool parent_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) } if (!allow_parenting_without_modifier_key(space_outliner)) { - if (!event->shift) { + if ((event->modifier & KM_SHIFT) == 0) { return false; } } @@ -417,8 +417,12 @@ static int parent_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event) ListBase *lb = reinterpret_cast<ListBase *>(event->customdata); wmDrag *drag = reinterpret_cast<wmDrag *>(lb->first); - parent_drop_set_parents( - C, op->reports, reinterpret_cast<wmDragID *>(drag->ids.first), par, PAR_OBJECT, event->alt); + parent_drop_set_parents(C, + op->reports, + reinterpret_cast<wmDragID *>(drag->ids.first), + par, + PAR_OBJECT, + event->modifier & KM_ALT); return OPERATOR_FINISHED; } @@ -446,7 +450,7 @@ static bool parent_clear_poll(bContext *C, wmDrag *drag, const wmEvent *event) SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); if (!allow_parenting_without_modifier_key(space_outliner)) { - if (!event->shift) { + if ((event->modifier & KM_SHIFT) == 0) { return false; } } @@ -471,7 +475,7 @@ static bool parent_clear_poll(bContext *C, wmDrag *drag, const wmEvent *event) case ID_OB: return ELEM(tselem->type, TSE_MODIFIER_BASE, TSE_CONSTRAINT_BASE); case ID_GR: - return event->shift || ELEM(tselem->type, TSE_LIBRARY_OVERRIDE_BASE); + return (event->modifier & KM_SHIFT) || ELEM(tselem->type, TSE_LIBRARY_OVERRIDE_BASE); default: return true; } @@ -496,7 +500,8 @@ static int parent_clear_invoke(bContext *C, wmOperator *UNUSED(op), const wmEven if (GS(drag_id->id->name) == ID_OB) { Object *object = (Object *)drag_id->id; - ED_object_parent_clear(object, event->alt ? CLEAR_PARENT_KEEP_TRANSFORM : CLEAR_PARENT_ALL); + ED_object_parent_clear( + object, (event->modifier & KM_ALT) ? CLEAR_PARENT_KEEP_TRANSFORM : CLEAR_PARENT_ALL); } } @@ -1166,10 +1171,11 @@ static bool collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event &space_outliner->tree, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false); CollectionDrop data; - if (!event->shift && collection_drop_init(C, drag, event->xy, event->ctrl, &data)) { + if (((event->modifier & KM_SHIFT) == 0) && + collection_drop_init(C, drag, event->xy, event->modifier & KM_CTRL, &data)) { TreeElement *te = data.te; TreeStoreElem *tselem = TREESTORE(te); - if (!data.from || event->ctrl) { + if (!data.from || event->modifier & KM_CTRL) { tselem->flag |= TSE_DRAG_INTO; changed = true; } @@ -1210,9 +1216,10 @@ static char *collection_drop_tooltip(bContext *C, const wmEvent *event = win ? win->eventstate : nullptr; CollectionDrop data; - if (event && !event->shift && collection_drop_init(C, drag, xy, event->ctrl, &data)) { + if (event && ((event->modifier & KM_SHIFT) == 0) && + collection_drop_init(C, drag, xy, event->modifier & KM_CTRL, &data)) { TreeElement *te = data.te; - if (!data.from || event->ctrl) { + if (!data.from || event->modifier & KM_CTRL) { return BLI_strdup(TIP_("Link inside Collection")); } switch (data.insert_type) { @@ -1263,7 +1270,7 @@ static int collection_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmE wmDrag *drag = reinterpret_cast<wmDrag *>(lb->first); CollectionDrop data; - if (!collection_drop_init(C, drag, event->xy, event->ctrl, &data)) { + if (!collection_drop_init(C, drag, event->xy, event->modifier & KM_CTRL, &data)) { return OPERATOR_CANCELLED; } @@ -1291,7 +1298,9 @@ static int collection_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmE LISTBASE_FOREACH (wmDragID *, drag_id, &drag->ids) { /* Ctrl enables linking, so we don't need a from collection then. */ - Collection *from = (event->ctrl) ? nullptr : collection_parent_from_ID(drag_id->from_parent); + Collection *from = (event->modifier & KM_CTRL) ? + nullptr : + collection_parent_from_ID(drag_id->from_parent); if (GS(drag_id->id->name) == ID_OB) { /* Move/link object into collection. */ diff --git a/source/blender/editors/space_outliner/outliner_draw.cc b/source/blender/editors/space_outliner/outliner_draw.cc index dd7ca128282..2da416c8671 100644 --- a/source/blender/editors/space_outliner/outliner_draw.cc +++ b/source/blender/editors/space_outliner/outliner_draw.cc @@ -166,7 +166,7 @@ static void restrictbutton_bone_visibility_fn(bContext *C, void *poin, void *UNU { Bone *bone = (Bone *)poin; - if (CTX_wm_window(C)->eventstate->shift) { + if (CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) { restrictbutton_recursive_bone(bone, BONE_HIDDEN_P, (bone->flag & BONE_HIDDEN_P) != 0); } } @@ -178,7 +178,7 @@ static void restrictbutton_bone_select_fn(bContext *C, void *UNUSED(poin), void bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); } - if (CTX_wm_window(C)->eventstate->shift) { + if (CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) { restrictbutton_recursive_bone(bone, BONE_UNSELECTABLE, (bone->flag & BONE_UNSELECTABLE) != 0); } @@ -194,7 +194,7 @@ static void restrictbutton_ebone_select_fn(bContext *C, void *poin, void *poin2) ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); } - if (CTX_wm_window(C)->eventstate->shift) { + if (CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) { restrictbutton_recursive_ebone( arm, ebone, BONE_UNSELECTABLE, (ebone->flag & BONE_UNSELECTABLE) != 0); } @@ -210,7 +210,7 @@ static void restrictbutton_ebone_visibility_fn(bContext *C, void *poin, void *po ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); } - if (CTX_wm_window(C)->eventstate->shift) { + if (CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) { restrictbutton_recursive_ebone(arm, ebone, BONE_HIDDEN_A, (ebone->flag & BONE_HIDDEN_A) != 0); } @@ -250,7 +250,7 @@ static void outliner_object_set_flag_recursive_fn(bContext *C, ViewLayer *view_layer = CTX_data_view_layer(C); PointerRNA ptr; - bool extend = (win->eventstate->shift != 0); + bool extend = (win->eventstate->modifier & KM_SHIFT); if (!extend) { return; @@ -571,8 +571,8 @@ static void outliner_collection_set_flag_recursive_fn(bContext *C, ViewLayer *view_layer = CTX_data_view_layer(C); PointerRNA ptr; - bool do_isolate = (win->eventstate->ctrl != 0); - bool extend = (win->eventstate->shift != 0); + bool do_isolate = (win->eventstate->modifier & KM_CTRL); + bool extend = (win->eventstate->modifier & KM_SHIFT); if (!ELEM(true, do_isolate, extend)) { return; @@ -2043,7 +2043,7 @@ static void outliner_mode_toggle_fn(bContext *C, void *tselem_poin, void *UNUSED const bool object_data_shared = (ob->data == tvc.obact->data); wmWindow *win = CTX_wm_window(C); - const bool do_extend = win->eventstate->ctrl != 0 && !object_data_shared; + const bool do_extend = (win->eventstate->modifier & KM_CTRL) && !object_data_shared; outliner_item_mode_toggle(C, &tvc, te, do_extend); } @@ -2592,7 +2592,7 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) case OB_CAMERA: data.icon = ICON_OUTLINER_OB_CAMERA; break; - case OB_CURVE: + case OB_CURVES_LEGACY: data.icon = ICON_OUTLINER_OB_CURVE; break; case OB_MBALL: @@ -2655,7 +2655,7 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) case ID_ME: data.icon = ICON_OUTLINER_DATA_MESH; break; - case ID_CU: + case ID_CU_LEGACY: data.icon = ICON_OUTLINER_DATA_CURVE; break; case ID_MB: diff --git a/source/blender/editors/space_outliner/outliner_edit.cc b/source/blender/editors/space_outliner/outliner_edit.cc index a6ac2a5a1f3..6916f5fe502 100644 --- a/source/blender/editors/space_outliner/outliner_edit.cc +++ b/source/blender/editors/space_outliner/outliner_edit.cc @@ -240,7 +240,7 @@ static int outliner_item_openclose_invoke(bContext *C, wmOperator *op, const wmE outliner_tag_redraw_avoid_rebuild_on_open_change(space_outliner, region); /* Only toggle once for single click toggling */ - if (event->type == LEFTMOUSE) { + if ((event->type == LEFTMOUSE) && (event->val != KM_CLICK_DRAG)) { return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_outliner/outliner_intern.hh b/source/blender/editors/space_outliner/outliner_intern.hh index bba4bac0e37..0516758e887 100644 --- a/source/blender/editors/space_outliner/outliner_intern.hh +++ b/source/blender/editors/space_outliner/outliner_intern.hh @@ -108,7 +108,7 @@ typedef struct TreeElementIcon { ID_LI, \ ID_OB, \ ID_ME, \ - ID_CU, \ + ID_CU_LEGACY, \ ID_MB, \ ID_NT, \ ID_MA, \ diff --git a/source/blender/editors/space_outliner/outliner_select.cc b/source/blender/editors/space_outliner/outliner_select.cc index df10ce002c3..c6b9d9577b5 100644 --- a/source/blender/editors/space_outliner/outliner_select.cc +++ b/source/blender/editors/space_outliner/outliner_select.cc @@ -1171,7 +1171,7 @@ static void outliner_set_properties_tab(bContext *C, TreeElement *te, TreeStoreE context = BCONTEXT_OBJECT; break; case ID_ME: - case ID_CU: + case ID_CU_LEGACY: case ID_MB: case ID_IM: case ID_LT: diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc index 18c37e08eff..8fcf967bce8 100644 --- a/source/blender/editors/space_outliner/outliner_tools.cc +++ b/source/blender/editors/space_outliner/outliner_tools.cc @@ -117,7 +117,7 @@ static void get_element_operation_type( break; case ID_ME: - case ID_CU: + case ID_CU_LEGACY: case ID_MB: case ID_LT: case ID_LA: @@ -236,7 +236,7 @@ static void unlink_material_fn(bContext *UNUSED(C), totcol = me->totcol; matar = me->mat; } - else if (GS(tsep->id->name) == ID_CU) { + else if (GS(tsep->id->name) == ID_CU_LEGACY) { Curve *cu = (Curve *)tsep->id; totcol = cu->totcol; matar = cu->mat; @@ -766,29 +766,37 @@ static void id_override_library_create_fn(bContext *C, void *user_data) { BLI_assert(TSE_IS_REAL_ID(tselem)); - ID *id_root = tselem->id; + + /* We can only safely apply this operation on one item at a time, so only do it on the active + * one. */ + if ((tselem->flag & TSE_ACTIVE) == 0) { + return; + } + + ID *id_root_reference = tselem->id; OutlinerLibOverrideData *data = reinterpret_cast<OutlinerLibOverrideData *>(user_data); const bool do_hierarchy = data->do_hierarchy; bool success = false; - ID *id_reference = nullptr; + ID *id_instance_hint = nullptr; bool is_override_instancing_object = false; if (tsep != nullptr && tsep->type == TSE_SOME_ID && tsep->id != nullptr && GS(tsep->id->name) == ID_OB && !ID_IS_OVERRIDE_LIBRARY(tsep->id)) { Object *ob = (Object *)tsep->id; - if (ob->type == OB_EMPTY && &ob->instance_collection->id == id_root) { - BLI_assert(GS(id_root->name) == ID_GR); + if (ob->type == OB_EMPTY && &ob->instance_collection->id == id_root_reference) { + BLI_assert(GS(id_root_reference->name) == ID_GR); /* Empty instantiating the collection we override, we need to pass it to BKE overriding code * for proper handling. */ - id_reference = tsep->id; + id_instance_hint = tsep->id; is_override_instancing_object = true; } } - if (ID_IS_OVERRIDABLE_LIBRARY(id_root) || (ID_IS_LINKED(id_root) && do_hierarchy)) { + if (ID_IS_OVERRIDABLE_LIBRARY(id_root_reference) || + (ID_IS_LINKED(id_root_reference) && do_hierarchy)) { Main *bmain = CTX_data_main(C); - id_root->tag |= LIB_TAG_DOIT; + id_root_reference->tag |= LIB_TAG_DOIT; /* For now, remap all local usages of linked ID to local override one here. */ ID *id_iter; @@ -804,38 +812,100 @@ static void id_override_library_create_fn(bContext *C, if (do_hierarchy) { /* Tag all linked parents in tree hierarchy to be also overridden. */ + ID *id_hierarchy_root_reference = id_root_reference; while ((te = te->parent) != nullptr) { if (!TSE_IS_REAL_ID(te->store_elem)) { continue; } - if (!ID_IS_LINKED(te->store_elem->id)) { + + /* Tentative hierarchy root. */ + ID *id_current_hierarchy_root = te->store_elem->id; + + /* If the parent ID is from a different library than the reference root one, we are done + * with upwards tree processing in any case. */ + if (id_current_hierarchy_root->lib != id_root_reference->lib) { + if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(id_current_hierarchy_root)) { + /* Virtual overrides (i.e. embedded IDs), we can simply keep processing their parent to + * get an actual real override. */ + continue; + } + + /* If the parent ID is already an override, and is valid (i.e. local override), we can + * access its hierarchy root directly. */ + if (!ID_IS_LINKED(id_current_hierarchy_root) && + ID_IS_OVERRIDE_LIBRARY_REAL(id_current_hierarchy_root) && + id_current_hierarchy_root->override_library->reference->lib == + id_root_reference->lib) { + id_hierarchy_root_reference = + id_current_hierarchy_root->override_library->hierarchy_root; + BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference)); + break; + } + + if (ID_IS_LINKED(id_current_hierarchy_root)) { + /* No local 'anchor' was found for the hierarchy to override, do not proceed, as this + * would most likely generate invisible/confusing/hard to use and manage overrides. */ + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); + BKE_reportf(reports, + RPT_WARNING, + "Invalid anchor ('%s') found, needed to create library override from " + "data-block '%s'", + id_current_hierarchy_root->name, + id_root_reference->name); + return; + } + + /* In all other cases, `id_current_hierarchy_root` cannot be a valid hierarchy root, so + * current `id_hierarchy_root_reference` is our best candidate. */ + break; } + /* If some element in the tree needs to be overridden, but its ID is not overridable, * abort. */ - if (!ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(te->store_elem->id)) { + if (!ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(id_current_hierarchy_root)) { 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); + id_root_reference->name, + id_current_hierarchy_root->name); return; } - te->store_elem->id->tag |= LIB_TAG_DOIT; + id_current_hierarchy_root->tag |= LIB_TAG_DOIT; + id_hierarchy_root_reference = id_current_hierarchy_root; + } + + /* That case can happen when linked data is a complex mix involving several libraries and/or + * linked overrides. E.g. a mix of overrides from one library, and indirectly linked data + * from another library. Do not try to support such cases for now. */ + if (!((id_hierarchy_root_reference->lib == id_root_reference->lib) || + (!ID_IS_LINKED(id_hierarchy_root_reference) && + ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference) && + id_hierarchy_root_reference->override_library->reference->lib == + id_root_reference->lib))) { + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); + BKE_reportf(reports, + RPT_WARNING, + "Invalid hierarchy root ('%s') found, needed to create library override from " + "data-block '%s'", + id_hierarchy_root_reference->name, + id_root_reference->name); + return; } success = BKE_lib_override_library_create(bmain, CTX_data_scene(C), CTX_data_view_layer(C), nullptr, - id_root, - id_reference, + id_root_reference, + id_hierarchy_root_reference, + id_instance_hint, nullptr); } - else if (ID_IS_OVERRIDABLE_LIBRARY(id_root)) { - success = BKE_lib_override_library_create_from_id(bmain, id_root, true) != nullptr; + else if (ID_IS_OVERRIDABLE_LIBRARY(id_root_reference)) { + success = BKE_lib_override_library_create_from_id(bmain, id_root_reference, true) != nullptr; /* Cleanup. */ BKE_main_id_newptr_and_tag_clear(bmain); @@ -845,14 +915,14 @@ static void id_override_library_create_fn(bContext *C, /* Remove the instance empty from this scene, the items now have an overridden collection * instead. */ if (success && is_override_instancing_object) { - ED_object_base_free_and_unlink(bmain, scene, (Object *)id_reference); + ED_object_base_free_and_unlink(bmain, scene, (Object *)id_instance_hint); } } if (!success) { BKE_reportf(reports, RPT_WARNING, "Could not create library override from data-block '%s'", - id_root->name); + id_root_reference->name); } } @@ -1770,13 +1840,15 @@ static const EnumPropertyItem prop_id_op_types[] = { {OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE, "OVERRIDE_LIBRARY_CREATE", 0, - "Make Library Override", - "Make a local override of this linked data-block"}, + "Make Library Override Single", + "Make a single, out-of-hierarchy local override of this linked data-block - only applies to " + "active Outliner item"}, {OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY, "OVERRIDE_LIBRARY_CREATE_HIERARCHY", 0, "Make Library Override Hierarchy", - "Make a local override of this linked data-block, and its hierarchy of dependencies"}, + "Make a local override of this linked data-block, and its hierarchy of dependencies - only " + "applies to active Outliner item"}, {OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET, "OVERRIDE_LIBRARY_RESET", 0, diff --git a/source/blender/editors/space_outliner/outliner_tree.cc b/source/blender/editors/space_outliner/outliner_tree.cc index 1605d5874ae..06a5918f25c 100644 --- a/source/blender/editors/space_outliner/outliner_tree.cc +++ b/source/blender/editors/space_outliner/outliner_tree.cc @@ -579,7 +579,7 @@ static void outliner_add_id_contents(SpaceOutliner *space_outliner, * would require going over all tfaces, sort images in use. etc... */ break; } - case ID_CU: { + case ID_CU_LEGACY: { Curve *cu = (Curve *)id; if (outliner_animdata_test(cu->adt)) { diff --git a/source/blender/editors/space_outliner/tree/tree_display.cc b/source/blender/editors/space_outliner/tree/tree_display.cc index 6b68f1ee4a4..f9141dffd6a 100644 --- a/source/blender/editors/space_outliner/tree/tree_display.cc +++ b/source/blender/editors/space_outliner/tree/tree_display.cc @@ -7,6 +7,8 @@ #include "DNA_listBase.h" #include "DNA_space_types.h" +#include "BLI_utildefines.h" + #include "tree_display.hh" using namespace blender::ed::outliner; @@ -30,11 +32,11 @@ std::unique_ptr<AbstractTreeDisplay> AbstractTreeDisplay::createFromDisplayMode( case SO_OVERRIDES_LIBRARY: return std::make_unique<TreeDisplayOverrideLibrary>(space_outliner); case SO_VIEW_LAYER: - /* FIXME(Julian): this should not be the default! Return nullptr and handle that as valid - * case. */ - default: return std::make_unique<TreeDisplayViewLayer>(space_outliner); } + + BLI_assert_unreachable(); + return nullptr; } bool AbstractTreeDisplay::hasWarnings() const diff --git a/source/blender/editors/space_outliner/tree/tree_display_override_library.cc b/source/blender/editors/space_outliner/tree/tree_display_override_library.cc index 43d67ee106d..f94727ba356 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_override_library.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_override_library.cc @@ -23,11 +23,6 @@ namespace blender::ed::outliner { /* Convenience/readability. */ -/* Convenience/readability. */ -/* Convenience/readability. */ -/* Convenience/readability. */ -/* Convenience/readability. */ -/* Convenience/readability. */ template<typename T> using List = ListBaseWrapper<T>; TreeDisplayOverrideLibrary::TreeDisplayOverrideLibrary(SpaceOutliner &space_outliner) diff --git a/source/blender/editors/space_outliner/tree/tree_element.hh b/source/blender/editors/space_outliner/tree/tree_element.hh index 996f51eee82..2fbc86705b9 100644 --- a/source/blender/editors/space_outliner/tree/tree_element.hh +++ b/source/blender/editors/space_outliner/tree/tree_element.hh @@ -89,8 +89,8 @@ void tree_element_expand(const AbstractTreeElement &tree_element, SpaceOutliner /** * Get actual warning data of a tree element, if any. * - * \param r_icon The icon to display as warning. - * \param r_message The message to display as warning. + * \param r_icon: The icon to display as warning. + * \param r_message: The message to display as warning. * \return true if there is a warning, false otherwise. */ bool tree_element_warnings_get(struct TreeElement *te, int *r_icon, const char **r_message); diff --git a/source/blender/editors/space_outliner/tree/tree_element_id.cc b/source/blender/editors/space_outliner/tree/tree_element_id.cc index e126b024d52..64c73f57107 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_id.cc +++ b/source/blender/editors/space_outliner/tree/tree_element_id.cc @@ -34,7 +34,7 @@ std::unique_ptr<TreeElementID> TreeElementID::createFromID(TreeElement &legacy_t return std::make_unique<TreeElementIDScene>(legacy_te, (Scene &)id); case ID_OB: case ID_ME: - case ID_CU: + case ID_CU_LEGACY: case ID_MB: case ID_MA: case ID_TE: diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 8c12193fb88..5ac4363e63d 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -1636,7 +1636,9 @@ static void sequencer_draw_gpencil_overlay(const bContext *C) ED_annotation_draw_view2d(C, 0); } -/* Draw content and safety borders borders. */ +/** + * Draw content and safety borders. + */ static void sequencer_draw_borders_overlay(const SpaceSeq *sseq, const View2D *v2d, const Scene *scene) @@ -1926,7 +1928,6 @@ static void sequencer_draw_display_buffer(const bContext *C, if (!glsl_used) { immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_COLOR); immUniformColor3f(1.0f, 1.0f, 1.0f); - immUniform1i("image", 0); } immBegin(GPU_PRIM_TRI_FAN, 4); diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc index 3be890bfcc5..fbdc451cf06 100644 --- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc +++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc @@ -264,7 +264,7 @@ Object *spreadsheet_get_object_eval(const SpaceSpreadsheet *sspreadsheet, return nullptr; } Object *object_orig = (Object *)used_id; - if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD, OB_VOLUME, OB_CURVE, OB_FONT)) { + if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD, OB_VOLUME, OB_CURVES_LEGACY, OB_FONT)) { return nullptr; } diff --git a/source/blender/editors/space_text/text_autocomplete.c b/source/blender/editors/space_text/text_autocomplete.c index 496f500ef04..55873740491 100644 --- a/source/blender/editors/space_text/text_autocomplete.c +++ b/source/blender/editors/space_text/text_autocomplete.c @@ -408,7 +408,7 @@ static int text_autocomplete_modal(bContext *C, wmOperator *op, const wmEvent *e case EVT_BACKSPACEKEY: if (event->val == KM_PRESS) { if (tools & TOOL_SUGG_LIST) { - if (event->ctrl) { + if (event->modifier & KM_CTRL) { texttool_suggest_clear(); retval = OPERATOR_CANCELLED; draw = 1; @@ -445,7 +445,7 @@ static int text_autocomplete_modal(bContext *C, wmOperator *op, const wmEvent *e case EVT_RIGHTARROWKEY: if (event->val == KM_PRESS) { if (tools & TOOL_SUGG_LIST) { - if (event->ctrl) { + if (event->modifier & KM_CTRL) { texttool_suggest_clear(); retval = OPERATOR_CANCELLED; draw = 1; diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c index ddba6803f61..3c29b32c2fa 100644 --- a/source/blender/editors/space_text/text_ops.c +++ b/source/blender/editors/space_text/text_ops.c @@ -3495,7 +3495,7 @@ static int text_insert_invoke(bContext *C, wmOperator *op, const wmEvent *event) * (when input method are used for utf8 inputs, the user may assign key event * including alt/ctrl/super like ctrl+m to commit utf8 string. in such case, * the modifiers in the utf8 character event make no sense.) */ - if ((event->ctrl || event->oskey) && !event->utf8_buf[0]) { + if ((event->modifier & (KM_CTRL | KM_OSKEY)) && !event->utf8_buf[0]) { return OPERATOR_PASS_THROUGH; } diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 7addda0f8c3..4656540c19b 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -413,6 +413,9 @@ static void view3d_main_region_init(wmWindowManager *wm, ARegion *region) keymap = WM_keymap_ensure(wm->defaultconf, "Particle", 0, 0); WM_event_add_keymap_handler(®ion->handlers, keymap); + keymap = WM_keymap_ensure(wm->defaultconf, "Sculpt Curves", 0, 0); + WM_event_add_keymap_handler(®ion->handlers, keymap); + /* editfont keymap swallows all... */ keymap = WM_keymap_ensure(wm->defaultconf, "Font", 0, 0); WM_event_add_keymap_handler(®ion->handlers, keymap); @@ -1475,6 +1478,9 @@ void ED_view3d_buttons_region_layout_ex(const bContext *C, case CTX_MODE_EDIT_CURVE: ARRAY_SET_ITEMS(contexts, ".curve_edit"); break; + case CTX_MODE_EDIT_CURVES: + ARRAY_SET_ITEMS(contexts, ".curves_edit"); + break; case CTX_MODE_EDIT_SURFACE: ARRAY_SET_ITEMS(contexts, ".curve_edit"); break; @@ -1523,6 +1529,9 @@ void ED_view3d_buttons_region_layout_ex(const bContext *C, case CTX_MODE_VERTEX_GPENCIL: ARRAY_SET_ITEMS(contexts, ".greasepencil_vertex"); break; + case CTX_MODE_SCULPT_CURVES: + ARRAY_SET_ITEMS(contexts, ".curves_sculpt"); + break; default: break; } diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c index b77994e28cb..cf52134f5ab 100644 --- a/source/blender/editors/space_view3d/view3d_buttons.c +++ b/source/blender/editors/space_view3d/view3d_buttons.c @@ -355,7 +355,7 @@ static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float has_meshdata = (tot || totedgedata); } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF)) { TransformMedian_Curve *median = &median_basis.curve; Curve *cu = ob->data; BPoint *bp; @@ -1089,7 +1089,7 @@ static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float } } } - else if (ELEM(ob->type, OB_CURVE, OB_SURF) && + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF) && (apply_vcos || median_basis.curve.b_weight || median_basis.curve.weight || median_basis.curve.radius || median_basis.curve.tilt)) { const TransformMedian_Curve *median = &median_basis.curve, diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c index 785c5ab28c8..53f7b3d5871 100644 --- a/source/blender/editors/space_view3d/view3d_cursor_snap.c +++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c @@ -63,7 +63,7 @@ typedef struct SnapCursorDataIntern { int x; int y; #ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK - short shift, ctrl, alt, oskey; + uint8_t modifier; #endif } last_eventstate; @@ -478,10 +478,7 @@ static bool v3d_cursor_eventstate_has_changed(SnapCursorDataIntern *data_intern, } #ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK if (!(state && (state->flag & V3D_SNAPCURSOR_TOGGLE_ALWAYS_TRUE))) { - if ((event->ctrl != data_intern->last_eventstate.ctrl) || - (event->shift != data_intern->last_eventstate.shift) || - (event->alt != data_intern->last_eventstate.alt) || - (event->oskey != data_intern->last_eventstate.oskey)) { + if (event->modifier != data_intern->last_eventstate.modifier) { return true; } } @@ -507,19 +504,13 @@ static bool v3d_cursor_is_snap_invert(SnapCursorDataIntern *data_intern, const w } const wmEvent *event = wm->winactive->eventstate; - if ((event->ctrl == data_intern->last_eventstate.ctrl) && - (event->shift == data_intern->last_eventstate.shift) && - (event->alt == data_intern->last_eventstate.alt) && - (event->oskey == data_intern->last_eventstate.oskey)) { + if (event->modifier == data_intern->last_eventstate.modifier) { /* Nothing has changed. */ return data_intern->snap_data.is_snap_invert; } /* Save new eventstate. */ - data_intern->last_eventstate.ctrl = event->ctrl; - data_intern->last_eventstate.shift = event->shift; - data_intern->last_eventstate.alt = event->alt; - data_intern->last_eventstate.oskey = event->oskey; + data_intern->last_eventstate.modifier = event->modifier; const int snap_on = data_intern->snap_on; @@ -530,10 +521,10 @@ static bool v3d_cursor_is_snap_invert(SnapCursorDataIntern *data_intern, const w } if (kmi->propvalue == snap_on) { - if ((ELEM(kmi->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY) && event->ctrl) || - (ELEM(kmi->type, EVT_LEFTSHIFTKEY, EVT_RIGHTSHIFTKEY) && event->shift) || - (ELEM(kmi->type, EVT_LEFTALTKEY, EVT_RIGHTALTKEY) && event->alt) || - ((kmi->type == EVT_OSKEY) && event->oskey)) { + if ((ELEM(kmi->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY) && (event->modifier & KM_CTRL)) || + (ELEM(kmi->type, EVT_LEFTSHIFTKEY, EVT_RIGHTSHIFTKEY) && (event->modifier & KM_SHIFT)) || + (ELEM(kmi->type, EVT_LEFTALTKEY, EVT_RIGHTALTKEY) && (event->modifier & KM_ALT)) || + ((kmi->type == EVT_OSKEY) && (event->modifier & KM_OSKEY))) { return true; } } diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index c4078c4a690..593c4f6e755 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -1351,7 +1351,7 @@ static void draw_selected_name( } } } - else if (ELEM(ob->type, OB_MESH, OB_LATTICE, OB_CURVE)) { + else if (ELEM(ob->type, OB_MESH, OB_LATTICE, OB_CURVES_LEGACY)) { /* try to display active bone and active shapekey too (if they exist) */ if (ob->type == OB_MESH && ob->mode & OB_MODE_WEIGHT_PAINT) { diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index d6bc7ded92e..5adce170e06 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -832,13 +832,13 @@ void ED_view3d_cursor3d_position(bContext *C, return; } - ED_view3d_calc_zfac(rv3d, cursor_co, &flip); + ED_view3d_calc_zfac_ex(rv3d, cursor_co, &flip); /* Reset the depth based on the view offset (we _know_ the offset is in front of us). */ if (flip) { negate_v3_v3(cursor_co, rv3d->ofs); /* re initialize, no need to check flip again */ - ED_view3d_calc_zfac(rv3d, cursor_co, NULL /* &flip */); + ED_view3d_calc_zfac(rv3d, cursor_co); } if (use_depth) { /* maybe this should be accessed some other way */ diff --git a/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c b/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c index 6cc197c8a43..a0c010a6813 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c @@ -105,8 +105,8 @@ static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int MeshElemGizmo3D *gz_ele = (MeshElemGizmo3D *)gz; /* Hack: Switch action mode based on key input */ - const bool is_ctrl_pressed = WM_event_modifier_flag(event) & KM_CTRL; - const bool is_shift_pressed = WM_event_modifier_flag(event) & KM_SHIFT; + const bool is_ctrl_pressed = (event->modifier & KM_CTRL) != 0; + const bool is_shift_pressed = (event->modifier & KM_SHIFT) != 0; EDBM_preselect_action_set(gz_ele->psel, PRESELECT_ACTION_TRANSFORM); if (is_ctrl_pressed && !is_shift_pressed) { EDBM_preselect_action_set(gz_ele->psel, PRESELECT_ACTION_CREATE); diff --git a/source/blender/editors/space_view3d/view3d_iterators.c b/source/blender/editors/space_view3d/view3d_iterators.c index e1fd96ca1d4..055aac041f1 100644 --- a/source/blender/editors/space_view3d/view3d_iterators.c +++ b/source/blender/editors/space_view3d/view3d_iterators.c @@ -25,6 +25,7 @@ #include "BKE_editmesh.h" #include "BKE_mesh_iterators.h" #include "BKE_mesh_runtime.h" +#include "BKE_mesh_wrapper.h" #include "BKE_modifier.h" #include "DEG_depsgraph.h" @@ -334,6 +335,7 @@ void mesh_foreachScreenVert( Mesh *me = editbmesh_get_eval_cage_from_orig( vc->depsgraph, vc->scene, vc->obedit, &CD_MASK_BAREMESH); + me = BKE_mesh_wrapper_ensure_subdivision(vc->obedit, me); ED_view3d_check_mats_rv3d(vc->rv3d); @@ -396,6 +398,7 @@ void mesh_foreachScreenEdge(ViewContext *vc, Mesh *me = editbmesh_get_eval_cage_from_orig( vc->depsgraph, vc->scene, vc->obedit, &CD_MASK_BAREMESH); + me = BKE_mesh_wrapper_ensure_subdivision(vc->obedit, me); ED_view3d_check_mats_rv3d(vc->rv3d); @@ -483,6 +486,7 @@ void mesh_foreachScreenEdge_clip_bb_segment(ViewContext *vc, Mesh *me = editbmesh_get_eval_cage_from_orig( vc->depsgraph, vc->scene, vc->obedit, &CD_MASK_BAREMESH); + me = BKE_mesh_wrapper_ensure_subdivision(vc->obedit, me); ED_view3d_check_mats_rv3d(vc->rv3d); @@ -554,6 +558,7 @@ void mesh_foreachScreenFace( Mesh *me = editbmesh_get_eval_cage_from_orig( vc->depsgraph, vc->scene, vc->obedit, &CD_MASK_BAREMESH); + me = BKE_mesh_wrapper_ensure_subdivision(vc->obedit, me); ED_view3d_check_mats_rv3d(vc->rv3d); data.vc = *vc; diff --git a/source/blender/editors/space_view3d/view3d_navigate.c b/source/blender/editors/space_view3d/view3d_navigate.c index 0305989d142..d1e7f6ffb12 100644 --- a/source/blender/editors/space_view3d/view3d_navigate.c +++ b/source/blender/editors/space_view3d/view3d_navigate.c @@ -396,7 +396,7 @@ ViewOpsData *viewops_data_create(bContext *C, const wmEvent *event, enum eViewOp { float tvec[3]; negate_v3_v3(tvec, rv3d->ofs); - vod->init.zfac = ED_view3d_calc_zfac(rv3d, tvec, NULL); + vod->init.zfac = ED_view3d_calc_zfac(rv3d, tvec); } vod->reverse = 1.0f; @@ -544,26 +544,24 @@ static void axis_set_view(bContext *C, void viewmove_apply(ViewOpsData *vod, int x, int y) { - if (ED_view3d_offset_lock_check(vod->v3d, vod->rv3d)) { - vod->rv3d->ofs_lock[0] -= ((vod->prev.event_xy[0] - x) * 2.0f) / (float)vod->region->winx; - vod->rv3d->ofs_lock[1] -= ((vod->prev.event_xy[1] - y) * 2.0f) / (float)vod->region->winy; + const float event_ofs[2] = { + vod->prev.event_xy[0] - x, + vod->prev.event_xy[1] - y, + }; + + if ((vod->rv3d->persp == RV3D_CAMOB) && !ED_view3d_camera_lock_check(vod->v3d, vod->rv3d)) { + ED_view3d_camera_view_pan(vod->region, event_ofs); } - else if ((vod->rv3d->persp == RV3D_CAMOB) && !ED_view3d_camera_lock_check(vod->v3d, vod->rv3d)) { - const float zoomfac = BKE_screen_view3d_zoom_to_fac(vod->rv3d->camzoom) * 2.0f; - vod->rv3d->camdx += (vod->prev.event_xy[0] - x) / (vod->region->winx * zoomfac); - vod->rv3d->camdy += (vod->prev.event_xy[1] - y) / (vod->region->winy * zoomfac); - CLAMP(vod->rv3d->camdx, -1.0f, 1.0f); - CLAMP(vod->rv3d->camdy, -1.0f, 1.0f); + else if (ED_view3d_offset_lock_check(vod->v3d, vod->rv3d)) { + vod->rv3d->ofs_lock[0] -= (event_ofs[0] * 2.0f) / (float)vod->region->winx; + vod->rv3d->ofs_lock[1] -= (event_ofs[1] * 2.0f) / (float)vod->region->winy; } else { float dvec[3]; - float mval_f[2]; - mval_f[0] = x - vod->prev.event_xy[0]; - mval_f[1] = y - vod->prev.event_xy[1]; - ED_view3d_win_to_delta(vod->region, mval_f, dvec, vod->init.zfac); + ED_view3d_win_to_delta(vod->region, event_ofs, vod->init.zfac, dvec); - add_v3_v3(vod->rv3d->ofs, dvec); + sub_v3_v3(vod->rv3d->ofs, dvec); if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_BOXVIEW) { view3d_boxview_sync(vod->area, vod->region); diff --git a/source/blender/editors/space_view3d/view3d_navigate_dolly.c b/source/blender/editors/space_view3d/view3d_navigate_dolly.c index 06b616e71da..7b6b119294d 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_dolly.c +++ b/source/blender/editors/space_view3d/view3d_navigate_dolly.c @@ -50,9 +50,12 @@ void viewdolly_modal_keymap(wmKeyConfig *keyconf) /* disabled mode switching for now, can re-implement better, later on */ #if 0 - WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE); - WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE); - WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE); + WM_modalkeymap_add_item( + keymap, LEFTMOUSE, KM_RELEASE, KM_ANY, 0, KM_ANY, VIEWROT_MODAL_SWITCH_ROTATE); + WM_modalkeymap_add_item( + keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, KM_ANY, VIEWROT_MODAL_SWITCH_ROTATE); + WM_modalkeymap_add_item( + keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, KM_ANY, VIEWROT_MODAL_SWITCH_MOVE); #endif /* assign map to operators */ diff --git a/source/blender/editors/space_view3d/view3d_navigate_move.c b/source/blender/editors/space_view3d/view3d_navigate_move.c index d2fd505a703..071643e9314 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_move.c +++ b/source/blender/editors/space_view3d/view3d_navigate_move.c @@ -43,8 +43,8 @@ void viewmove_modal_keymap(wmKeyConfig *keyconf) keymap = WM_modalkeymap_ensure(keyconf, "View3D Move Modal", modal_items); /* items for modal map */ - WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, VIEW_MODAL_CONFIRM); - WM_modalkeymap_add_item(keymap, EVT_ESCKEY, KM_PRESS, KM_ANY, 0, VIEW_MODAL_CONFIRM); + WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, KM_ANY, VIEW_MODAL_CONFIRM); + WM_modalkeymap_add_item(keymap, EVT_ESCKEY, KM_PRESS, KM_ANY, 0, KM_ANY, VIEW_MODAL_CONFIRM); /* disabled mode switching for now, can re-implement better, later on */ #if 0 diff --git a/source/blender/editors/space_view3d/view3d_navigate_ndof.c b/source/blender/editors/space_view3d/view3d_navigate_ndof.c index ced8eca710b..1ce9bdcb211 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_ndof.c +++ b/source/blender/editors/space_view3d/view3d_navigate_ndof.c @@ -48,7 +48,7 @@ static float view3d_ndof_pan_speed_calc_ex(RegionView3D *rv3d, const float depth float speed = rv3d->pixsize * NDOF_PIXELS_PER_SECOND; if (rv3d->is_persp) { - speed *= ED_view3d_calc_zfac(rv3d, depth_pt, NULL); + speed *= ED_view3d_calc_zfac(rv3d, depth_pt); } return speed; @@ -347,6 +347,70 @@ void view3d_ndof_fly(const wmNDOFMotionData *ndof, /** \} */ /* -------------------------------------------------------------------- */ +/** \name NDOF Camera View Support + * \{ */ + +/** + * 2D orthographic style NDOF navigation within the camera view. + * Support navigating the camera view instead of leaving the camera-view and navigating in 3D. + */ +static int view3d_ndof_cameraview_pan_zoom(bContext *C, const wmEvent *event) +{ + const wmNDOFMotionData *ndof = event->customdata; + View3D *v3d = CTX_wm_view3d(C); + ARegion *region = CTX_wm_region(C); + RegionView3D *rv3d = region->regiondata; + + ED_view3d_smooth_view_force_finish(C, v3d, region); + + if ((v3d->camera && (rv3d->persp == RV3D_CAMOB) && (v3d->flag2 & V3D_LOCK_CAMERA) == 0)) { + /* pass */ + } + else { + return OPERATOR_PASS_THROUGH; + } + + const bool has_translate = !is_zero_v2(ndof->tvec); + const bool has_zoom = ndof->tvec[2] != 0.0f; + + /* NOTE(@campbellbarton): In principle rotating could pass through to regular + * non-camera NDOF behavior (exiting the camera-view and rotating). + * Disabled this block since in practice it's difficult to control NDOF devices + * to perform some rotation with absolutely no translation. Causing rotation to + * randomly exit from the user perspective. Adjusting the dead-zone could avoid + * the motion feeling *glitchy* although in my own tests even then it didn't work reliably. + * Leave rotating out of camera-view disabled unless it can be made to work reliably. */ + if (!(has_translate || has_zoom)) { + // return OPERATOR_PASS_THROUGH; + } + + bool changed = false; + + if (has_translate) { + const float speed = ndof->dt * NDOF_PIXELS_PER_SECOND; + float event_ofs[2] = {ndof->tvec[0] * speed, ndof->tvec[1] * speed}; + if (ED_view3d_camera_view_pan(region, event_ofs)) { + changed = true; + } + } + + if (has_zoom) { + const float scale = 1.0f + (ndof->dt * ndof->tvec[2]); + if (ED_view3d_camera_view_zoom_scale(rv3d, scale)) { + changed = true; + } + } + + if (changed) { + ED_region_tag_redraw(region); + return OPERATOR_FINISHED; + } + return OPERATOR_CANCELLED; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name NDOF Orbit/Translate Operator * \{ */ @@ -436,6 +500,13 @@ static int ndof_orbit_zoom_invoke(bContext *C, wmOperator *op, const wmEvent *ev return OPERATOR_CANCELLED; } + if (U.ndof_flag & NDOF_CAMERA_PAN_ZOOM) { + const int camera_retval = view3d_ndof_cameraview_pan_zoom(C, event); + if (camera_retval != OPERATOR_PASS_THROUGH) { + return camera_retval; + } + } + const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewOpsData *vod; View3D *v3d; @@ -550,6 +621,13 @@ static int ndof_pan_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *e return OPERATOR_CANCELLED; } + if (U.ndof_flag & NDOF_CAMERA_PAN_ZOOM) { + const int camera_retval = view3d_ndof_cameraview_pan_zoom(C, event); + if (camera_retval != OPERATOR_PASS_THROUGH) { + return camera_retval; + } + } + const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); View3D *v3d = CTX_wm_view3d(C); RegionView3D *rv3d = CTX_wm_region_view3d(C); diff --git a/source/blender/editors/space_view3d/view3d_navigate_roll.c b/source/blender/editors/space_view3d/view3d_navigate_roll.c index 56bd9c93216..9c070fb0341 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_roll.c +++ b/source/blender/editors/space_view3d/view3d_navigate_roll.c @@ -24,8 +24,17 @@ /** \name View Roll Operator * \{ */ -static void view_roll_angle( - ARegion *region, float quat[4], const float orig_quat[4], const float dvec[3], float angle) +/** + * \param use_axis_view: When true, keep axis-aligned orthographic views + * (when rotating in 90 degree increments). While this may seem obscure some NDOF + * devices have key shortcuts to do this (see #NDOF_BUTTON_ROLL_CW & #NDOF_BUTTON_ROLL_CCW). + */ +static void view_roll_angle(ARegion *region, + float quat[4], + const float orig_quat[4], + const float dvec[3], + float angle, + bool use_axis_view) { RegionView3D *rv3d = region->regiondata; float quat_mul[4]; @@ -38,7 +47,16 @@ static void view_roll_angle( /* avoid precision loss over time */ normalize_qt(quat); - rv3d->view = RV3D_VIEW_USER; + if (use_axis_view && RV3D_VIEW_IS_AXIS(rv3d->view) && (fabsf(angle) == (float)M_PI_2)) { + if (ED_view3d_quat_to_axis_view(quat, 0.01f, &rv3d->view, &rv3d->view_axis_roll)) { + if (rv3d->view != RV3D_VIEW_USER) { + ED_view3d_quat_from_axis_view(rv3d->view, rv3d->view_axis_roll, quat_mul); + } + } + } + else { + rv3d->view = RV3D_VIEW_USER; + } } static void viewroll_apply(ViewOpsData *vod, int x, int y) @@ -46,7 +64,8 @@ static void viewroll_apply(ViewOpsData *vod, int x, int y) float angle = BLI_dial_angle(vod->init.dial, (const float[2]){x, y}); if (angle != 0.0f) { - view_roll_angle(vod->region, vod->rv3d->viewquat, vod->init.quat, vod->init.mousevec, angle); + view_roll_angle( + vod->region, vod->rv3d->viewquat, vod->init.quat, vod->init.mousevec, angle, false); } if (vod->use_dyn_ofs) { @@ -169,7 +188,7 @@ static int viewroll_exec(bContext *C, wmOperator *op) normalize_v3_v3(mousevec, rv3d->viewinv[2]); negate_v3(mousevec); - view_roll_angle(region, quat_new, rv3d->viewquat, mousevec, angle); + view_roll_angle(region, quat_new, rv3d->viewquat, mousevec, angle, true); const float *dyn_ofs_pt = NULL; float dyn_ofs[3]; diff --git a/source/blender/editors/space_view3d/view3d_navigate_rotate.c b/source/blender/editors/space_view3d/view3d_navigate_rotate.c index 774a8983c67..11de5463cdb 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_rotate.c +++ b/source/blender/editors/space_view3d/view3d_navigate_rotate.c @@ -383,7 +383,7 @@ static int viewrotate_invoke(bContext *C, wmOperator *op, const wmEvent *event) int event_xy[2]; if (event->type == MOUSEPAN) { - if (event->is_direction_inverted) { + if (event->flag & WM_EVENT_SCROLL_INVERT) { event_xy[0] = 2 * event->xy[0] - event->prev_xy[0]; event_xy[1] = 2 * event->xy[1] - event->prev_xy[1]; } diff --git a/source/blender/editors/space_view3d/view3d_navigate_zoom.c b/source/blender/editors/space_view3d/view3d_navigate_zoom.c index 8eb8fffaa31..5f6f9fde324 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_zoom.c +++ b/source/blender/editors/space_view3d/view3d_navigate_zoom.c @@ -125,18 +125,18 @@ static void view_zoom_to_window_xy_3d(ARegion *region, float dfac, const int zoo float dvec[3]; float tvec[3]; float tpos[3]; - float mval_f[2]; + float xy_delta[2]; float zfac; negate_v3_v3(tpos, rv3d->ofs); - mval_f[0] = (float)(((zoom_xy[0] - region->winrct.xmin) * 2) - region->winx) / 2.0f; - mval_f[1] = (float)(((zoom_xy[1] - region->winrct.ymin) * 2) - region->winy) / 2.0f; + xy_delta[0] = (float)(((zoom_xy[0] - region->winrct.xmin) * 2) - region->winx) / 2.0f; + xy_delta[1] = (float)(((zoom_xy[1] - region->winrct.ymin) * 2) - region->winy) / 2.0f; /* Project cursor position into 3D space */ - zfac = ED_view3d_calc_zfac(rv3d, tpos, NULL); - ED_view3d_win_to_delta(region, mval_f, dvec, zfac); + zfac = ED_view3d_calc_zfac(rv3d, tpos); + ED_view3d_win_to_delta(region, xy_delta, zfac, dvec); /* Calculate view target position for dolly */ add_v3_v3v3(tvec, tpos, dvec); diff --git a/source/blender/editors/space_view3d/view3d_navigate_zoom_border.c b/source/blender/editors/space_view3d/view3d_navigate_zoom_border.c index 4e909151ce4..f834efe4a7b 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_zoom_border.c +++ b/source/blender/editors/space_view3d/view3d_navigate_zoom_border.c @@ -125,7 +125,7 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op) negate_v3_v3(new_ofs, p); } else { - float mval_f[2]; + float xy_delta[2]; float zfac; /* We can't use the depth, fallback to the old way that doesn't set the center depth */ @@ -134,12 +134,12 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op) { float tvec[3]; negate_v3_v3(tvec, new_ofs); - zfac = ED_view3d_calc_zfac(rv3d, tvec, NULL); + zfac = ED_view3d_calc_zfac(rv3d, tvec); } - mval_f[0] = (rect.xmin + rect.xmax - vb[0]) / 2.0f; - mval_f[1] = (rect.ymin + rect.ymax - vb[1]) / 2.0f; - ED_view3d_win_to_delta(region, mval_f, dvec, zfac); + xy_delta[0] = (rect.xmin + rect.xmax - vb[0]) / 2.0f; + xy_delta[1] = (rect.ymin + rect.ymax - vb[1]) / 2.0f; + ED_view3d_win_to_delta(region, xy_delta, zfac, dvec); /* center the view to the center of the rectangle */ sub_v3_v3(new_ofs, dvec); } diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c index 06b848571d8..98fb914cda9 100644 --- a/source/blender/editors/space_view3d/view3d_placement.c +++ b/source/blender/editors/space_view3d/view3d_placement.c @@ -727,6 +727,17 @@ static void view3d_interactive_add_begin(bContext *C, wmOperator *op, const wmEv V3DSnapCursorState *snap_state_new = ED_view3d_cursor_snap_active(); if (snap_state_new) { ipd->snap_state = snap_state = snap_state_new; + + /* For drag events, update the location since it will be set from the drag-start. + * This is needed as cursor-drawing doesn't deal with drag events and will use + * the current cursor location instead of the drag-start. */ + if (event->val == KM_CLICK_DRAG) { + /* Set this flag so snapping always updated. */ + int flag_orig = snap_state_new->flag; + snap_state_new->flag |= V3D_SNAPCURSOR_TOGGLE_ALWAYS_TRUE; + ED_view3d_cursor_snap_data_get(snap_state_new, C, event->mval[0], event->mval[1]); + snap_state_new->flag = flag_orig; + } } snap_state->draw_point = true; diff --git a/source/blender/editors/space_view3d/view3d_project.c b/source/blender/editors/space_view3d/view3d_project.c index 2cf9ac0a52e..85d239507ce 100644 --- a/source/blender/editors/space_view3d/view3d_project.c +++ b/source/blender/editors/space_view3d/view3d_project.c @@ -276,7 +276,7 @@ float ED_view3d_pixel_size_no_ui_scale(const RegionView3D *rv3d, const float co[ return mul_project_m4_v3_zfac(rv3d->persmat, co) * rv3d->pixsize; } -float ED_view3d_calc_zfac(const RegionView3D *rv3d, const float co[3], bool *r_flip) +float ED_view3d_calc_zfac_ex(const RegionView3D *rv3d, const float co[3], bool *r_flip) { float zfac = mul_project_m4_v3_zfac(rv3d->persmat, co); @@ -299,10 +299,15 @@ float ED_view3d_calc_zfac(const RegionView3D *rv3d, const float co[3], bool *r_f return zfac; } +float ED_view3d_calc_zfac(const RegionView3D *rv3d, const float co[3]) +{ + return ED_view3d_calc_zfac_ex(rv3d, co, NULL); +} + float ED_view3d_calc_depth_for_comparison(const RegionView3D *rv3d, const float co[3]) { if (rv3d->is_persp) { - return ED_view3d_calc_zfac(rv3d, co, NULL); + return ED_view3d_calc_zfac(rv3d, co); } return -dot_v3v3(rv3d->viewinv[2], co); } @@ -436,8 +441,8 @@ bool view3d_get_view_aligned_coordinate(ARegion *region, if (ret == V3D_PROJ_RET_OK) { const float mval_f[2] = {(float)(mval_cpy[0] - mval[0]), (float)(mval_cpy[1] - mval[1])}; - const float zfac = ED_view3d_calc_zfac(rv3d, fp, NULL); - ED_view3d_win_to_delta(region, mval_f, dvec, zfac); + const float zfac = ED_view3d_calc_zfac(rv3d, fp); + ED_view3d_win_to_delta(region, mval_f, zfac, dvec); sub_v3_v3(fp, dvec); return true; @@ -584,57 +589,57 @@ bool ED_view3d_win_to_3d_on_plane_with_fallback(const ARegion *region, } void ED_view3d_win_to_delta(const ARegion *region, - const float mval[2], - float out[3], - const float zfac) + const float xy_delta[2], + const float zfac, + float r_out[3]) { RegionView3D *rv3d = region->regiondata; float dx, dy; - dx = 2.0f * mval[0] * zfac / region->winx; - dy = 2.0f * mval[1] * zfac / region->winy; + dx = 2.0f * xy_delta[0] * zfac / region->winx; + dy = 2.0f * xy_delta[1] * zfac / region->winy; - out[0] = (rv3d->persinv[0][0] * dx + rv3d->persinv[1][0] * dy); - out[1] = (rv3d->persinv[0][1] * dx + rv3d->persinv[1][1] * dy); - out[2] = (rv3d->persinv[0][2] * dx + rv3d->persinv[1][2] * dy); + r_out[0] = (rv3d->persinv[0][0] * dx + rv3d->persinv[1][0] * dy); + r_out[1] = (rv3d->persinv[0][1] * dx + rv3d->persinv[1][1] * dy); + r_out[2] = (rv3d->persinv[0][2] * dx + rv3d->persinv[1][2] * dy); } -void ED_view3d_win_to_origin(const ARegion *region, const float mval[2], float out[3]) +void ED_view3d_win_to_origin(const ARegion *region, const float mval[2], float r_out[3]) { RegionView3D *rv3d = region->regiondata; if (rv3d->is_persp) { - copy_v3_v3(out, rv3d->viewinv[3]); + copy_v3_v3(r_out, rv3d->viewinv[3]); } else { - out[0] = 2.0f * mval[0] / region->winx - 1.0f; - out[1] = 2.0f * mval[1] / region->winy - 1.0f; + r_out[0] = 2.0f * mval[0] / region->winx - 1.0f; + r_out[1] = 2.0f * mval[1] / region->winy - 1.0f; if (rv3d->persp == RV3D_CAMOB) { - out[2] = -1.0f; + r_out[2] = -1.0f; } else { - out[2] = 0.0f; + r_out[2] = 0.0f; } - mul_project_m4_v3(rv3d->persinv, out); + mul_project_m4_v3(rv3d->persinv, r_out); } } -void ED_view3d_win_to_vector(const ARegion *region, const float mval[2], float out[3]) +void ED_view3d_win_to_vector(const ARegion *region, const float mval[2], float r_out[3]) { RegionView3D *rv3d = region->regiondata; if (rv3d->is_persp) { - out[0] = 2.0f * (mval[0] / region->winx) - 1.0f; - out[1] = 2.0f * (mval[1] / region->winy) - 1.0f; - out[2] = -0.5f; - mul_project_m4_v3(rv3d->persinv, out); - sub_v3_v3(out, rv3d->viewinv[3]); + r_out[0] = 2.0f * (mval[0] / region->winx) - 1.0f; + r_out[1] = 2.0f * (mval[1] / region->winy) - 1.0f; + r_out[2] = -0.5f; + mul_project_m4_v3(rv3d->persinv, r_out); + sub_v3_v3(r_out, rv3d->viewinv[3]); } else { - negate_v3_v3(out, rv3d->viewinv[2]); + negate_v3_v3(r_out, rv3d->viewinv[2]); } - normalize_v3(out); + normalize_v3(r_out); } bool ED_view3d_win_to_segment_clipped(struct Depsgraph *depsgraph, diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index f08c53fff47..e380a08dcc7 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -1313,7 +1313,7 @@ static bool view3d_lasso_select(bContext *C, case OB_MESH: changed = do_lasso_select_mesh(vc, wm_userdata, mcoords, mcoords_len, sel_op); break; - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: changed = do_lasso_select_curve(vc, mcoords, mcoords_len, sel_op); break; @@ -2695,7 +2695,7 @@ static int view3d_select_exec(bContext *C, wmOperator *op) retval = ED_lattice_deselect_all_multi(C); } } - else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { + else if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) { retval = ED_curve_editnurb_select_pick(C, location, extend, deselect, toggle); if (!retval && deselect_all) { retval = ED_curve_deselect_all_multi(C); @@ -3586,7 +3586,7 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op) WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data); } break; - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: changed = do_nurbs_box_select(&vc, &rect, sel_op); if (changed) { @@ -4342,7 +4342,7 @@ static bool obedit_circle_select(bContext *C, case OB_MESH: changed = mesh_circle_select(vc, wm_userdata, sel_op, mval, rad); break; - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: changed = nurbscurve_circle_select(vc, sel_op, mval, rad); break; diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c index 8a219cd96d1..3e788f2d643 100644 --- a/source/blender/editors/space_view3d/view3d_utils.c +++ b/source/blender/editors/space_view3d/view3d_utils.c @@ -510,6 +510,39 @@ bool ED_view3d_persp_ensure(const Depsgraph *depsgraph, View3D *v3d, ARegion *re /** \} */ /* -------------------------------------------------------------------- */ +/** \name Camera View Utilities + * + * Utilities for manipulating the camera-view. + * \{ */ + +bool ED_view3d_camera_view_zoom_scale(RegionView3D *rv3d, const float scale) +{ + const float camzoom_init = rv3d->camzoom; + float zoomfac = BKE_screen_view3d_zoom_to_fac(rv3d->camzoom); + /* Clamp both before and after conversion to prevent NAN on negative values. */ + + zoomfac = zoomfac * scale; + CLAMP(zoomfac, RV3D_CAMZOOM_MIN_FACTOR, RV3D_CAMZOOM_MAX_FACTOR); + rv3d->camzoom = BKE_screen_view3d_zoom_from_fac(zoomfac); + CLAMP(rv3d->camzoom, RV3D_CAMZOOM_MIN, RV3D_CAMZOOM_MAX); + return (rv3d->camzoom != camzoom_init); +} + +bool ED_view3d_camera_view_pan(ARegion *region, const float event_ofs[2]) +{ + RegionView3D *rv3d = region->regiondata; + const float camdxy_init[2] = {rv3d->camdx, rv3d->camdy}; + const float zoomfac = BKE_screen_view3d_zoom_to_fac(rv3d->camzoom) * 2.0f; + rv3d->camdx += event_ofs[0] / (region->winx * zoomfac); + rv3d->camdy += event_ofs[1] / (region->winy * zoomfac); + CLAMP(rv3d->camdx, -1.0f, 1.0f); + CLAMP(rv3d->camdy, -1.0f, 1.0f); + return (camdxy_init[0] != rv3d->camdx) || (camdxy_init[1] != rv3d->camdy); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Camera Lock API * * Lock the camera to the 3D Viewport, allowing view manipulation to transform the camera. diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 3bcc1b2968e..fd01f708ed2 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -177,8 +177,8 @@ void convertViewVec(TransInfo *t, float r_vec[3], double dx, double dy) r_vec[1] = dy; } else { - const float mval_f[2] = {(float)dx, (float)dy}; - ED_view3d_win_to_delta(t->region, mval_f, r_vec, t->zfac); + const float xy_delta[2] = {(float)dx, (float)dy}; + ED_view3d_win_to_delta(t->region, xy_delta, t->zfac, r_vec); } } else if (t->spacetype == SPACE_IMAGE) { @@ -1150,10 +1150,10 @@ int transformEvent(TransInfo *t, const wmEvent *event) else if (event->val == KM_PRESS) { switch (event->type) { case EVT_CKEY: - if (event->is_repeat) { + if (event->flag & WM_EVENT_IS_REPEAT) { break; } - if (event->alt) { + if (event->modifier & KM_ALT) { if (!(t->options & CTX_NO_PET)) { t->flag ^= T_PROP_CONNECTED; sort_trans_data_dist(t); @@ -1164,10 +1164,10 @@ int transformEvent(TransInfo *t, const wmEvent *event) } break; case EVT_OKEY: - if (event->is_repeat) { + if (event->flag & WM_EVENT_IS_REPEAT) { break; } - if (t->flag & T_PROP_EDIT && event->shift) { + if ((t->flag & T_PROP_EDIT) && (event->modifier & KM_SHIFT)) { t->prop_mode = (t->prop_mode + 1) % PROP_MODE_MAX; calculatePropRatio(t); t->redraw |= TREDRAW_HARD; @@ -1175,7 +1175,7 @@ int transformEvent(TransInfo *t, const wmEvent *event) } break; case EVT_PADPLUSKEY: - if (event->alt && t->flag & T_PROP_EDIT) { + if ((event->modifier & KM_ALT) && (t->flag & T_PROP_EDIT)) { t->prop_size *= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f; if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO) { t->prop_size = min_ff(t->prop_size, ((View3D *)t->view)->clip_end); @@ -1186,7 +1186,7 @@ int transformEvent(TransInfo *t, const wmEvent *event) } break; case EVT_PADMINUS: - if (event->alt && t->flag & T_PROP_EDIT) { + if ((event->modifier & KM_ALT) && (t->flag & T_PROP_EDIT)) { t->prop_size /= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f; calculatePropRatio(t); t->redraw = TREDRAW_HARD; @@ -1202,7 +1202,7 @@ int transformEvent(TransInfo *t, const wmEvent *event) } break; case EVT_NKEY: - if (event->is_repeat) { + if (event->flag & WM_EVENT_IS_REPEAT) { break; } if (ELEM(t->mode, TFM_ROTATION)) { @@ -1697,7 +1697,7 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve /* Needed to translate tweak events to mouse buttons. */ t->launch_event = event ? WM_userdef_event_type_from_keymap_type(event->type) : -1; - t->is_launch_event_tweak = event ? ISTWEAK(event->type) : false; + t->is_launch_event_drag = event ? (event->val == KM_CLICK_DRAG) : false; /* XXX Remove this when wm_operator_call_internal doesn't use window->eventstate * (which can have type = 0) */ @@ -1780,10 +1780,12 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve } if (kmi->propvalue == TFM_MODAL_SNAP_INV_ON && kmi->val == KM_PRESS) { - if ((ELEM(kmi->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY) && event->ctrl) || - (ELEM(kmi->type, EVT_LEFTSHIFTKEY, EVT_RIGHTSHIFTKEY) && event->shift) || - (ELEM(kmi->type, EVT_LEFTALTKEY, EVT_RIGHTALTKEY) && event->alt) || - ((kmi->type == EVT_OSKEY) && event->oskey)) { + if ((ELEM(kmi->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY) && + (event->modifier & KM_CTRL)) || + (ELEM(kmi->type, EVT_LEFTSHIFTKEY, EVT_RIGHTSHIFTKEY) && + (event->modifier & KM_SHIFT)) || + (ELEM(kmi->type, EVT_LEFTALTKEY, EVT_RIGHTALTKEY) && (event->modifier & KM_ALT)) || + ((kmi->type == EVT_OSKEY) && (event->modifier & KM_OSKEY))) { t->modifiers |= MOD_SNAP_INVERT; } break; @@ -1967,7 +1969,7 @@ bool checkUseAxisMatrix(TransInfo *t) /* currently only checks for editmode */ if (t->flag & T_EDIT) { if ((t->around == V3D_AROUND_LOCAL_ORIGINS) && - (ELEM(t->obedit_type, OB_MESH, OB_CURVE, OB_MBALL, OB_ARMATURE))) { + (ELEM(t->obedit_type, OB_MESH, OB_CURVES_LEGACY, OB_MBALL, OB_ARMATURE))) { /* not all editmode supports axis-matrix */ return true; } diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 757c11f1179..3ee5868d5be 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -592,9 +592,11 @@ typedef struct TransInfo { /*************** NEW STUFF *********************/ /** event type used to launch transform. */ short launch_event; - /** Is the actual launch event a tweak event? (launch_event above is set to the corresponding - * mouse button then.) */ - bool is_launch_event_tweak; + /** + * Is the actual launch event a drag event? + * (`launch_event` is set to the corresponding mouse button then.) + */ + bool is_launch_event_drag; bool is_orient_default_overwrite; diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c index 64ef170a13f..81b35e4539b 100644 --- a/source/blender/editors/transform/transform_constraints.c +++ b/source/blender/editors/transform/transform_constraints.c @@ -1041,8 +1041,7 @@ static void setNearestAxis3d(TransInfo *t) * and to overflow the short integers. * The formula used is a bit stupid, just a simplification of the subtraction * of two 2D points 30 pixels apart (that's the last factor in the formula) after - * projecting them with ED_view3d_win_to_delta and then get the length of that vector. - */ + * projecting them with #ED_view3d_win_to_delta and then get the length of that vector. */ zfac = mul_project_m4_v3_zfac(t->persmat, t->center_global); zfac = len_v3(t->persinv[0]) * 2.0f / t->region->winx * zfac * 30.0f; diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index 95b810daeaf..4a2169b381e 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -1040,7 +1040,7 @@ static void init_proportional_edit(TransInfo *t) /* Already calculated by uv_set_connectivity_distance. */ } else if (convert_type == TC_CURVE_VERTS) { - BLI_assert(t->obedit_type == OB_CURVE); + BLI_assert(t->obedit_type == OB_CURVES_LEGACY); set_prop_dist(t, false); } else { @@ -1049,7 +1049,7 @@ static void init_proportional_edit(TransInfo *t) sort_trans_data_dist(t); } - else if (ELEM(t->obedit_type, OB_CURVE)) { + else if (ELEM(t->obedit_type, OB_CURVES_LEGACY)) { /* Needed because bezier handles can be partially selected * and are still added into transform data. */ sort_trans_data_selected_first(t); @@ -1286,7 +1286,7 @@ static eTConvertType convert_type_get(const TransInfo *t, Object **r_obj_armatur convert_type = TC_MESH_VERTS; } } - else if (ELEM(t->obedit_type, OB_CURVE, OB_SURF)) { + else if (ELEM(t->obedit_type, OB_CURVES_LEGACY, OB_SURF)) { convert_type = TC_CURVE_VERTS; } else if (t->obedit_type == OB_LATTICE) { diff --git a/source/blender/editors/transform/transform_convert_graph.c b/source/blender/editors/transform/transform_convert_graph.c index 608fd59c8b7..54222fbb117 100644 --- a/source/blender/editors/transform/transform_convert_graph.c +++ b/source/blender/editors/transform/transform_convert_graph.c @@ -161,7 +161,7 @@ static void graph_bezt_get_transform_selection(const TransInfo *t, bool left = use_handle ? ((bezt->f1 & SELECT) != 0) : key; bool right = use_handle ? ((bezt->f3 & SELECT) != 0) : key; - if (use_handle && t->is_launch_event_tweak) { + if (use_handle && t->is_launch_event_drag) { if (sipo->runtime.flag & SIPO_RUNTIME_FLAG_TWEAK_HANDLES_LEFT) { key = right = false; } diff --git a/source/blender/editors/transform/transform_convert_object_texspace.c b/source/blender/editors/transform/transform_convert_object_texspace.c index e12d0db8758..763af1f3384 100644 --- a/source/blender/editors/transform/transform_convert_object_texspace.c +++ b/source/blender/editors/transform/transform_convert_object_texspace.c @@ -44,7 +44,7 @@ void createTransTexspace(TransInfo *t) } id = ob->data; - if (id == NULL || !ELEM(GS(id->name), ID_ME, ID_CU, ID_MB)) { + if (id == NULL || !ELEM(GS(id->name), ID_ME, ID_CU_LEGACY, ID_MB)) { BKE_report(t->reports, RPT_ERROR, "Unsupported object type for text-space transform"); return; } diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index 1d86a8f9413..8987325145c 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -731,7 +731,8 @@ void postTrans(bContext *C, TransInfo *t) if (t->data_len_all != 0) { FOREACH_TRANS_DATA_CONTAINER (t, tc) { /* free data malloced per trans-data */ - if (ELEM(t->obedit_type, OB_CURVE, OB_SURF, OB_GPENCIL) || (t->spacetype == SPACE_GRAPH)) { + if (ELEM(t->obedit_type, OB_CURVES_LEGACY, OB_SURF, OB_GPENCIL) || + (t->spacetype == SPACE_GRAPH)) { TransData *td = tc->data; for (int a = 0; a < tc->data_len; a++, td++) { if (td->flag & TD_BEZTRIPLE) { @@ -1145,7 +1146,7 @@ void calculateCenter(TransInfo *t) projectFloatView(t, axis, t->center2d); - /* rotate only needs correct 2d center, grab needs ED_view3d_calc_zfac() value */ + /* Rotate only needs correct 2d center, grab needs #ED_view3d_calc_zfac() value. */ if (t->mode == TFM_TRANSLATION) { copy_v3_v3(t->center_global, axis); } @@ -1154,17 +1155,16 @@ void calculateCenter(TransInfo *t) } if (t->spacetype == SPACE_VIEW3D) { - /* ED_view3d_calc_zfac() defines a factor for perspective depth correction, - * used in ED_view3d_win_to_delta() */ + /* #ED_view3d_calc_zfac() defines a factor for perspective depth correction, + * used in #ED_view3d_win_to_delta(). */ - /* zfac is only used convertViewVec only in cases operator was invoked in RGN_TYPE_WINDOW - * and never used in other cases. + /* NOTE: `t->zfac` is only used #convertViewVec only in cases operator was invoked in + * #RGN_TYPE_WINDOW and never used in other cases. * - * We need special case here as well, since ED_view3d_calc_zfac will crash when called - * for a region different from RGN_TYPE_WINDOW. - */ + * We need special case here as well, since #ED_view3d_calc_zfac will crash when called + * for a region different from #RGN_TYPE_WINDOW. */ if (t->region->regiontype == RGN_TYPE_WINDOW) { - t->zfac = ED_view3d_calc_zfac(t->region->regiondata, t->center_global, NULL); + t->zfac = ED_view3d_calc_zfac(t->region->regiondata, t->center_global); } else { t->zfac = 0.0f; diff --git a/source/blender/editors/transform/transform_gizmo_2d.c b/source/blender/editors/transform/transform_gizmo_2d.c index d2b2d2f116e..da601328192 100644 --- a/source/blender/editors/transform/transform_gizmo_2d.c +++ b/source/blender/editors/transform/transform_gizmo_2d.c @@ -669,6 +669,7 @@ static void gizmo2d_xform_invoke_prepare(const bContext *C, float c[3] = {mid[0], mid[1], 0.0f}; float orient_matrix[3][3]; + unit_m3(orient_matrix); ScrArea *area = CTX_wm_area(C); diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c index 7e121a717aa..f07fadb7fc1 100644 --- a/source/blender/editors/transform/transform_gizmo_3d.c +++ b/source/blender/editors/transform/transform_gizmo_3d.c @@ -816,7 +816,7 @@ int ED_transform_calc_gizmo_stats(const bContext *C, } FOREACH_EDIT_OBJECT_END(); } - else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { + else if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) { FOREACH_EDIT_OBJECT_BEGIN (ob_iter, use_mat_local) { Curve *cu = ob_iter->data; Nurb *nu; @@ -1869,7 +1869,7 @@ static void WIDGETGROUP_gizmo_invoke_prepare(const bContext *C, if (axis != -1) { wmWindow *win = CTX_wm_window(C); /* Swap single axis for two-axis constraint. */ - bool flip = win->eventstate->shift; + bool flip = (win->eventstate->modifier & KM_SHIFT) != 0; BLI_assert(axis_idx != -1); const short axis_type = gizmo_get_axis_type(axis_idx); if (axis_type != MAN_AXES_ROTATE) { diff --git a/source/blender/editors/transform/transform_gizmo_extrude_3d.c b/source/blender/editors/transform/transform_gizmo_extrude_3d.c index 6873ea862ce..f6f43f867ae 100644 --- a/source/blender/editors/transform/transform_gizmo_extrude_3d.c +++ b/source/blender/editors/transform/transform_gizmo_extrude_3d.c @@ -153,7 +153,7 @@ static void gizmo_mesh_extrude_setup(const bContext *C, wmGizmoGroup *gzgroup) op_idname = "ARMATURE_OT_extrude_move"; ggd->normal_axis = 1; } - else if (obact->type == OB_CURVE) { + else if (obact->type == OB_CURVES_LEGACY) { op_idname = "CURVE_OT_extrude_move"; ggd->normal_axis = 2; } diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c index b6c4002b1c7..6162dfc9bb5 100644 --- a/source/blender/editors/transform/transform_mode.c +++ b/source/blender/editors/transform/transform_mode.c @@ -57,7 +57,7 @@ bool transdata_check_local_center(const TransInfo *t, short around) return ((around == V3D_AROUND_LOCAL_ORIGINS) && ((t->options & (CTX_OBJECT | CTX_POSE_BONE)) || /* implicit: (t->flag & T_EDIT) */ - (ELEM(t->obedit_type, OB_MESH, OB_CURVE, OB_MBALL, OB_ARMATURE, OB_GPENCIL)) || + (ELEM(t->obedit_type, OB_MESH, OB_CURVES_LEGACY, OB_MBALL, OB_ARMATURE, OB_GPENCIL)) || (t->spacetype == SPACE_GRAPH) || (t->options & (CTX_MOVIECLIP | CTX_MASK | CTX_PAINT_CURVE | CTX_SEQUENCER_IMAGE)))); } diff --git a/source/blender/editors/transform/transform_mode_vert_slide.c b/source/blender/editors/transform/transform_mode_vert_slide.c index 0afc527d4a8..77c5707d814 100644 --- a/source/blender/editors/transform/transform_mode_vert_slide.c +++ b/source/blender/editors/transform/transform_mode_vert_slide.c @@ -146,9 +146,9 @@ static void calcVertSlideMouseActiveEdges(struct TransInfo *t, const int mval[2] * by finding the closest edge in local-space. * However this skews the outcome with non-uniform-scale. */ - /* first get the direction of the original mouse position */ + /* First get the direction of the original mouse position. */ sub_v2_v2v2(dir, imval_fl, mval_fl); - ED_view3d_win_to_delta(t->region, dir, dir, t->zfac); + ED_view3d_win_to_delta(t->region, dir, t->zfac, dir); normalize_v3(dir); for (i = 0, sv = sld->sv; i < sld->totsv; i++, sv++) { @@ -425,18 +425,18 @@ void drawVertSlide(TransInfo *t) /* direction from active vertex! */ if ((t->mval[0] != t->mouse.imval[0]) || (t->mval[1] != t->mouse.imval[1])) { float zfac; - float mval_ofs[2]; + float xy_delta[2]; float co_orig_3d[3]; float co_dest_3d[3]; - mval_ofs[0] = t->mval[0] - t->mouse.imval[0]; - mval_ofs[1] = t->mval[1] - t->mouse.imval[1]; + xy_delta[0] = t->mval[0] - t->mouse.imval[0]; + xy_delta[1] = t->mval[1] - t->mouse.imval[1]; mul_v3_m4v3( co_orig_3d, TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->obmat, curr_sv->co_orig_3d); - zfac = ED_view3d_calc_zfac(t->region->regiondata, co_orig_3d, NULL); + zfac = ED_view3d_calc_zfac(t->region->regiondata, co_orig_3d); - ED_view3d_win_to_delta(t->region, mval_ofs, co_dest_3d, zfac); + ED_view3d_win_to_delta(t->region, xy_delta, zfac, co_dest_3d); invert_m4_m4(TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->imat, TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->obmat); diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c index 3722e83e451..936aca7d2e0 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -628,7 +628,7 @@ void Transform_Properties(struct wmOperatorType *ot, int flags) "Proportional Falloff", "Falloff type for proportional editing mode"); /* Abusing id_curve :/ */ - RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); + RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE_LEGACY); RNA_def_float(ot->srna, "proportional_size", 1, diff --git a/source/blender/editors/transform/transform_orientations.c b/source/blender/editors/transform/transform_orientations.c index 6ca61d415ea..c0d943e17ee 100644 --- a/source/blender/editors/transform/transform_orientations.c +++ b/source/blender/editors/transform/transform_orientations.c @@ -350,7 +350,7 @@ bool BIF_createTransformOrientation(bContext *C, else if (obedit->type == OB_ARMATURE) { ts = createBoneSpace(C, reports, name, overwrite); } - else if (obedit->type == OB_CURVE) { + else if (obedit->type == OB_CURVES_LEGACY) { ts = createCurveSpace(C, reports, name, overwrite); } } @@ -984,7 +984,7 @@ int getTransformOrientation_ex(ViewLayer *view_layer, negate_v3(plane); } /* end editmesh */ - else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { + else if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) { Curve *cu = obedit->data; Nurb *nu = NULL; int a; diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index ce0cf3aa6cb..2f3c021ba38 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -86,7 +86,7 @@ int BIF_snappingSupported(Object *obedit) int status = 0; /* only support object mesh, armature, curves */ - if (obedit == NULL || ELEM(obedit->type, OB_MESH, OB_ARMATURE, OB_CURVE, OB_LATTICE, OB_MBALL)) { + if (obedit == NULL || ELEM(obedit->type, OB_MESH, OB_ARMATURE, OB_CURVES_LEGACY, OB_LATTICE, OB_MBALL)) { status = 1; } @@ -308,7 +308,8 @@ eRedrawFlag handleSnapping(TransInfo *t, const wmEvent *event) eRedrawFlag status = TREDRAW_NOTHING; #if 0 /* XXX need a proper selector for all snap mode */ - if (BIF_snappingSupported(t->obedit) && event->type == TABKEY && event->shift) { + if (BIF_snappingSupported(t->obedit) && (event->type == EVT_TABKEY) && + (event->modifier & KM_SHIFT)) { /* toggle snap and reinit */ t->settings->snap_flag ^= SCE_SNAP; initSnapping(t, NULL); @@ -583,7 +584,7 @@ static short snap_mode_from_scene(TransInfo *t) /* All obedit types will match. */ const int obedit_type = t->obedit_type; if ((t->options & (CTX_GPENCIL_STROKES | CTX_CURSOR | CTX_OBMODE_XFORM_OBDATA)) || - ELEM(obedit_type, OB_MESH, OB_ARMATURE, OB_CURVE, OB_LATTICE, OB_MBALL, -1)) { + ELEM(obedit_type, OB_MESH, OB_ARMATURE, OB_CURVES_LEGACY, OB_LATTICE, OB_MBALL, -1)) { r_snap_mode = ts->snap_mode; if ((r_snap_mode & SCE_SNAP_MODE_INCREMENT) && (ts->snap_flag & SCE_SNAP_ABS_GRID) && (t->mode == TFM_TRANSLATION)) { @@ -617,7 +618,7 @@ static short snap_select_type_get(TransInfo *t) * When we're moving the origins, allow snapping onto our own geometry (see T69132). */ } else if ((obedit_type != -1) && - ELEM(obedit_type, OB_MESH, OB_ARMATURE, OB_CURVE, OB_LATTICE, OB_MBALL)) { + ELEM(obedit_type, OB_MESH, OB_ARMATURE, OB_CURVES_LEGACY, OB_LATTICE, OB_MBALL)) { /* Edit mode */ /* Temporary limited to edit mode meshes, armature, curves, metaballs. */ diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c index ca6940040b2..8b7133892ff 100644 --- a/source/blender/editors/transform/transform_snap_object.c +++ b/source/blender/editors/transform/transform_snap_object.c @@ -477,7 +477,7 @@ static void iter_snap_objects(SnapObjectContext *sctx, } } else if (snap_select == SNAP_NOT_SELECTED) { - if (is_object_active && !(base->object->mode & OB_MODE_OBJECT)) { + if (is_object_active && base->object->mode != OB_MODE_OBJECT) { /* Pass. Consider the selection of elements being edited. */ } else if ((base->flag & BASE_SELECTED) || (base->flag_legacy & BA_WAS_SEL)) { @@ -1054,7 +1054,7 @@ static void raycast_obj_fn(SnapObjectContext *sctx, dt->r_hit_list); break; } - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: case OB_FONT: { if (!is_object_active) { @@ -2743,7 +2743,7 @@ static void snap_obj_fn(SnapObjectContext *sctx, dt->r_no, dt->r_index); break; - case OB_CURVE: + case OB_CURVES_LEGACY: retval = snapCurve( sctx, params, ob_eval, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index); break; /* Use ATTR_FALLTHROUGH if we want to snap to the generated mesh. */ @@ -3117,7 +3117,7 @@ static short transform_snap_context_project_view3d_mixed_impl( sctx->runtime.has_occlusion_plane = false; /* By convention we only snap to the original elements of a curve. */ - if (has_hit && ob_eval->type != OB_CURVE) { + if (has_hit && ob_eval->type != OB_CURVES_LEGACY) { /* Compute the new clip_pane but do not add it yet. */ float new_clipplane[4]; BLI_ASSERT_UNIT_V3(no); diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt index 2300e664dfa..7b4551fdb0c 100644 --- a/source/blender/editors/util/CMakeLists.txt +++ b/source/blender/editors/util/CMakeLists.txt @@ -40,6 +40,7 @@ set(SRC ../include/ED_clip.h ../include/ED_curve.h ../include/ED_curves.h + ../include/ED_curves_sculpt.h ../include/ED_datafiles.h ../include/ED_file_indexer.h ../include/ED_fileselect.h diff --git a/source/blender/editors/util/ed_transverts.c b/source/blender/editors/util/ed_transverts.c index 0523a58825e..c1e093d5555 100644 --- a/source/blender/editors/util/ed_transverts.c +++ b/source/blender/editors/util/ed_transverts.c @@ -45,7 +45,7 @@ void ED_transverts_update_obedit(TransVertStore *tvs, Object *obedit) BMEditMesh *em = BKE_editmesh_from_object(obedit); BM_mesh_normals_update(em->bm); } - else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { + else if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) { Curve *cu = obedit->data; ListBase *nurbs = BKE_curve_editNurbs_get(cu); Nurb *nu = nurbs->first; @@ -181,7 +181,8 @@ static void set_mapped_co(void *vuserdata, int index, const float co[3], const f bool ED_transverts_check_obedit(const Object *obedit) { - return (ELEM(obedit->type, OB_ARMATURE, OB_LATTICE, OB_MESH, OB_SURF, OB_CURVE, OB_MBALL)); + return ( + ELEM(obedit->type, OB_ARMATURE, OB_LATTICE, OB_MESH, OB_SURF, OB_CURVES_LEGACY, OB_MBALL)); } void ED_transverts_create_from_obedit(TransVertStore *tvs, const Object *obedit, const int mode) @@ -351,7 +352,7 @@ void ED_transverts_create_from_obedit(TransVertStore *tvs, const Object *obedit, } } } - else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { + else if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) { Curve *cu = obedit->data; int totmalloc = 0; ListBase *nurbs = BKE_curve_editNurbs_get(cu); diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c index 15fe944be49..32d405df841 100644 --- a/source/blender/editors/util/ed_util.c +++ b/source/blender/editors/util/ed_util.c @@ -306,7 +306,7 @@ bool ED_editors_flush_edits(Main *bmain) /* ***** XXX: functions are using old blender names, cleanup later ***** */ void apply_keyb_grid( - int shift, int ctrl, float *val, float fac1, float fac2, float fac3, int invert) + bool shift, bool ctrl, float *val, float fac1, float fac2, float fac3, int invert) { /* fac1 is for 'nothing', fac2 for CTRL, fac3 for SHIFT */ if (invert) { diff --git a/source/blender/editors/util/ed_util_ops.cc b/source/blender/editors/util/ed_util_ops.cc index 014944da916..25deacbcdd1 100644 --- a/source/blender/editors/util/ed_util_ops.cc +++ b/source/blender/editors/util/ed_util_ops.cc @@ -226,7 +226,7 @@ static int lib_id_fake_user_toggle_exec(bContext *C, wmOperator *op) ID *id = (ID *)idptr.data; - if ((id->lib != nullptr) || (ELEM(GS(id->name), ID_GR, ID_SCE, ID_SCR, ID_TXT, ID_OB, ID_WS))) { + if (ID_IS_LINKED(id) || (ELEM(GS(id->name), ID_GR, ID_SCE, ID_SCR, ID_TXT, ID_OB, ID_WS))) { BKE_report(op->reports, RPT_ERROR, "Data-block type does not support fake user"); return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/util/numinput.c b/source/blender/editors/util/numinput.c index 3bf226f6ba1..be6ac6e13e6 100644 --- a/source/blender/editors/util/numinput.c +++ b/source/blender/editors/util/numinput.c @@ -321,7 +321,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) if (U.flag & USER_FLAG_NUMINPUT_ADVANCED) #endif { - if ((event->ctrl == 0) && (event->alt == 0) && (event->ascii != '\0') && + if (((event->modifier & (KM_CTRL | KM_ALT)) == 0) && (event->ascii != '\0') && strchr("01234567890@%^&*-+/{}()[]<>.|", event->ascii)) { if (!(n->flag & NUM_EDIT_FULL)) { n->flag |= NUM_EDITED; @@ -339,7 +339,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) n->val_flag[idx] |= NUM_EDITED; return true; } - if (event->ctrl) { + if (event->modifier & KM_CTRL) { n->flag &= ~NUM_EDIT_FULL; return true; } @@ -375,7 +375,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) updated = true; break; } - else if (event->shift || !n->str[0]) { + else if ((event->modifier & KM_SHIFT) || !n->str[0]) { n->val[idx] = n->val_org[idx]; n->val_flag[idx] &= ~NUM_EDITED; n->str[0] = '\0'; @@ -390,7 +390,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) case EVT_DELKEY: if ((n->val_flag[idx] & NUM_EDITED) && n->str[0]) { int t_cur = cur = n->str_cur; - if (event->ctrl) { + if (event->modifier & KM_CTRL) { mode = STRCUR_JUMP_DELIM; } BLI_str_cursor_step_utf8(n->str, strlen(n->str), &t_cur, dir, mode, true); @@ -416,7 +416,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) ATTR_FALLTHROUGH; case EVT_RIGHTARROWKEY: cur = n->str_cur; - if (event->ctrl) { + if (event->modifier & KM_CTRL) { mode = STRCUR_JUMP_DELIM; } BLI_str_cursor_step_utf8(n->str, strlen(n->str), &cur, dir, mode, true); @@ -442,7 +442,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) n->val_flag[idx] &= ~(NUM_NEGATE | NUM_INVERSE); #endif - idx = (idx + idx_max + (event->ctrl ? 0 : 2)) % (idx_max + 1); + idx = (idx + idx_max + ((event->modifier & KM_CTRL) ? 0 : 2)) % (idx_max + 1); n->idx = idx; if (n->val_flag[idx] & NUM_EDITED) { value_to_editstr(n, idx); @@ -470,7 +470,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) n->val_flag[idx] |= NUM_EDITED; return true; } - else if (event->ctrl) { + else if (event->modifier & KM_CTRL) { n->flag &= ~NUM_EDIT_FULL; return true; } @@ -480,28 +480,28 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) #ifdef USE_FAKE_EDIT case EVT_PADMINUS: case EVT_MINUSKEY: - if (event->ctrl || !(n->flag & NUM_EDIT_FULL)) { + if ((event->modifier & KM_CTRL) || !(n->flag & NUM_EDIT_FULL)) { n->val_flag[idx] ^= NUM_NEGATE; updated = true; } break; case EVT_PADSLASHKEY: case EVT_SLASHKEY: - if (event->ctrl || !(n->flag & NUM_EDIT_FULL)) { + if ((event->modifier & KM_CTRL) || !(n->flag & NUM_EDIT_FULL)) { n->val_flag[idx] ^= NUM_INVERSE; updated = true; } break; #endif case EVT_CKEY: - if (event->ctrl) { + if (event->modifier & KM_CTRL) { /* Copy current `str` to the copy/paste buffer. */ WM_clipboard_text_set(n->str, 0); updated = true; } break; case EVT_VKEY: - if (event->ctrl) { + if (event->modifier & KM_CTRL) { /* extract the first line from the clipboard */ int pbuf_len; char *pbuf = WM_clipboard_text_get_firstline(false, &pbuf_len); @@ -531,7 +531,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) /* Up to this point, if we have a ctrl modifier, skip. * This allows to still access most of modals' shortcuts even in numinput mode. */ - if (!updated && event->ctrl) { + if (!updated && (event->modifier & KM_CTRL)) { return false; } diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c index 91b29049ecf..c0d0fe95c8c 100644 --- a/source/blender/editors/uvedit/uvedit_smart_stitch.c +++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c @@ -2598,7 +2598,7 @@ static int stitch_modal(bContext *C, wmOperator *op, const wmEvent *event) /* Increase limit */ case EVT_PADPLUSKEY: case WHEELUPMOUSE: - if (event->val == KM_PRESS && event->alt) { + if ((event->val == KM_PRESS) && (event->modifier & KM_ALT)) { ssc->limit_dist += 0.01f; if (!stitch_process_data(ssc, active_state, scene, false)) { stitch_cancel(C, op); @@ -2612,7 +2612,7 @@ static int stitch_modal(bContext *C, wmOperator *op, const wmEvent *event) /* Decrease limit */ case EVT_PADMINUS: case WHEELDOWNMOUSE: - if (event->val == KM_PRESS && event->alt) { + if ((event->val == KM_PRESS) && (event->modifier & KM_ALT)) { ssc->limit_dist -= 0.01f; ssc->limit_dist = MAX2(0.01f, ssc->limit_dist); if (!stitch_process_data(ssc, active_state, scene, false)) { @@ -2673,7 +2673,7 @@ static int stitch_modal(bContext *C, wmOperator *op, const wmEvent *event) /* Select geometry */ case RIGHTMOUSE: - if (!event->shift) { + if ((event->modifier & KM_SHIFT) == 0) { stitch_cancel(C, op); return OPERATOR_CANCELLED; } diff --git a/source/blender/functions/FN_generic_virtual_array.hh b/source/blender/functions/FN_generic_virtual_array.hh index 2655cd26bfe..ced0c2b9546 100644 --- a/source/blender/functions/FN_generic_virtual_array.hh +++ b/source/blender/functions/FN_generic_virtual_array.hh @@ -139,7 +139,7 @@ class GVArrayCommon { */ bool is_span() const; /** - * Returns the internally used span of the virtual array. This invokes undefined behavior is the + * Returns the internally used span of the virtual array. This invokes undefined behavior if the * virtual array is not stored as a span internally. */ GSpan get_internal_span() const; diff --git a/source/blender/functions/FN_multi_function_procedure_optimization.hh b/source/blender/functions/FN_multi_function_procedure_optimization.hh index 6e1e804c804..f5a02d84d42 100644 --- a/source/blender/functions/FN_multi_function_procedure_optimization.hh +++ b/source/blender/functions/FN_multi_function_procedure_optimization.hh @@ -38,9 +38,9 @@ namespace blender::fn::procedure_optimization { * For simplicity, and because this is the most common use case, this optimization currently only * works on a single chain of instructions. Destruct instructions are not moved across branches. * - * \param procedure The procedure that should be optimized. - * \param block_end_instr The instruction that points to the last instruction within a linear chain - * of instructions. The algorithm moves instructions backward starting at this instruction. + * \param procedure: The procedure that should be optimized. + * \param block_end_instr: The instruction that points to the last instruction within a linear + * chain of instructions. The algorithm moves instructions backward starting at this instruction. */ void move_destructs_up(MFProcedure &procedure, MFInstruction &block_end_instr); diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc index 054a6b51adf..7b514b6a49b 100644 --- a/source/blender/functions/intern/field.cc +++ b/source/blender/functions/intern/field.cc @@ -26,7 +26,7 @@ struct FieldTreeInfo { */ MultiValueMap<GFieldRef, GFieldRef> field_users; /** - * The same field input may exist in the field tree as as separate nodes due to the way + * The same field input may exist in the field tree as separate nodes due to the way * the tree is constructed. This set contains every different input only once. */ VectorSet<std::reference_wrapper<const FieldInput>> deduplicated_field_inputs; @@ -137,7 +137,7 @@ static Set<GFieldRef> find_varying_fields(const FieldTreeInfo &field_tree_info, } /** - * Builds the #procedure so that it computes the the fields. + * Builds the #procedure so that it computes the fields. */ static void build_multi_function_procedure_for_fields(MFProcedure &procedure, ResourceScope &scope, diff --git a/source/blender/geometry/intern/realize_instances.cc b/source/blender/geometry/intern/realize_instances.cc index c98bd9d6b74..4f7024eea82 100644 --- a/source/blender/geometry/intern/realize_instances.cc +++ b/source/blender/geometry/intern/realize_instances.cc @@ -13,6 +13,7 @@ #include "BLI_task.hh" #include "BKE_collection.h" +#include "BKE_curves.hh" #include "BKE_geometry_set_instances.hh" #include "BKE_material.h" #include "BKE_mesh.h" @@ -118,7 +119,7 @@ struct RealizeMeshTask { }; struct RealizeCurveInfo { - const CurveEval *curve = nullptr; + const Curves *curves; /** * Matches the order in #AllCurvesInfo.attributes. For point attributes, the `std::optional` * will be empty. @@ -163,7 +164,7 @@ struct AllCurvesInfo { /** Ordering of all attributes that are propagated to the output curve generically. */ OrderedAttributes attributes; /** Ordering of the original curves that are joined. */ - VectorSet<const CurveEval *> order; + VectorSet<const Curves *> order; /** Preprocessed data about every original curve. This is ordered by #order. */ Array<RealizeCurveInfo> realize_info; bool create_id_attribute = false; @@ -443,16 +444,16 @@ static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info, } case GEO_COMPONENT_TYPE_CURVE: { const CurveComponent &curve_component = *static_cast<const CurveComponent *>(component); - const CurveEval *curve = curve_component.get_for_read(); - if (curve != nullptr && !curve->splines().is_empty()) { - const int curve_index = gather_info.curves.order.index_of(curve); + const Curves *curves = curve_component.get_for_read(); + if (curves != nullptr && curves->geometry.curve_size > 0) { + const int curve_index = gather_info.curves.order.index_of(curves); const RealizeCurveInfo &curve_info = gather_info.curves.realize_info[curve_index]; gather_info.r_tasks.curve_tasks.append({gather_info.r_offsets.spline_offset, &curve_info, base_transform, base_instance_context.curves, base_instance_context.id}); - gather_info.r_offsets.spline_offset += curve->splines().size(); + gather_info.r_offsets.spline_offset += curves->geometry.curve_size; } break; } @@ -1038,11 +1039,11 @@ static OrderedAttributes gather_generic_curve_attributes_to_propagate( } static void gather_curves_to_realize(const GeometrySet &geometry_set, - VectorSet<const CurveEval *> &r_curves) + VectorSet<const Curves *> &r_curves) { - if (const CurveEval *curve = geometry_set.get_curve_for_read()) { - if (!curve->splines().is_empty()) { - r_curves.add(curve); + if (const Curves *curves = geometry_set.get_curves_for_read()) { + if (curves->geometry.curve_size != 0) { + r_curves.add(curves); } } if (const InstancesComponent *instances = @@ -1064,12 +1065,12 @@ static AllCurvesInfo preprocess_curves(const GeometrySet &geometry_set, info.realize_info.reinitialize(info.order.size()); for (const int curve_index : info.realize_info.index_range()) { RealizeCurveInfo &curve_info = info.realize_info[curve_index]; - const CurveEval *curve = info.order[curve_index]; - curve_info.curve = curve; + const Curves *curves = info.order[curve_index]; + curve_info.curves = curves; /* Access attributes. */ CurveComponent component; - component.replace(const_cast<CurveEval *>(curve), GeometryOwnershipType::ReadOnly); + component.replace(const_cast<Curves *>(curves), GeometryOwnershipType::ReadOnly); curve_info.spline_attributes.reinitialize(info.attributes.size()); for (const int attribute_index : info.attributes.index_range()) { const AttributeDomain domain = info.attributes.kinds[attribute_index].domain; @@ -1095,9 +1096,9 @@ static void execute_realize_curve_task(const RealizeInstancesOptions &options, MutableSpan<GMutableSpan> dst_spline_attributes) { const RealizeCurveInfo &curve_info = *task.curve_info; - const CurveEval &curve = *curve_info.curve; + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_info.curves); - const Span<SplinePtr> src_splines = curve.splines(); + const Span<SplinePtr> src_splines = curve->splines(); /* Initialize point attributes. */ threading::parallel_for(src_splines.index_range(), 100, [&](const IndexRange src_spline_range) { @@ -1206,12 +1207,12 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options, } const RealizeCurveTask &last_task = tasks.last(); - const CurveEval &last_curve = *last_task.curve_info->curve; - const int tot_splines = last_task.start_spline_index + last_curve.splines().size(); + const Curves &last_curves = *last_task.curve_info->curves; + const int tot_splines = last_task.start_spline_index + last_curves.geometry.curve_size; Array<SplinePtr> dst_splines(tot_splines); - CurveEval *dst_curve = new CurveEval(); + std::unique_ptr<CurveEval> dst_curve = std::make_unique<CurveEval>(); dst_curve->attributes.reallocate(tot_splines); CustomDataAttributes &spline_attributes = dst_curve->attributes; @@ -1242,7 +1243,7 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options, dst_curve->add_splines(dst_splines); CurveComponent &dst_component = r_realized_geometry.get_component_for_write<CurveComponent>(); - dst_component.replace(dst_curve); + dst_component.replace(curve_eval_to_curves(*dst_curve)); } /** \} */ diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c index e036b814916..fba83579ab1 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c @@ -104,7 +104,9 @@ static void BKE_gpencil_instance_modifier_instance_tfm(Object *ob, float obinv[4][4]; unit_m4(mat_offset); - add_v3_v3(mat_offset[3], mmd->offset); + if (mmd->flag & GP_ARRAY_USE_OFFSET) { + add_v3_v3(mat_offset[3], mmd->offset); + } invert_m4_m4(obinv, ob->obmat); mul_m4_series(r_offset, mat_offset, obinv, mmd->object->obmat); diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c index ba61ea148ca..1058f861be3 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c @@ -219,7 +219,7 @@ static void add_this_collection(Collection *c, return; } FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN (c, ob, mode) { - if (ELEM(ob->type, OB_MESH, OB_MBALL, OB_CURVE, OB_SURF, OB_FONT)) { + if (ELEM(ob->type, OB_MESH, OB_MBALL, OB_CURVES_LEGACY, OB_SURF, OB_FONT)) { if (ob->lineart.usage != OBJECT_LRT_EXCLUDE) { DEG_add_object_relation(ctx->node, ob, DEG_OB_COMP_GEOMETRY, "Line Art Modifier"); DEG_add_object_relation(ctx->node, ob, DEG_OB_COMP_TRANSFORM, "Line Art Modifier"); @@ -393,7 +393,7 @@ static void options_panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(col, ptr, "use_clip_plane_boundaries", 0, NULL, ICON_NONE); uiItemR(col, ptr, "use_crease_on_smooth", 0, IFACE_("Crease On Smooth"), ICON_NONE); uiItemR(col, ptr, "use_crease_on_sharp", 0, IFACE_("Crease On Sharp"), ICON_NONE); - uiItemR(col, ptr, "use_back_face_culling", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "use_back_face_culling", 0, IFACE_("Force Backface Culling"), ICON_NONE); } static void style_panel_draw(const bContext *UNUSED(C), Panel *panel) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c index 3b7b82e3085..365b9afe348 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c @@ -88,7 +88,7 @@ static void deformStroke(GpencilModifierData *md, break; } case GP_SIMPLIFY_SAMPLE: { - BKE_gpencil_stroke_sample(gpd, gps, mmd->length, false); + BKE_gpencil_stroke_sample(gpd, gps, mmd->length, false, mmd->sharp_threshold); break; } case GP_SIMPLIFY_MERGE: { @@ -143,6 +143,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) } else if (mode == GP_SIMPLIFY_SAMPLE) { uiItemR(layout, ptr, "length", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "sharp_threshold", 0, NULL, ICON_NONE); } else if (mode == GP_SIMPLIFY_MERGE) { uiItemR(layout, ptr, "distance", 0, NULL, ICON_NONE); diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c index 5e916a13f2c..dcec2865f29 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c @@ -79,11 +79,6 @@ static void deformStroke(GpencilModifierData *md, short type = gps->totpoints < 3 ? GP_SUBDIV_SIMPLE : mmd->type; BKE_gpencil_stroke_subdivide(gpd, gps, mmd->level, type); - - /* If the stroke is cyclic, must generate the closing geometry. */ - if (gps->flag & GP_STROKE_CYCLIC) { - BKE_gpencil_stroke_close(gps); - } } static void bakeModifier(struct Main *UNUSED(bmain), diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h index 2c7999699c7..5d952991cf7 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h +++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h @@ -343,6 +343,7 @@ typedef enum eLineartTriangleFlags { LRT_CULL_GENERATED = (1 << 2), LRT_TRIANGLE_INTERSECTION_ONLY = (1 << 3), LRT_TRIANGLE_NO_INTERSECTION = (1 << 4), + LRT_TRIANGLE_MAT_BACK_FACE_CULLING = (1 << 5), } eLineartTriangleFlags; /** diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index 6177b9ac366..2f648d7ed4b 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -1388,21 +1388,20 @@ static void lineart_main_perspective_division(LineartRenderBuffer *rb) LineartVert *vt; int i; - if (!rb->cam_is_persp) { - return; - } - LISTBASE_FOREACH (LineartElementLinkNode *, eln, &rb->vertex_buffer_pointers) { vt = eln->pointer; for (i = 0; i < eln->element_count; i++) { - /* Do not divide Z, we use Z to back transform cut points in later chaining process. */ - vt[i].fbcoord[0] /= vt[i].fbcoord[3]; - vt[i].fbcoord[1] /= vt[i].fbcoord[3]; - /* Re-map z into (0-1) range, because we no longer need NDC (Normalized Device Coordinates) - * at the moment. - * The algorithm currently doesn't need Z for operation, we use W instead. If Z is needed in - * the future, the line below correctly transforms it to view space coordinates. */ - // `vt[i].fbcoord[2] = -2 * vt[i].fbcoord[2] / (far - near) - (far + near) / (far - near); + if (rb->cam_is_persp) { + /* Do not divide Z, we use Z to back transform cut points in later chaining process. */ + vt[i].fbcoord[0] /= vt[i].fbcoord[3]; + vt[i].fbcoord[1] /= vt[i].fbcoord[3]; + /* Re-map z into (0-1) range, because we no longer need NDC (Normalized Device Coordinates) + * at the moment. + * The algorithm currently doesn't need Z for operation, we use W instead. If Z is needed + * in the future, the line below correctly transforms it to view space coordinates. */ + // `vt[i].fbcoord[2] = -2 * vt[i].fbcoord[2] / (far - near) - (far + near) / (far - near); + } + /* Shifting is always needed. */ vt[i].fbcoord[0] -= rb->shift_x * 2; vt[i].fbcoord[1] -= rb->shift_y * 2; } @@ -1529,8 +1528,9 @@ static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb, double *view_vector = vv; double dot_1 = 0, dot_2 = 0; double result; + bool material_back_face = ((tri1->flags | tri2->flags) & LRT_TRIANGLE_MAT_BACK_FACE_CULLING); - if (rb->use_contour || rb->use_back_face_culling) { + if (rb->use_contour || rb->use_back_face_culling || material_back_face) { if (rb->cam_is_persp) { sub_v3_v3v3_db(view_vector, rb->camera_pos, l->gloc); @@ -1555,14 +1555,19 @@ static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb, tri2->flags |= LRT_CULL_DISCARD; } } + if (material_back_face) { + if (tri1->flags & LRT_TRIANGLE_MAT_BACK_FACE_CULLING && dot_1 < 0) { + tri1->flags |= LRT_CULL_DISCARD; + } + if (tri2->flags & LRT_TRIANGLE_MAT_BACK_FACE_CULLING && dot_2 < 0) { + tri2->flags |= LRT_CULL_DISCARD; + } + } } else { view_vector = rb->view_vector; } - dot_1 = dot_v3v3_db(view_vector, tri1->gn); - dot_2 = dot_v3v3_db(view_vector, tri2->gn); - if ((result = dot_1 * dot_2) <= 0 && (fabs(dot_1) + fabs(dot_2))) { edge_flag_result |= LRT_EDGE_FLAG_CONTOUR; } @@ -1573,6 +1578,16 @@ static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb, return edge_flag_result; } + /* Do not show lines other than contour on back face (because contour has one adjacent face that + * isn't a back face). + * TODO(Yiming): Do we need separate option for this? */ + if (rb->use_back_face_culling || + ((tri1->flags & tri2->flags) & LRT_TRIANGLE_MAT_BACK_FACE_CULLING)) { + if (dot_1 < 0 && dot_2 < 0) { + return edge_flag_result; + } + } + if (rb->use_crease) { if (rb->sharp_as_crease && !BM_elem_flag_test(e, BM_ELEM_SMOOTH)) { edge_flag_result |= LRT_EDGE_FLAG_CREASE; @@ -1859,6 +1874,9 @@ static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBu mat->lineart.material_mask_bits : 0); tri->mat_occlusion |= (mat ? mat->lineart.mat_occlusion : 1); + tri->flags |= (mat && (mat->blend_flag & MA_BL_CULL_BACKFACE)) ? + LRT_TRIANGLE_MAT_BACK_FACE_CULLING : + 0; tri->intersection_mask = obi->override_intersection_mask; @@ -2193,7 +2211,7 @@ static void lineart_main_load_geometries( mul_m4db_m4db_m4fl_uniq(obi->model_view_proj, rb->view_projection, ob->obmat); mul_m4db_m4db_m4fl_uniq(obi->model_view, rb->view, ob->obmat); - if (!ELEM(use_ob->type, OB_MESH, OB_MBALL, OB_CURVE, OB_SURF, OB_FONT)) { + if (!ELEM(use_ob->type, OB_MESH, OB_MBALL, OB_CURVES_LEGACY, OB_SURF, OB_FONT)) { continue; } @@ -2205,7 +2223,7 @@ static void lineart_main_load_geometries( } if (use_ob->type == OB_MESH) { - use_mesh = use_ob->data; + use_mesh = BKE_object_get_evaluated_mesh(use_ob); } else { /* If DEG_ITER_OBJECT_FLAG_DUPLI is set, some curve objects may also have an evaluated mesh @@ -2502,19 +2520,16 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl), interp_v3_v3v3_db(gloc, e->v1->gloc, e->v2->gloc, cut); mul_v4_m4v3_db(trans, vp, gloc); mul_v3db_db(trans, (1 / trans[3])); - } - else { - interp_v3_v3v3_db(trans, e->v1->fbcoord, e->v2->fbcoord, cut); - } - trans[0] -= cam_shift_x * 2; - trans[1] -= cam_shift_y * 2; - - /* To accommodate `k=0` and `k=inf` (vertical) lines. here the cut is in image space. */ - if (fabs(e->v1->fbcoord[0] - e->v2->fbcoord[0]) > fabs(e->v1->fbcoord[1] - e->v2->fbcoord[1])) { - cut = ratiod(e->v1->fbcoord[0], e->v2->fbcoord[0], trans[0]); - } - else { - cut = ratiod(e->v1->fbcoord[1], e->v2->fbcoord[1], trans[1]); + trans[0] -= cam_shift_x * 2; + trans[1] -= cam_shift_y * 2; + /* To accommodate `k=0` and `k=inf` (vertical) lines. here the cut is in image space. */ + if (fabs(e->v1->fbcoord[0] - e->v2->fbcoord[0]) > + fabs(e->v1->fbcoord[1] - e->v2->fbcoord[1])) { + cut = ratiod(e->v1->fbcoord[0], e->v2->fbcoord[0], trans[0]); + } + else { + cut = ratiod(e->v1->fbcoord[1], e->v2->fbcoord[1], trans[1]); + } } #define LRT_GUARD_NOT_FOUND \ @@ -2940,9 +2955,10 @@ static LineartEdge *lineart_triangle_intersect(LineartRenderBuffer *rb, * them as well. */ mul_v4_m4v3_db(v1->fbcoord, rb->view_projection, v1->gloc); mul_v4_m4v3_db(v2->fbcoord, rb->view_projection, v2->gloc); - mul_v3db_db(v1->fbcoord, (1 / v1->fbcoord[3])); - mul_v3db_db(v2->fbcoord, (1 / v2->fbcoord[3])); - + if (rb->cam_is_persp) { + mul_v3db_db(v1->fbcoord, (1 / v1->fbcoord[3])); + mul_v3db_db(v2->fbcoord, (1 / v2->fbcoord[3])); + } v1->fbcoord[0] -= rb->shift_x * 2; v1->fbcoord[1] -= rb->shift_y * 2; v2->fbcoord[0] -= rb->shift_x * 2; @@ -4453,7 +4469,7 @@ static void lineart_gpencil_generate(LineartCache *cache, if ((match_output || (gpdg = BKE_object_defgroup_name_index(gpencil_object, vgname)) >= 0)) { if (eval_ob && eval_ob->type == OB_MESH) { int dindex = 0; - Mesh *me = (Mesh *)eval_ob->data; + Mesh *me = BKE_object_get_evaluated_mesh(eval_ob); if (me->dvert) { LISTBASE_FOREACH (bDeformGroup *, db, &me->vertex_group_names) { if ((!source_vgname) || strstr(db->name, source_vgname) == db->name) { diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index e883a12a5b2..3ad43ef05af 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -147,6 +147,7 @@ set(SRC intern/gpu_select_private.h intern/gpu_shader_create_info.hh intern/gpu_shader_create_info_private.hh + intern/gpu_shader_dependency_private.h intern/gpu_shader_interface.hh intern/gpu_shader_private.hh intern/gpu_state_private.hh @@ -384,7 +385,7 @@ file(GENERATE OUTPUT ${glsl_source_list_file} CONTENT "${GLSL_SOURCE_CONTENT}") list(APPEND SRC ${glsl_source_list_file}) list(APPEND INC ${CMAKE_CURRENT_BINARY_DIR}) -set(SHADER_CREATE_INFOS +set(SRC_SHADER_CREATE_INFOS ../draw/engines/workbench/shaders/infos/workbench_composite_info.hh ../draw/engines/workbench/shaders/infos/workbench_effect_antialiasing_info.hh ../draw/engines/workbench/shaders/infos/workbench_effect_cavity_info.hh @@ -435,7 +436,7 @@ set(SHADER_CREATE_INFOS ) set(SHADER_CREATE_INFOS_CONTENT "") -foreach(DESCRIPTOR_FILE ${SHADER_CREATE_INFOS}) +foreach(DESCRIPTOR_FILE ${SRC_SHADER_CREATE_INFOS}) string(APPEND SHADER_CREATE_INFOS_CONTENT "#include \"${DESCRIPTOR_FILE}\"\n") endforeach() @@ -452,12 +453,21 @@ if(WITH_IMAGE_DDS) add_definitions(-DWITH_DDS) endif() +if(WITH_OPENCOLORIO) + add_definitions(-DWITH_OCIO) +endif() + blender_add_lib(bf_gpu "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") target_link_libraries(bf_gpu PUBLIC bf_draw_shaders bf_gpu_shaders ) +if(WITH_OPENCOLORIO) + target_link_libraries(bf_gpu PUBLIC bf_ocio_shaders) +endif() + + if(CXX_WARN_NO_SUGGEST_OVERRIDE) target_compile_options(bf_gpu PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-Wsuggest-override>) endif() @@ -477,18 +487,22 @@ if(WITH_GPU_SHADER_BUILDER) ) target_include_directories(shader_builder PRIVATE ${INC} ${CMAKE_CURRENT_BINARY_DIR}) - set(BAKED_CREATE_INFOS_FILE ${CMAKE_CURRENT_BINARY_DIR}/shader_baked.hh) + set(SRC_BAKED_CREATE_INFOS_FILE ${CMAKE_CURRENT_BINARY_DIR}/shader_baked.hh) add_custom_command( OUTPUT - ${BAKED_CREATE_INFOS_FILE} + ${SRC_BAKED_CREATE_INFOS_FILE} COMMAND - "$<TARGET_FILE:shader_builder>" ${BAKED_CREATE_INFOS_FILE} + "$<TARGET_FILE:shader_builder>" ${SRC_BAKED_CREATE_INFOS_FILE} DEPENDS shader_builder ) set(GPU_SHADER_INFO_SRC intern/gpu_shader_info_baked.cc - ${BAKED_CREATE_INFOS_FILE} + ${SRC_BAKED_CREATE_INFOS_FILE} + + # For project files to be aware of these headers. + ${SRC_SHADER_CREATE_INFOS} + shaders/infos/gpu_interface_info.hh ) blender_add_lib(bf_gpu_shader_infos "${GPU_SHADER_INFO_SRC}" "" "" "") diff --git a/source/blender/gpu/GPU_batch.h b/source/blender/gpu/GPU_batch.h index 803a5825c94..7fad8dd23be 100644 --- a/source/blender/gpu/GPU_batch.h +++ b/source/blender/gpu/GPU_batch.h @@ -119,6 +119,7 @@ int GPU_batch_instbuf_add_ex(GPUBatch *, GPUVertBuf *, bool own_vbo); * Returns the index of verts in the batch. */ int GPU_batch_vertbuf_add_ex(GPUBatch *, GPUVertBuf *, bool own_vbo); +bool GPU_batch_vertbuf_has(GPUBatch *, GPUVertBuf *); #define GPU_batch_vertbuf_add(batch, verts) GPU_batch_vertbuf_add_ex(batch, verts, false) diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h index d689fbe14b5..734d407d011 100644 --- a/source/blender/gpu/GPU_texture.h +++ b/source/blender/gpu/GPU_texture.h @@ -226,6 +226,19 @@ GPUTexture *GPU_texture_create_compressed_2d( * Create an error texture that will bind an invalid texture (pink) at draw time. */ GPUTexture *GPU_texture_create_error(int dimension, bool array); +/** + * Create an alias of the source texture data. + * If \a src is freed, the texture view will continue to be valid. + * If \a mip_start or \a mip_len is bigger than available mips they will be clamped. + * TODO(@fclem): Target conversion is not implemented yet. + */ +GPUTexture *GPU_texture_create_view(const char *name, + const GPUTexture *src, + eGPUTextureFormat format, + int mip_start, + int mip_len, + int layer_start, + int layer_len); void GPU_texture_update_mipmap(GPUTexture *tex, int miplvl, diff --git a/source/blender/gpu/intern/gpu_batch.cc b/source/blender/gpu/intern/gpu_batch.cc index 44e95b1f5f5..32b117dac12 100644 --- a/source/blender/gpu/intern/gpu_batch.cc +++ b/source/blender/gpu/intern/gpu_batch.cc @@ -191,6 +191,16 @@ int GPU_batch_vertbuf_add_ex(GPUBatch *batch, GPUVertBuf *verts, bool own_vbo) return -1; } +bool GPU_batch_vertbuf_has(GPUBatch *batch, GPUVertBuf *verts) +{ + for (uint v = 0; v < GPU_BATCH_VBO_MAX_LEN; v++) { + if (batch->verts[v] == verts) { + return true; + } + } + return false; +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/gpu/intern/gpu_node_graph.c b/source/blender/gpu/intern/gpu_node_graph.c index da4e49f0257..57e415a8183 100644 --- a/source/blender/gpu/intern/gpu_node_graph.c +++ b/source/blender/gpu/intern/gpu_node_graph.c @@ -165,7 +165,7 @@ static const char *gpu_uniform_set_function_from_type(eNodeSocketDatatype type) /** * Link stack uniform buffer. - * This is called for the input/output sockets that are note connected. + * This is called for the input/output sockets that are not connected. */ static GPUNodeLink *gpu_uniformbuffer_link(GPUMaterial *mat, bNode *node, diff --git a/source/blender/gpu/intern/gpu_shader_create_info.hh b/source/blender/gpu/intern/gpu_shader_create_info.hh index bd0187e2dc5..bf74d44d9a7 100644 --- a/source/blender/gpu/intern/gpu_shader_create_info.hh +++ b/source/blender/gpu/intern/gpu_shader_create_info.hh @@ -255,7 +255,7 @@ struct StageInterfaceInfo { }; /** - * @brief Describe inputs & outputs, stage interfaces, resources and sources of a shader. + * \brief Describe inputs & outputs, stage interfaces, resources and sources of a shader. * If all data is correctly provided, this is all that is needed to create and compile * a GPUShader. * @@ -497,9 +497,9 @@ struct ShaderCreateInfo { /** * IMPORTANT: invocations count is only used if GL_ARB_gpu_shader5 is supported. On - * implementations that do not supports it, the max_vertices will be be multiplied by - * invocations. Your shader needs to account for this fact. Use `#ifdef GPU_ARB_gpu_shader5` - * and make a code path that does not rely on gl_InvocationID. + * implementations that do not supports it, the max_vertices will be multiplied by invocations. + * Your shader needs to account for this fact. Use `#ifdef GPU_ARB_gpu_shader5` and make a code + * path that does not rely on #gl_InvocationID. */ Self &geometry_layout(PrimitiveIn prim_in, PrimitiveOut prim_out, diff --git a/source/blender/gpu/intern/gpu_shader_dependency.cc b/source/blender/gpu/intern/gpu_shader_dependency.cc index 8a842ef4d7c..5b7df035acd 100644 --- a/source/blender/gpu/intern/gpu_shader_dependency.cc +++ b/source/blender/gpu/intern/gpu_shader_dependency.cc @@ -21,6 +21,9 @@ extern "C" { #define SHADER_SOURCE(datatoc, filename, filepath) extern char datatoc[]; #include "glsl_draw_source_list.h" #include "glsl_gpu_source_list.h" +#ifdef WITH_OCIO +# include "glsl_ocio_source_list.h" +#endif #undef SHADER_SOURCE } @@ -348,6 +351,9 @@ void gpu_shader_dependency_init() g_sources->add_new(filename, new GPUSource(filepath, filename, datatoc)); #include "glsl_draw_source_list.h" #include "glsl_gpu_source_list.h" +#ifdef WITH_OCIO +# include "glsl_ocio_source_list.h" +#endif #undef SHADER_SOURCE int errors = 0; diff --git a/source/blender/gpu/intern/gpu_shader_dependency_private.h b/source/blender/gpu/intern/gpu_shader_dependency_private.h index 29ee70962d1..7b9ecef5835 100644 --- a/source/blender/gpu/intern/gpu_shader_dependency_private.h +++ b/source/blender/gpu/intern/gpu_shader_dependency_private.h @@ -38,7 +38,7 @@ StringRefNull gpu_shader_dependency_get_source(const StringRefNull source_name); /** * \brief Find the name of the file from which the given string was generated. - * \return Return filename or empty string. + * \return filename or empty string. * \note source_string needs to be identical to the one given by gpu_shader_dependency_get_source() */ StringRefNull gpu_shader_dependency_get_filename_from_source_string( diff --git a/source/blender/gpu/intern/gpu_texture.cc b/source/blender/gpu/intern/gpu_texture.cc index da5f08f003e..9e6a6f75391 100644 --- a/source/blender/gpu/intern/gpu_texture.cc +++ b/source/blender/gpu/intern/gpu_texture.cc @@ -52,11 +52,13 @@ Texture::~Texture() #endif } -bool Texture::init_1D(int w, int layers, eGPUTextureFormat format) +bool Texture::init_1D(int w, int layers, int mips, eGPUTextureFormat format) { w_ = w; h_ = layers; d_ = 0; + int mips_max = 1 + floorf(log2f(w)); + mipmaps_ = min_ii(mips, mips_max); format_ = format; format_flag_ = to_format_flag(format); type_ = (layers > 0) ? GPU_TEXTURE_1D_ARRAY : GPU_TEXTURE_1D; @@ -66,11 +68,13 @@ bool Texture::init_1D(int w, int layers, eGPUTextureFormat format) return this->init_internal(); } -bool Texture::init_2D(int w, int h, int layers, eGPUTextureFormat format) +bool Texture::init_2D(int w, int h, int layers, int mips, eGPUTextureFormat format) { w_ = w; h_ = h; d_ = layers; + int mips_max = 1 + floorf(log2f(max_ii(w, h))); + mipmaps_ = min_ii(mips, mips_max); format_ = format; format_flag_ = to_format_flag(format); type_ = (layers > 0) ? GPU_TEXTURE_2D_ARRAY : GPU_TEXTURE_2D; @@ -80,11 +84,13 @@ bool Texture::init_2D(int w, int h, int layers, eGPUTextureFormat format) return this->init_internal(); } -bool Texture::init_3D(int w, int h, int d, eGPUTextureFormat format) +bool Texture::init_3D(int w, int h, int d, int mips, eGPUTextureFormat format) { w_ = w; h_ = h; d_ = d; + int mips_max = 1 + floorf(log2f(max_iii(w, h, d))); + mipmaps_ = min_ii(mips, mips_max); format_ = format; format_flag_ = to_format_flag(format); type_ = GPU_TEXTURE_3D; @@ -94,11 +100,13 @@ bool Texture::init_3D(int w, int h, int d, eGPUTextureFormat format) return this->init_internal(); } -bool Texture::init_cubemap(int w, int layers, eGPUTextureFormat format) +bool Texture::init_cubemap(int w, int layers, int mips, eGPUTextureFormat format) { w_ = w; h_ = w; d_ = max_ii(1, layers) * 6; + int mips_max = 1 + floorf(log2f(w)); + mipmaps_ = min_ii(mips, mips_max); format_ = format; format_flag_ = to_format_flag(format); type_ = (layers > 0) ? GPU_TEXTURE_CUBE_ARRAY : GPU_TEXTURE_CUBE; @@ -123,6 +131,42 @@ bool Texture::init_buffer(GPUVertBuf *vbo, eGPUTextureFormat format) return this->init_internal(vbo); } +bool Texture::init_view(const GPUTexture *src_, + eGPUTextureFormat format, + int mip_start, + int mip_len, + int layer_start, + int layer_len) +{ + const Texture *src = unwrap(src_); + w_ = src->w_; + h_ = src->h_; + d_ = src->d_; + switch (type_) { + case GPU_TEXTURE_1D_ARRAY: + h_ = layer_len; + break; + case GPU_TEXTURE_CUBE_ARRAY: + BLI_assert(layer_len % 6 == 0); + ATTR_FALLTHROUGH; + case GPU_TEXTURE_2D_ARRAY: + d_ = layer_len; + break; + default: + BLI_assert(layer_len == 1 && layer_start == 0); + break; + } + mip_start = min_ii(mip_start, src->mipmaps_ - 1); + mip_len = min_ii(mip_len, (src->mipmaps_ - mip_start)); + mipmaps_ = mip_len; + format_ = format; + format_flag_ = to_format_flag(format); + /* For now always copy the target. Target aliasing could be exposed later. */ + type_ = src->type_; + sampler_state = src->sampler_state; + return this->init_internal(src_, mip_start, layer_start); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -187,28 +231,29 @@ static inline GPUTexture *gpu_texture_create(const char *name, const int h, const int d, const eGPUTextureType type, - int UNUSED(mips), + int mips, eGPUTextureFormat tex_format, eGPUDataFormat data_format, const void *pixels) { + BLI_assert(mips > 0); Texture *tex = GPUBackend::get()->texture_alloc(name); bool success = false; switch (type) { case GPU_TEXTURE_1D: case GPU_TEXTURE_1D_ARRAY: - success = tex->init_1D(w, h, tex_format); + success = tex->init_1D(w, h, mips, tex_format); break; case GPU_TEXTURE_2D: case GPU_TEXTURE_2D_ARRAY: - success = tex->init_2D(w, h, d, tex_format); + success = tex->init_2D(w, h, d, mips, tex_format); break; case GPU_TEXTURE_3D: - success = tex->init_3D(w, h, d, tex_format); + success = tex->init_3D(w, h, d, mips, tex_format); break; case GPU_TEXTURE_CUBE: case GPU_TEXTURE_CUBE_ARRAY: - success = tex->init_cubemap(w, d, tex_format); + success = tex->init_cubemap(w, d, mips, tex_format); break; default: break; @@ -286,7 +331,7 @@ GPUTexture *GPU_texture_create_compressed_2d( const char *name, int w, int h, int miplen, eGPUTextureFormat tex_format, const void *data) { Texture *tex = GPUBackend::get()->texture_alloc(name); - bool success = tex->init_2D(w, h, 0, tex_format); + bool success = tex->init_2D(w, h, 0, miplen, tex_format); if (!success) { delete tex; @@ -334,6 +379,21 @@ GPUTexture *GPU_texture_create_error(int dimension, bool is_array) return gpu_texture_create("invalid_tex", w, h, d, type, 1, GPU_RGBA8, GPU_DATA_FLOAT, pixel); } +GPUTexture *GPU_texture_create_view(const char *name, + const GPUTexture *src, + eGPUTextureFormat format, + int mip_start, + int mip_len, + int layer_start, + int layer_len) +{ + BLI_assert(mip_len > 0); + BLI_assert(layer_len > 0); + Texture *view = GPUBackend::get()->texture_alloc(name); + view->init_view(src, format, mip_start, mip_len, layer_start, layer_len); + return wrap(view); +} + /* ------ Update ------ */ void GPU_texture_update_mipmap(GPUTexture *tex_, diff --git a/source/blender/gpu/intern/gpu_texture_private.hh b/source/blender/gpu/intern/gpu_texture_private.hh index 2c57c467bcf..ec11fab421c 100644 --- a/source/blender/gpu/intern/gpu_texture_private.hh +++ b/source/blender/gpu/intern/gpu_texture_private.hh @@ -101,11 +101,17 @@ class Texture { virtual ~Texture(); /* Return true on success. */ - bool init_1D(int w, int layers, eGPUTextureFormat format); - bool init_2D(int w, int h, int layers, eGPUTextureFormat format); - bool init_3D(int w, int h, int d, eGPUTextureFormat format); - bool init_cubemap(int w, int layers, eGPUTextureFormat format); + bool init_1D(int w, int layers, int mips, eGPUTextureFormat format); + bool init_2D(int w, int h, int layers, int mips, eGPUTextureFormat format); + bool init_3D(int w, int h, int d, int mips, eGPUTextureFormat format); + bool init_cubemap(int w, int layers, int mips, eGPUTextureFormat format); bool init_buffer(GPUVertBuf *vbo, eGPUTextureFormat format); + bool init_view(const GPUTexture *src, + eGPUTextureFormat format, + int mip_start, + int mip_len, + int layer_start, + int layer_len); virtual void generate_mipmap() = 0; virtual void copy_to(Texture *tex) = 0; @@ -234,6 +240,7 @@ class Texture { protected: virtual bool init_internal() = 0; virtual bool init_internal(GPUVertBuf *vbo) = 0; + virtual bool init_internal(const GPUTexture *src, int mip_offset, int layer_offset) = 0; }; /* Syntactic sugar. */ diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c index fd4a87bc544..b5a572bccbe 100644 --- a/source/blender/gpu/intern/gpu_viewport.c +++ b/source/blender/gpu/intern/gpu_viewport.c @@ -277,8 +277,6 @@ void GPU_viewport_stereo_composite(GPUViewport *viewport, Stereo3dFormat *stereo GPU_matrix_identity_set(); GPU_matrix_identity_projection_set(); immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_OVERLAYS_STEREO_MERGE); - immUniform1i("overlayTexture", 0); - immUniform1i("imageTexture", 1); int settings = stereo_format->display_mode; if (settings == S3D_DISPLAY_ANAGLYPH) { switch (stereo_format->anaglyph_type) { @@ -432,8 +430,6 @@ static void gpu_viewport_draw_colormanaged(GPUViewport *viewport, GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_IMAGE_OVERLAYS_MERGE); GPU_batch_uniform_1i(batch, "overlay", do_overlay_merge); GPU_batch_uniform_1i(batch, "display_transform", display_colorspace); - GPU_batch_uniform_1i(batch, "image_texture", 0); - GPU_batch_uniform_1i(batch, "overlays_texture", 1); } GPU_texture_bind(color, 0); diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc index 7a1674a5f01..302d8249914 100644 --- a/source/blender/gpu/opengl/gl_backend.cc +++ b/source/blender/gpu/opengl/gl_backend.cc @@ -240,6 +240,7 @@ static void detect_workarounds() GLContext::texture_cube_map_array_support = false; GLContext::texture_filter_anisotropic_support = false; GLContext::texture_gather_support = false; + GLContext::texture_storage_support = false; GLContext::vertex_attrib_binding_support = false; return; } @@ -439,6 +440,7 @@ bool GLContext::shader_draw_parameters_support = false; bool GLContext::texture_cube_map_array_support = false; bool GLContext::texture_filter_anisotropic_support = false; bool GLContext::texture_gather_support = false; +bool GLContext::texture_storage_support = false; bool GLContext::vertex_attrib_binding_support = false; /** Workarounds. */ @@ -501,6 +503,7 @@ void GLBackend::capabilities_init() GLContext::texture_cube_map_array_support = GLEW_ARB_texture_cube_map_array; GLContext::texture_filter_anisotropic_support = GLEW_EXT_texture_filter_anisotropic; GLContext::texture_gather_support = GLEW_ARB_texture_gather; + GLContext::texture_storage_support = GLEW_VERSION_4_3; GLContext::vertex_attrib_binding_support = GLEW_ARB_vertex_attrib_binding; detect_workarounds(); diff --git a/source/blender/gpu/opengl/gl_context.hh b/source/blender/gpu/opengl/gl_context.hh index fe2ad0a4747..369b667cbcc 100644 --- a/source/blender/gpu/opengl/gl_context.hh +++ b/source/blender/gpu/opengl/gl_context.hh @@ -64,6 +64,7 @@ class GLContext : public Context { static bool texture_cube_map_array_support; static bool texture_filter_anisotropic_support; static bool texture_gather_support; + static bool texture_storage_support; static bool vertex_attrib_binding_support; /** Workarounds. */ diff --git a/source/blender/gpu/opengl/gl_immediate.cc b/source/blender/gpu/opengl/gl_immediate.cc index cfedfe348f1..c32a6afd8cf 100644 --- a/source/blender/gpu/opengl/gl_immediate.cc +++ b/source/blender/gpu/opengl/gl_immediate.cc @@ -139,7 +139,7 @@ void GLImmediate::end() GLContext::get()->state_manager->apply_state(); /* We convert the offset in vertex offset from the buffer's start. - * This works because we added some padding to align the first vertex vertex. */ + * This works because we added some padding to align the first vertex. */ uint v_first = buffer_offset() / vertex_format.stride; GLVertArray::update_bindings( vao_id_, v_first, &vertex_format, reinterpret_cast<Shader *>(shader)->interface); diff --git a/source/blender/gpu/opengl/gl_texture.cc b/source/blender/gpu/opengl/gl_texture.cc index 22af2dd463f..0a5c7f8e79e 100644 --- a/source/blender/gpu/opengl/gl_texture.cc +++ b/source/blender/gpu/opengl/gl_texture.cc @@ -69,9 +69,79 @@ bool GLTexture::init_internal() return false; } - this->ensure_mipmaps(0); + GLenum internal_format = to_gl_internal_format(format_); + const bool is_cubemap = bool(type_ == GPU_TEXTURE_CUBE); + const bool is_layered = bool(type_ & GPU_TEXTURE_ARRAY); + const bool is_compressed = bool(format_flag_ & GPU_TEXTURE_ARRAY); + const int dimensions = (is_cubemap) ? 2 : this->dimensions_count(); + GLenum gl_format = to_gl_data_format(format_); + GLenum gl_type = to_gl(to_data_format(format_)); + + auto mip_size = [&](int h, int w = 1, int d = 1) -> size_t { + return divide_ceil_u(w, 4) * divide_ceil_u(h, 4) * divide_ceil_u(d, 4) * + to_block_size(format_); + }; + switch (dimensions) { + default: + case 1: + if (GLContext::texture_storage_support) { + glTexStorage1D(target_, mipmaps_, internal_format, w_); + } + else { + for (int i = 0, w = w_; i < mipmaps_; i++) { + if (is_compressed) { + glCompressedTexImage1D(target_, i, internal_format, w, 0, mip_size(w), nullptr); + } + else { + glTexImage1D(target_, i, internal_format, w, 0, gl_format, gl_type, nullptr); + } + w = max_ii(1, (w / 2)); + } + } + break; + case 2: + if (GLContext::texture_storage_support) { + glTexStorage2D(target_, mipmaps_, internal_format, w_, h_); + } + else { + for (int i = 0, w = w_, h = h_; i < mipmaps_; i++) { + for (int f = 0; f < (is_cubemap ? 6 : 1); f++) { + GLenum target = (is_cubemap) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + f : target_; + if (is_compressed) { + glCompressedTexImage2D(target, i, internal_format, w, h, 0, mip_size(w, h), nullptr); + } + else { + glTexImage2D(target, i, internal_format, w, h, 0, gl_format, gl_type, nullptr); + } + } + w = max_ii(1, (w / 2)); + h = is_layered ? h_ : max_ii(1, (h / 2)); + } + } + break; + case 3: + if (GLContext::texture_storage_support) { + glTexStorage3D(target_, mipmaps_, internal_format, w_, h_, d_); + } + else { + for (int i = 0, w = w_, h = h_, d = d_; i < mipmaps_; i++) { + if (is_compressed) { + glCompressedTexImage3D( + target_, i, internal_format, w, h, d, 0, mip_size(w, h, d), nullptr); + } + else { + glTexImage3D(target_, i, internal_format, w, h, d, 0, gl_format, gl_type, nullptr); + } + w = max_ii(1, (w / 2)); + h = max_ii(1, (h / 2)); + d = is_layered ? d_ : max_ii(1, (d / 2)); + } + } + break; + } + this->mip_range_set(0, mipmaps_ - 1); - /* Avoid issue with incomplete textures. */ + /* Avoid issue with formats not supporting filtering. Nearest by default. */ if (GLContext::direct_state_access_support) { glTextureParameteri(tex_id_, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } @@ -105,65 +175,26 @@ bool GLTexture::init_internal(GPUVertBuf *vbo) return true; } -void GLTexture::ensure_mipmaps(int miplvl) +bool GLTexture::init_internal(const GPUTexture *src, int mip_offset, int layer_offset) { - int effective_h = (type_ == GPU_TEXTURE_1D_ARRAY) ? 0 : h_; - int effective_d = (type_ != GPU_TEXTURE_3D) ? 0 : d_; - int max_dimension = max_iii(w_, effective_h, effective_d); - int max_miplvl = floor(log2(max_dimension)); - miplvl = min_ii(miplvl, max_miplvl); - - while (mipmaps_ < miplvl) { - int mip = ++mipmaps_; - const int dimensions = this->dimensions_count(); - - int w = mip_width_get(mip); - int h = mip_height_get(mip); - int d = mip_depth_get(mip); - GLenum internal_format = to_gl_internal_format(format_); - GLenum gl_format = to_gl_data_format(format_); - GLenum gl_type = to_gl(to_data_format(format_)); + BLI_assert(GLContext::texture_storage_support); - GLContext::state_manager_active_get()->texture_bind_temp(this); + const GLTexture *gl_src = static_cast<const GLTexture *>(unwrap(src)); + GLenum internal_format = to_gl_internal_format(format_); + target_ = to_gl_target(type_); - if (type_ == GPU_TEXTURE_CUBE) { - for (int i = 0; i < d; i++) { - GLenum target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + i; - glTexImage2D(target, mip, internal_format, w, h, 0, gl_format, gl_type, nullptr); - } - } - else if (format_flag_ & GPU_FORMAT_COMPRESSED) { - size_t size = ((w + 3) / 4) * ((h + 3) / 4) * to_block_size(format_); - switch (dimensions) { - default: - case 1: - glCompressedTexImage1D(target_, mip, internal_format, w, 0, size, nullptr); - break; - case 2: - glCompressedTexImage2D(target_, mip, internal_format, w, h, 0, size, nullptr); - break; - case 3: - glCompressedTexImage3D(target_, mip, internal_format, w, h, d, 0, size, nullptr); - break; - } - } - else { - switch (dimensions) { - default: - case 1: - glTexImage1D(target_, mip, internal_format, w, 0, gl_format, gl_type, nullptr); - break; - case 2: - glTexImage2D(target_, mip, internal_format, w, h, 0, gl_format, gl_type, nullptr); - break; - case 3: - glTexImage3D(target_, mip, internal_format, w, h, d, 0, gl_format, gl_type, nullptr); - break; - } - } - } + glTextureView(tex_id_, + target_, + gl_src->tex_id_, + internal_format, + mip_offset, + mipmaps_, + layer_offset, + this->layer_count()); - this->mip_range_set(0, mipmaps_); + debug::object_label(GL_TEXTURE, tex_id_, name_); + + return true; } /** \} */ @@ -216,9 +247,7 @@ void GLTexture::update_sub( BLI_assert(validate_data_format(format_, type)); BLI_assert(data != nullptr); - this->ensure_mipmaps(mip); - - if (mip > mipmaps_) { + if (mip >= mipmaps_) { debug::raise_gl_error("Updating a miplvl on a texture too small to have this many levels."); return; } @@ -283,7 +312,6 @@ void GLTexture::update_sub( */ void GLTexture::generate_mipmap() { - this->ensure_mipmaps(9999); /* Some drivers have bugs when using #glGenerateMipmap with depth textures (see T56789). * In this case we just create a complete texture with mipmaps manually without * down-sampling. You must initialize the texture levels using other methods like diff --git a/source/blender/gpu/opengl/gl_texture.hh b/source/blender/gpu/opengl/gl_texture.hh index 07d3bb8c946..d4d024f5e3e 100644 --- a/source/blender/gpu/opengl/gl_texture.hh +++ b/source/blender/gpu/opengl/gl_texture.hh @@ -74,11 +74,11 @@ class GLTexture : public Texture { bool init_internal() override; /** Return true on success. */ bool init_internal(GPUVertBuf *vbo) override; + /** Return true on success. */ + bool init_internal(const GPUTexture *src, int mip_offset, int layer_offset) override; private: bool proxy_check(int mip); - /** Will create enough mipmaps up to get to the given level. */ - void ensure_mipmaps(int mip); void update_sub_direct_state_access( int mip, int offset[3], int extent[3], GLenum gl_format, GLenum gl_type, const void *data); GPUFrameBuffer *framebuffer_get(); diff --git a/source/blender/gpu/opengl/gl_vertex_array.cc b/source/blender/gpu/opengl/gl_vertex_array.cc index 88b69eb5b62..04f60f10d41 100644 --- a/source/blender/gpu/opengl/gl_vertex_array.cc +++ b/source/blender/gpu/opengl/gl_vertex_array.cc @@ -54,7 +54,7 @@ static uint16_t vbo_bind(const ShaderInterface *interface, const char *name = GPU_vertformat_attr_name_get(format, a, n_idx); const ShaderInput *input = interface->attr_get(name); - if (input == nullptr) { + if (input == nullptr || input->location == -1) { continue; } diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_merge_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_merge_info.hh index d3b70cb67f9..2798846b310 100644 --- a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_merge_info.hh +++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_overlays_merge_info.hh @@ -16,6 +16,7 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_image_overlays_merge) .push_constant(Type::MAT4, "ModelViewProjectionMatrix") .push_constant(Type::BOOL, "display_transform") .push_constant(Type::BOOL, "overlay") + /* Sampler slots should match OCIO's. */ .sampler(0, ImageType::FLOAT_2D, "image_texture") .sampler(1, ImageType::FLOAT_2D, "overlays_texture") .vertex_source("gpu_shader_2D_image_vert.glsl") diff --git a/source/blender/ikplugin/intern/itasc_plugin.cpp b/source/blender/ikplugin/intern/itasc_plugin.cpp index 6dcfd3d014e..470dfb01bdd 100644 --- a/source/blender/ikplugin/intern/itasc_plugin.cpp +++ b/source/blender/ikplugin/intern/itasc_plugin.cpp @@ -244,9 +244,9 @@ static int initialize_chain(Object *ob, bPoseChannel *pchan_tip, bConstraint *co } if (BLI_listbase_is_empty(&curchan->iktree) == false) { - /* Oh oh, there is already a chain starting from this channel and our chain is longer... + /* Oh, there is already a chain starting from this channel and our chain is longer. * Should handle this by moving the previous chain up to the beginning of our chain - * For now we just stop here */ + * For now we just stop here. */ break; } } diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index 23b9c85bd5b..8929a467670 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -560,6 +560,9 @@ bool IMB_alpha_affects_rgb(const struct ImBuf *ibuf); * Create char buffer, color corrected if necessary, for ImBufs that lack one. */ void IMB_rect_from_float(struct ImBuf *ibuf); +void IMB_float_from_rect_ex(struct ImBuf *dst, + const struct ImBuf *src, + const struct rcti *region_to_update); void IMB_float_from_rect(struct ImBuf *ibuf); /** * No profile conversion. @@ -897,16 +900,16 @@ typedef enum eIMBTransformMode { /** * \brief Transform source image buffer onto destination image buffer using a transform matrix. * - * \param src Image buffer to read from. - * \param dst Image buffer to write to. rect or rect_float must already be initialized. + * \param src: Image buffer to read from. + * \param dst: Image buffer to write to. rect or rect_float must already be initialized. * - dst buffer must be a 4 channel buffers. * - Only one data type buffer will be used (rect_float has priority over rect) - * \param mode Cropping/Wrap repeat effect to apply during transformation. - * \param filter Interpolation to use during sampling. - * \param transform_matrix Transformation matrix to use. + * \param mode: Cropping/Wrap repeat effect to apply during transformation. + * \param filter: Interpolation to use during sampling. + * \param transform_matrix: Transformation matrix to use. * The given matrix should transform between dst pixel space to src pixel space. * One unit is one pixel. - * \param src_crop cropping region how to crop the source buffer. Should only be passed when mode + * \param src_crop: Cropping region how to crop the source buffer. Should only be passed when mode * is set to #IMB_TRANSFORM_MODE_CROP_SRC. For any other mode this should be empty. * * During transformation no data/color conversion will happens. diff --git a/source/blender/imbuf/intern/IMB_anim.h b/source/blender/imbuf/intern/IMB_anim.h index bad2081448f..e99572adbb0 100644 --- a/source/blender/imbuf/intern/IMB_anim.h +++ b/source/blender/imbuf/intern/IMB_anim.h @@ -108,7 +108,7 @@ struct anim { #ifdef WITH_FFMPEG AVFormatContext *pFormatCtx; AVCodecContext *pCodecCtx; - AVCodec *pCodec; + const AVCodec *pCodec; AVFrame *pFrame; int pFrameComplete; AVFrame *pFrameRGB; diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index 4125662d35f..f97a50ecf47 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -492,7 +492,7 @@ static int startffmpeg(struct anim *anim) { int i, video_stream_index; - AVCodec *pCodec; + const AVCodec *pCodec; AVFormatContext *pFormatCtx = NULL; AVCodecContext *pCodecCtx; AVRational frame_rate; @@ -867,6 +867,17 @@ static void ffmpeg_decode_store_frame_pts(struct anim *anim) (int64_t)anim->cur_pts); } +static int ffmpeg_read_video_frame(struct anim *anim, AVPacket *packet) +{ + int ret = 0; + while (ret = av_read_frame(anim->pFormatCtx, packet) >= 0) { + if (packet->stream_index == anim->videoStream) { + break; + } + } + return ret; +} + /* decode one video frame also considering the packet read into cur_packet */ static int ffmpeg_decode_video_frame(struct anim *anim) { @@ -887,7 +898,7 @@ static int ffmpeg_decode_video_frame(struct anim *anim) anim->cur_packet->stream_index = -1; } - while ((rval = av_read_frame(anim->pFormatCtx, anim->cur_packet)) >= 0) { + while ((rval = ffmpeg_read_video_frame(anim, anim->cur_packet)) >= 0) { av_log(anim->pFormatCtx, AV_LOG_DEBUG, "%sREAD: strID=%d (VID: %d) dts=%" PRId64 " pts=%" PRId64 " %s\n", @@ -897,14 +908,13 @@ static int ffmpeg_decode_video_frame(struct anim *anim) (anim->cur_packet->dts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->cur_packet->dts, (anim->cur_packet->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->cur_packet->pts, (anim->cur_packet->flags & AV_PKT_FLAG_KEY) ? " KEY" : ""); - if (anim->cur_packet->stream_index == anim->videoStream) { - avcodec_send_packet(anim->pCodecCtx, anim->cur_packet); - anim->pFrameComplete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0; - if (anim->pFrameComplete) { - ffmpeg_decode_store_frame_pts(anim); - break; - } + avcodec_send_packet(anim->pCodecCtx, anim->cur_packet); + anim->pFrameComplete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0; + + if (anim->pFrameComplete) { + ffmpeg_decode_store_frame_pts(anim); + break; } av_packet_unref(anim->cur_packet); anim->cur_packet->stream_index = -1; @@ -1159,13 +1169,59 @@ static int ffmpeg_generic_seek_workaround(struct anim *anim, return av_seek_frame(anim->pFormatCtx, anim->videoStream, current_pts, AVSEEK_FLAG_BACKWARD); } +/* Read packet until timestamp matches `anim->cur_packet`, thus recovering internal `anim` stream + * position state. */ +static void ffmpeg_seek_recover_stream_position(struct anim *anim) +{ + AVPacket *temp_packet = av_packet_alloc(); + while (ffmpeg_read_video_frame(anim, temp_packet)) { + int64_t current_pts = timestamp_from_pts_or_dts(anim->cur_packet->pts, anim->cur_packet->dts); + int64_t temp_pts = timestamp_from_pts_or_dts(temp_packet->pts, temp_packet->dts); + av_packet_unref(temp_packet); + + if (current_pts == temp_pts) { + break; + } + } + av_packet_free(&temp_packet); +} + +/* Check if seeking and mainly flushing codec buffers is needed. */ +static bool ffmpeg_seek_buffers_need_flushing(struct anim *anim, int position, int64_t seek_pos) +{ + /* Get timestamp of packet read after seeking. */ + AVPacket *temp_packet = av_packet_alloc(); + ffmpeg_read_video_frame(anim, temp_packet); + int64_t gop_pts = timestamp_from_pts_or_dts(temp_packet->pts, temp_packet->dts); + av_packet_unref(temp_packet); + av_packet_free(&temp_packet); + + /* Seeking gives packet, that is currently read. No seeking was necessary, so buffers don't have + * to be flushed. */ + if (gop_pts == timestamp_from_pts_or_dts(anim->cur_packet->pts, anim->cur_packet->dts)) { + return false; + } + + /* Packet after seeking is same key frame as current, and further in time. No seeking was + * necessary, so buffers don't have to be flushed. But stream position has to be recovered. */ + if (gop_pts == anim->cur_key_frame_pts && position > anim->cur_position) { + ffmpeg_seek_recover_stream_position(anim); + return false; + } + + /* Seeking was necessary, but we have read packets. Therefore we must seek again. */ + av_seek_frame(anim->pFormatCtx, anim->videoStream, seek_pos, AVSEEK_FLAG_BACKWARD); + anim->cur_key_frame_pts = gop_pts; + return true; +} + /* Seek to last necessary key frame. */ static int ffmpeg_seek_to_key_frame(struct anim *anim, int position, struct anim_index *tc_index, int64_t pts_to_search) { - int64_t pos; + int64_t seek_pos; int ret; if (tc_index) { @@ -1180,23 +1236,23 @@ static int ffmpeg_seek_to_key_frame(struct anim *anim, uint64_t pts; uint64_t dts; - pos = IMB_indexer_get_seek_pos(tc_index, new_frame_index); + seek_pos = IMB_indexer_get_seek_pos(tc_index, new_frame_index); pts = IMB_indexer_get_seek_pos_pts(tc_index, new_frame_index); dts = IMB_indexer_get_seek_pos_dts(tc_index, new_frame_index); anim->cur_key_frame_pts = timestamp_from_pts_or_dts(pts, dts); - av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek pos = %" PRId64 "\n", pos); + av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek seek_pos = %" PRId64 "\n", seek_pos); av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek pts = %" PRIu64 "\n", pts); av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek dts = %" PRIu64 "\n", dts); if (ffmpeg_seek_by_byte(anim->pFormatCtx)) { - av_log(anim->pFormatCtx, AV_LOG_DEBUG, "... using BYTE pos\n"); + av_log(anim->pFormatCtx, AV_LOG_DEBUG, "... using BYTE seek_pos\n"); - ret = av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BYTE); + ret = av_seek_frame(anim->pFormatCtx, -1, seek_pos, AVSEEK_FLAG_BYTE); } else { - av_log(anim->pFormatCtx, AV_LOG_DEBUG, "... using PTS pos\n"); + av_log(anim->pFormatCtx, AV_LOG_DEBUG, "... using PTS seek_pos\n"); ret = av_seek_frame( anim->pFormatCtx, anim->videoStream, anim->cur_key_frame_pts, AVSEEK_FLAG_BACKWARD); } @@ -1204,58 +1260,25 @@ 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_pts(anim, pts_to_search); - av_log(anim->pFormatCtx, AV_LOG_DEBUG, "NO INDEX final seek pos = %" PRId64 "\n", pos); + seek_pos = ffmpeg_get_seek_pts(anim, pts_to_search); + av_log( + anim->pFormatCtx, AV_LOG_DEBUG, "NO INDEX final seek seek_pos = %" PRId64 "\n", seek_pos); AVFormatContext *format_ctx = anim->pFormatCtx; if (format_ctx->iformat->read_seek2 || format_ctx->iformat->read_seek) { - ret = av_seek_frame(anim->pFormatCtx, anim->videoStream, pos, AVSEEK_FLAG_BACKWARD); + ret = av_seek_frame(anim->pFormatCtx, anim->videoStream, seek_pos, AVSEEK_FLAG_BACKWARD); } else { - ret = ffmpeg_generic_seek_workaround(anim, &pos, pts_to_search); - av_log(anim->pFormatCtx, AV_LOG_DEBUG, "Adjusted final seek pos = %" PRId64 "\n", pos); + ret = ffmpeg_generic_seek_workaround(anim, &seek_pos, pts_to_search); + av_log(anim->pFormatCtx, + AV_LOG_DEBUG, + "Adjusted final seek seek_pos = %" PRId64 "\n", + seek_pos); } - if (ret >= 0) { - /* Double check if we need to seek and decode all packets. */ - AVPacket *current_gop_start_packet = av_packet_alloc(); - while (av_read_frame(anim->pFormatCtx, current_gop_start_packet) >= 0) { - if (current_gop_start_packet->stream_index == anim->videoStream) { - break; - } - av_packet_unref(current_gop_start_packet); - } - int64_t gop_pts = timestamp_from_pts_or_dts(current_gop_start_packet->pts, - current_gop_start_packet->dts); - - av_packet_free(¤t_gop_start_packet); - bool same_gop = gop_pts == anim->cur_key_frame_pts; - - if (same_gop && position > anim->cur_position) { - /* Change back to our old frame position so we can simply continue decoding from there. */ - int64_t cur_pts = timestamp_from_pts_or_dts(anim->cur_packet->pts, anim->cur_packet->dts); - - if (cur_pts == gop_pts) { - /* We are already at the correct position. */ - return 0; - } - AVPacket *temp = av_packet_alloc(); - - while (av_read_frame(anim->pFormatCtx, temp) >= 0) { - int64_t temp_pts = timestamp_from_pts_or_dts(temp->pts, temp->dts); - if (temp->stream_index == anim->videoStream && temp_pts == cur_pts) { - break; - } - av_packet_unref(temp); - } - av_packet_free(&temp); - return 0; - } - - 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, anim->videoStream, pos, AVSEEK_FLAG_BACKWARD); + if (ret <= 0 && !ffmpeg_seek_buffers_need_flushing(anim, position, seek_pos)) { + return 0; } } @@ -1265,7 +1288,7 @@ static int ffmpeg_seek_to_key_frame(struct anim *anim, "FETCH: " "error while seeking to DTS = %" PRId64 " (frameno = %d, PTS = %" PRId64 "): errcode = %d\n", - pos, + seek_pos, position, pts_to_search, ret); @@ -1290,7 +1313,7 @@ static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Typ return NULL; } - av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: pos=%d\n", position); + av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: seek_pos=%d\n", position); 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); diff --git a/source/blender/imbuf/intern/divers.c b/source/blender/imbuf/intern/divers.c index 0bf50937674..588c92d748d 100644 --- a/source/blender/imbuf/intern/divers.c +++ b/source/blender/imbuf/intern/divers.c @@ -6,6 +6,7 @@ */ #include "BLI_math.h" +#include "BLI_rect.h" #include "BLI_utildefines.h" #include "IMB_filter.h" @@ -752,6 +753,61 @@ void IMB_rect_from_float(ImBuf *ibuf) ibuf->userflags &= ~IB_RECT_INVALID; } +void IMB_float_from_rect_ex(struct ImBuf *dst, + const struct ImBuf *src, + const rcti *region_to_update) +{ + BLI_assert_msg(dst->rect_float != NULL, + "Destination buffer should have a float buffer assigned."); + BLI_assert_msg(src->rect != NULL, "Source buffer should have a byte buffer assigned."); + BLI_assert_msg(dst->x == src->x, "Source and destination buffer should have the same dimension"); + BLI_assert_msg(dst->y == src->y, "Source and destination buffer should have the same dimension"); + BLI_assert_msg(dst->channels = 4, "Destination buffer should have 4 channels."); + BLI_assert_msg(region_to_update->xmin >= 0, + "Region to update should be clipped to the given buffers."); + BLI_assert_msg(region_to_update->ymin >= 0, + "Region to update should be clipped to the given buffers."); + BLI_assert_msg(region_to_update->xmax <= dst->x, + "Region to update should be clipped to the given buffers."); + BLI_assert_msg(region_to_update->ymax <= dst->y, + "Region to update should be clipped to the given buffers."); + + float *rect_float = dst->rect_float; + rect_float += (region_to_update->xmin + region_to_update->ymin * dst->x) * 4; + unsigned char *rect = (unsigned char *)src->rect; + rect += (region_to_update->xmin + region_to_update->ymin * dst->x) * 4; + const int region_width = BLI_rcti_size_x(region_to_update); + const int region_height = BLI_rcti_size_y(region_to_update); + + /* Convert byte buffer to float buffer without color or alpha conversion. */ + IMB_buffer_float_from_byte(rect_float, + rect, + IB_PROFILE_SRGB, + IB_PROFILE_SRGB, + false, + region_width, + region_height, + src->x, + dst->x); + + /* Perform color space conversion from rect color space to linear. */ + float *float_ptr = rect_float; + for (int i = 0; i < region_height; i++) { + IMB_colormanagement_colorspace_to_scene_linear( + float_ptr, region_width, 1, dst->channels, src->rect_colorspace, false); + float_ptr += 4 * dst->x; + } + + /* Perform alpha conversion. */ + if (IMB_alpha_affects_rgb(src)) { + float_ptr = rect_float; + for (int i = 0; i < region_height; i++) { + IMB_premultiply_rect_float(float_ptr, dst->channels, region_width, 1); + float_ptr += 4 * dst->x; + } + } +} + void IMB_float_from_rect(ImBuf *ibuf) { float *rect_float; @@ -775,33 +831,14 @@ void IMB_float_from_rect(ImBuf *ibuf) } ibuf->channels = 4; - } - - /* first, create float buffer in non-linear space */ - IMB_buffer_float_from_byte(rect_float, - (unsigned char *)ibuf->rect, - IB_PROFILE_SRGB, - IB_PROFILE_SRGB, - false, - ibuf->x, - ibuf->y, - ibuf->x, - ibuf->x); - - /* then make float be in linear space */ - IMB_colormanagement_colorspace_to_scene_linear( - rect_float, ibuf->x, ibuf->y, ibuf->channels, ibuf->rect_colorspace, false); - - /* byte buffer is straight alpha, float should always be premul */ - if (IMB_alpha_affects_rgb(ibuf)) { - IMB_premultiply_rect_float(rect_float, ibuf->channels, ibuf->x, ibuf->y); - } - - if (ibuf->rect_float == NULL) { ibuf->rect_float = rect_float; ibuf->mall |= IB_rectfloat; ibuf->flags |= IB_rectfloat; } + + rcti region_to_update; + BLI_rcti_init(®ion_to_update, 0, ibuf->x, 0, ibuf->y); + IMB_float_from_rect_ex(ibuf, ibuf, ®ion_to_update); } /** \} */ diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index 2cf82ca5c48..c1e00642a6d 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -478,7 +478,7 @@ struct proxy_output_ctx { AVFormatContext *of; AVStream *st; AVCodecContext *c; - AVCodec *codec; + const AVCodec *codec; struct SwsContext *sws_ctx; AVFrame *frame; int cfra; @@ -510,12 +510,9 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( rv->st = avformat_new_stream(rv->of, NULL); rv->st->id = 0; - rv->c = avcodec_alloc_context3(NULL); - rv->c->codec_type = AVMEDIA_TYPE_VIDEO; - rv->c->codec_id = AV_CODEC_ID_H264; + rv->codec = avcodec_find_encoder(AV_CODEC_ID_H264); - rv->of->oformat->video_codec = rv->c->codec_id; - rv->codec = avcodec_find_encoder(rv->c->codec_id); + rv->c = avcodec_alloc_context3(rv->codec); if (!rv->codec) { fprintf(stderr, @@ -527,8 +524,6 @@ static struct proxy_output_ctx *alloc_proxy_output_ffmpeg( return NULL; } - avcodec_get_context_defaults3(rv->c, rv->codec); - rv->c->width = width; rv->c->height = height; rv->c->gop_size = 10; @@ -779,7 +774,7 @@ typedef struct FFmpegIndexBuilderContext { AVFormatContext *iFormatCtx; AVCodecContext *iCodecCtx; - AVCodec *iCodec; + const AVCodec *iCodec; AVStream *iStream; int videoStream; @@ -1167,7 +1162,7 @@ static int indexer_performance_get_max_gop_size(FFmpegIndexBuilderContext *conte } /* Assess scrubbing performance of provided file. This function is not meant to be very exact. - * It compares number number of frames decoded in reasonable time with largest detected GOP size. + * It compares number of frames decoded in reasonable time with largest detected GOP size. * Because seeking happens in single GOP, it means, that maximum seek time can be detected this * way. * Since proxies use GOP size of 10 frames, skip building if detected GOP size is less or diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp index a47009e3abd..418a4724c00 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp @@ -16,30 +16,46 @@ #include <stdexcept> #include <string> -#include <Iex.h> -#include <ImathBox.h> -#include <ImfArray.h> -#include <ImfChannelList.h> -#include <ImfCompression.h> -#include <ImfCompressionAttribute.h> -#include <ImfIO.h> -#include <ImfInputFile.h> -#include <ImfOutputFile.h> -#include <ImfPixelType.h> -#include <ImfStandardAttributes.h> -#include <ImfStringAttribute.h> -#include <ImfVersion.h> -#include <half.h> +/* The OpenEXR version can reliably be found in this header file from OpenEXR, + * for both 2.x and 3.x: + */ +#include <OpenEXR/OpenEXRConfig.h> +#define COMBINED_OPENEXR_VERSION \ + ((10000 * OPENEXR_VERSION_MAJOR) + (100 * OPENEXR_VERSION_MINOR) + OPENEXR_VERSION_PATCH) + +#if COMBINED_OPENEXR_VERSION >= 20599 +/* >=2.5.99 -> OpenEXR >=3.0 */ +# include <Imath/half.h> +# include <OpenEXR/ImfFrameBuffer.h> +# define exr_file_offset_t uint64_t +#else +/* OpenEXR 2.x, use the old locations. */ +# include <OpenEXR/half.h> +# define exr_file_offset_t Int64 +#endif + +#include <OpenEXR/Iex.h> +#include <OpenEXR/ImfArray.h> +#include <OpenEXR/ImfChannelList.h> +#include <OpenEXR/ImfCompression.h> +#include <OpenEXR/ImfCompressionAttribute.h> +#include <OpenEXR/ImfIO.h> +#include <OpenEXR/ImfInputFile.h> +#include <OpenEXR/ImfOutputFile.h> +#include <OpenEXR/ImfPixelType.h> +#include <OpenEXR/ImfStandardAttributes.h> +#include <OpenEXR/ImfStringAttribute.h> +#include <OpenEXR/ImfVersion.h> /* multiview/multipart */ -#include <ImfInputPart.h> -#include <ImfMultiPartInputFile.h> -#include <ImfMultiPartOutputFile.h> -#include <ImfMultiView.h> -#include <ImfOutputPart.h> -#include <ImfPartHelper.h> -#include <ImfPartType.h> -#include <ImfTiledOutputPart.h> +#include <OpenEXR/ImfInputPart.h> +#include <OpenEXR/ImfMultiPartInputFile.h> +#include <OpenEXR/ImfMultiPartOutputFile.h> +#include <OpenEXR/ImfMultiView.h> +#include <OpenEXR/ImfOutputPart.h> +#include <OpenEXR/ImfPartHelper.h> +#include <OpenEXR/ImfPartType.h> +#include <OpenEXR/ImfTiledOutputPart.h> #include "DNA_scene_types.h" /* For OpenEXR compression constants */ @@ -115,12 +131,12 @@ class IMemStream : public Imf::IStream { return false; } - Int64 tellg() override + exr_file_offset_t tellg() override { return _exrpos; } - void seekg(Int64 pos) override + void seekg(exr_file_offset_t pos) override { _exrpos = pos; } @@ -130,8 +146,8 @@ class IMemStream : public Imf::IStream { } private: - Int64 _exrpos; - Int64 _exrsize; + exr_file_offset_t _exrpos; + exr_file_offset_t _exrsize; unsigned char *_exrbuf; }; @@ -166,12 +182,12 @@ class IFileStream : public Imf::IStream { return check_error(); } - Int64 tellg() override + exr_file_offset_t tellg() override { return std::streamoff(ifs.tellg()); } - void seekg(Int64 pos) override + void seekg(exr_file_offset_t pos) override { ifs.seekg(pos); check_error(); @@ -215,19 +231,19 @@ class OMemStream : public OStream { ibuf->encodedsize += n; } - Int64 tellp() override + exr_file_offset_t tellp() override { return offset; } - void seekp(Int64 pos) override + void seekp(exr_file_offset_t pos) override { offset = pos; ensure_size(offset); } private: - void ensure_size(Int64 size) + void ensure_size(exr_file_offset_t size) { /* if buffer is too small increase it. */ while (size > ibuf->encodedbuffersize) { @@ -238,7 +254,7 @@ class OMemStream : public OStream { } ImBuf *ibuf; - Int64 offset; + exr_file_offset_t offset; }; /* File Output Stream */ @@ -268,12 +284,12 @@ class OFileStream : public OStream { check_error(); } - Int64 tellp() override + exr_file_offset_t tellp() override { return std::streamoff(ofs.tellp()); } - void seekp(Int64 pos) override + void seekp(exr_file_offset_t pos) override { ofs.seekp(pos); check_error(); diff --git a/source/blender/imbuf/intern/util.c b/source/blender/imbuf/intern/util.c index cabd6070400..241f1a736f4 100644 --- a/source/blender/imbuf/intern/util.c +++ b/source/blender/imbuf/intern/util.c @@ -250,7 +250,7 @@ static int isffmpeg(const char *filepath) AVFormatContext *pFormatCtx = NULL; unsigned int i; int videoStream; - AVCodec *pCodec; + const AVCodec *pCodec; if (BLI_path_extension_check_n(filepath, ".swf", diff --git a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc index 4723b89de08..d33adfdb4b9 100644 --- a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc +++ b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc @@ -190,7 +190,7 @@ ABCAbstractWriter *ABCHierarchyIterator::create_data_writer_for_object_type( return new ABCMeshWriter(writer_args); case OB_CAMERA: return new ABCCameraWriter(writer_args); - case OB_CURVE: + case OB_CURVES_LEGACY: if (params_.curves_as_mesh) { return new ABCCurveMeshWriter(writer_args); } diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.cc b/source/blender/io/alembic/exporter/abc_writer_mesh.cc index 67e9846030a..11693eeb4de 100644 --- a/source/blender/io/alembic/exporter/abc_writer_mesh.cc +++ b/source/blender/io/alembic/exporter/abc_writer_mesh.cc @@ -309,7 +309,7 @@ void ABCGenericMeshWriter::write_subd(HierarchyContext &context, struct Mesh *me } if (args_.export_params->orcos) { - write_generated_coordinates(abc_poly_mesh_schema_.getArbGeomParams(), m_custom_data_config); + write_generated_coordinates(abc_subdiv_schema_.getArbGeomParams(), m_custom_data_config); } if (!edge_crease_indices.empty()) { diff --git a/source/blender/io/alembic/exporter/abc_writer_nurbs.cc b/source/blender/io/alembic/exporter/abc_writer_nurbs.cc index 5e307d59ed4..89ed7c5ce06 100644 --- a/source/blender/io/alembic/exporter/abc_writer_nurbs.cc +++ b/source/blender/io/alembic/exporter/abc_writer_nurbs.cc @@ -84,7 +84,7 @@ bool ABCNurbsWriter::check_is_animated(const HierarchyContext &context) const bool ABCNurbsWriter::is_supported(const HierarchyContext *context) const { - return ELEM(context->object->type, OB_SURF, OB_CURVE); + return ELEM(context->object->type, OB_SURF, OB_CURVES_LEGACY); } static void get_knots(std::vector<float> &knots, const int num_knots, float *nu_knots) diff --git a/source/blender/io/alembic/intern/abc_reader_curves.cc b/source/blender/io/alembic/intern/abc_reader_curves.cc index a6400dc6f6c..d8859acdf5f 100644 --- a/source/blender/io/alembic/intern/abc_reader_curves.cc +++ b/source/blender/io/alembic/intern/abc_reader_curves.cc @@ -66,7 +66,7 @@ bool AbcCurveReader::accepts_object_type( return false; } - if (ob->type != OB_CURVE) { + if (ob->type != OB_CURVES_LEGACY) { *err_str = "Object type mismatch, Alembic object path points to Curves."; return false; } @@ -76,7 +76,7 @@ bool AbcCurveReader::accepts_object_type( void AbcCurveReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) { - Curve *cu = BKE_curve_add(bmain, m_data_name.c_str(), OB_CURVE); + Curve *cu = BKE_curve_add(bmain, m_data_name.c_str(), OB_CURVES_LEGACY); cu->flag |= CU_3D; cu->actvert = CU_ACT_NONE; @@ -91,7 +91,7 @@ void AbcCurveReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSele } } - m_object = BKE_object_add_only_object(bmain, OB_CURVE, m_object_name.c_str()); + m_object = BKE_object_add_only_object(bmain, OB_CURVES_LEGACY, m_object_name.c_str()); m_object->data = cu; read_curve_sample(cu, m_curves_schema, sample_sel); diff --git a/source/blender/io/alembic/intern/abc_reader_nurbs.cc b/source/blender/io/alembic/intern/abc_reader_nurbs.cc index b744106ff95..78f9c0a85b4 100644 --- a/source/blender/io/alembic/intern/abc_reader_nurbs.cc +++ b/source/blender/io/alembic/intern/abc_reader_nurbs.cc @@ -69,7 +69,7 @@ bool AbcNurbsReader::accepts_object_type( return false; } - if (ob->type != OB_CURVE) { + if (ob->type != OB_CURVES_LEGACY) { *err_str = "Object type mismatch, Alembic object path points to NURBS."; return false; } diff --git a/source/blender/io/collada/CMakeLists.txt b/source/blender/io/collada/CMakeLists.txt index 34949da622a..3289a7c6e66 100644 --- a/source/blender/io/collada/CMakeLists.txt +++ b/source/blender/io/collada/CMakeLists.txt @@ -14,6 +14,10 @@ if(OPENCOLLADA_ANIMATION_CLIP) add_definitions(-DWITH_OPENCOLLADA_ANIMATION_CLIP) endif() +# In cmake version 3.21 and up, we can instead use the NO_CACHE option for +# find_file so we don't need to clear it from the cache here. +unset(OPENCOLLADA_ANIMATION_CLIP CACHE) + set(INC . ../../blenkernel diff --git a/source/blender/io/gpencil/intern/gpencil_io_base.cc b/source/blender/io/gpencil/intern/gpencil_io_base.cc index 9d4311bd693..9379e72bdd9 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_base.cc +++ b/source/blender/io/gpencil/intern/gpencil_io_base.cc @@ -158,7 +158,7 @@ void GpencilIO::create_object_list() float zdepth = 0; if (rv3d_) { if (rv3d_->is_persp) { - zdepth = ED_view3d_calc_zfac(rv3d_, object->obmat[3], nullptr); + zdepth = ED_view3d_calc_zfac(rv3d_, object->obmat[3]); } else { zdepth = -dot_v3v3(rv3d_->viewinv[2], object->obmat[3]); diff --git a/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc b/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc index 3c4676beb75..cc3eab02e07 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc +++ b/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc @@ -196,7 +196,7 @@ void GpencilExporterPDF::export_gpencil_layers() /* Sample stroke. */ if (params_.stroke_sample > 0.0f) { - BKE_gpencil_stroke_sample(gpd_eval, gps_perimeter, params_.stroke_sample, false); + BKE_gpencil_stroke_sample(gpd_eval, gps_perimeter, params_.stroke_sample, false, 0); } export_stroke_to_polyline(gpl, gps_perimeter, is_stroke, false, false); diff --git a/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc b/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc index d21af4c09eb..f8d30546e39 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc +++ b/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc @@ -221,7 +221,7 @@ void GpencilExporterSVG::export_gpencil_layers() /* Sample stroke. */ if (params_.stroke_sample > 0.0f) { - BKE_gpencil_stroke_sample(gpd_eval, gps_perimeter, params_.stroke_sample, false); + BKE_gpencil_stroke_sample(gpd_eval, gps_perimeter, params_.stroke_sample, false, 0); } export_stroke_to_path(gpl, gps_perimeter, node_gpl, false); diff --git a/source/blender/io/usd/intern/usd_capi_import.cc b/source/blender/io/usd/intern/usd_capi_import.cc index 1f384bdf127..6186e2bb61f 100644 --- a/source/blender/io/usd/intern/usd_capi_import.cc +++ b/source/blender/io/usd/intern/usd_capi_import.cc @@ -502,6 +502,9 @@ CacheArchiveHandle *USD_create_handle(struct Main * /*bmain*/, const char *filename, ListBase *object_paths) { + /* Must call this so that USD file format plugins are loaded. */ + ensure_usd_plugin_path_registered(); + pxr::UsdStageRefPtr stage = pxr::UsdStage::Open(filename); if (!stage) { diff --git a/source/blender/io/usd/intern/usd_hierarchy_iterator.cc b/source/blender/io/usd/intern/usd_hierarchy_iterator.cc index a86deb7c9f0..7a0d896fb3e 100644 --- a/source/blender/io/usd/intern/usd_hierarchy_iterator.cc +++ b/source/blender/io/usd/intern/usd_hierarchy_iterator.cc @@ -95,7 +95,7 @@ AbstractHierarchyWriter *USDHierarchyIterator::create_data_writer(const Hierarch break; case OB_EMPTY: - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: case OB_FONT: case OB_SPEAKER: diff --git a/source/blender/io/usd/intern/usd_reader_curve.cc b/source/blender/io/usd/intern/usd_reader_curve.cc index e331030164e..0d3c2feb8f3 100644 --- a/source/blender/io/usd/intern/usd_reader_curve.cc +++ b/source/blender/io/usd/intern/usd_reader_curve.cc @@ -26,13 +26,13 @@ namespace blender::io::usd { void USDCurvesReader::create_object(Main *bmain, const double /* motionSampleTime */) { - curve_ = BKE_curve_add(bmain, name_.c_str(), OB_CURVE); + curve_ = BKE_curve_add(bmain, name_.c_str(), OB_CURVES_LEGACY); curve_->flag |= CU_3D; curve_->actvert = CU_ACT_NONE; curve_->resolu = 2; - object_ = BKE_object_add_only_object(bmain, OB_CURVE, name_.c_str()); + object_ = BKE_object_add_only_object(bmain, OB_CURVES_LEGACY, name_.c_str()); object_->data = curve_; } diff --git a/source/blender/io/usd/intern/usd_reader_nurbs.cc b/source/blender/io/usd/intern/usd_reader_nurbs.cc index c7fd788e072..d0a5dc1c4b4 100644 --- a/source/blender/io/usd/intern/usd_reader_nurbs.cc +++ b/source/blender/io/usd/intern/usd_reader_nurbs.cc @@ -43,13 +43,13 @@ namespace blender::io::usd { void USDNurbsReader::create_object(Main *bmain, const double /* motionSampleTime */) { - curve_ = BKE_curve_add(bmain, name_.c_str(), OB_CURVE); + curve_ = BKE_curve_add(bmain, name_.c_str(), OB_CURVES_LEGACY); curve_->flag |= CU_3D; curve_->actvert = CU_ACT_NONE; curve_->resolu = 2; - object_ = BKE_object_add_only_object(bmain, OB_CURVE, name_.c_str()); + object_ = BKE_object_add_only_object(bmain, OB_CURVES_LEGACY, name_.c_str()); object_->data = curve_; } diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_io.hh b/source/blender/io/wavefront_obj/exporter/obj_export_io.hh index 7016dff2a29..3bee93b36a5 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_io.hh +++ b/source/blender/io/wavefront_obj/exporter/obj_export_io.hh @@ -310,7 +310,7 @@ class FormatHandler : NonCopyable, NonMovable { /** * Example invocation: `writer->write<eMTLSyntaxElement::newmtl>("foo")`. * - * \param key Must match what the instance's filetype expects; i.e., `eMTLSyntaxElement` for + * \param key: Must match what the instance's filetype expects; i.e., `eMTLSyntaxElement` for * `eFileType::MTL`. */ template<typename FileTypeTraits<filetype>::SyntaxType key, typename... T> diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh index 9b9c8483464..5b11c85b7a4 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh +++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh @@ -189,7 +189,7 @@ class OBJMesh : NonCopyable { } /** * Calculate a polygon's polygon/loop normal indices. - * \param poly_index Index of the polygon to calculate indices for. + * \param poly_index: Index of the polygon to calculate indices for. * \return Vector of normal indices, aligned with vertices of polygon. */ Vector<int> calc_poly_normal_indices(int poly_index) const; diff --git a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc index 9ea75e6eb4c..2cef5192337 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc @@ -93,7 +93,7 @@ filter_supported_objects(Depsgraph *depsgraph, const OBJExportParams &export_par case OB_MESH: r_exportable_meshes.append(std::make_unique<OBJMesh>(depsgraph, export_params, object)); break; - case OB_CURVE: { + case OB_CURVES_LEGACY: { Curve *curve = static_cast<Curve *>(object->data); Nurb *nurb{static_cast<Nurb *>(curve->nurb.first)}; if (!nurb) { diff --git a/source/blender/io/wavefront_obj/exporter/obj_exporter.hh b/source/blender/io/wavefront_obj/exporter/obj_exporter.hh index 9f9ec5a6083..676b1f3598c 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_exporter.hh +++ b/source/blender/io/wavefront_obj/exporter/obj_exporter.hh @@ -64,11 +64,12 @@ void export_frame(Depsgraph *depsgraph, * Find the objects to be exported in the `view_layer` of the dependency graph`depsgraph`, * and return them in vectors `unique_ptr`s of `OBJMesh` and `OBJCurve`. * If `export_params.export_selected_objects` is set, then only selected objects are to be - * exported, else all objects are to be exported. But only objects of type `OB_MESH`, `OB_CURVE`, - * and `OB_SURF` are supported; the rest will be ignored. If `export_params.export_curves_as_nurbs` - * is set, then curves of type `CU_NURBS` are exported in curve form in the .obj file, otherwise - * they are converted to mesh and returned in the `OBJMesh` vector. All other exportable types are - * always converted to mesh and returned in the `OBJMesh` vector. + * exported, else all objects are to be exported. But only objects of type `OB_MESH`, + * `OB_CURVES_LEGACY`, and `OB_SURF` are supported; the rest will be ignored. If + * `export_params.export_curves_as_nurbs` is set, then curves of type `CU_NURBS` are exported in + * curve form in the .obj file, otherwise they are converted to mesh and returned in the `OBJMesh` + * vector. All other exportable types are always converted to mesh and returned in the `OBJMesh` + * vector. */ std::pair<Vector<std::unique_ptr<OBJMesh>>, Vector<std::unique_ptr<OBJCurve>>> filter_supported_objects(Depsgraph *depsgraph, const OBJExportParams &export_params); diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index c045082d569..011f3618e15 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -330,7 +330,7 @@ enum { /* 2 characters for ID code and 64 for actual name */ #define MAX_ID_NAME 66 -/* ID_Runtime.remapping_status */ +/* ID_Runtime_Remap.status */ enum { /** new_id is directly linked in current .blend. */ ID_REMAP_IS_LINKED_DIRECT = 1 << 0, @@ -879,7 +879,7 @@ typedef enum IDRecalcFlag { #define FILTER_ID_AR (1ULL << 1) #define FILTER_ID_BR (1ULL << 2) #define FILTER_ID_CA (1ULL << 3) -#define FILTER_ID_CU (1ULL << 4) +#define FILTER_ID_CU_LEGACY (1ULL << 4) #define FILTER_ID_GD (1ULL << 5) #define FILTER_ID_GR (1ULL << 6) #define FILTER_ID_IM (1ULL << 7) @@ -912,12 +912,12 @@ typedef enum IDRecalcFlag { #define FILTER_ID_SIM (1ULL << 35) #define FILTER_ID_ALL \ - (FILTER_ID_AC | FILTER_ID_AR | FILTER_ID_BR | FILTER_ID_CA | FILTER_ID_CU | FILTER_ID_GD | \ - FILTER_ID_GR | FILTER_ID_IM | FILTER_ID_LA | FILTER_ID_LS | FILTER_ID_LT | FILTER_ID_MA | \ - FILTER_ID_MB | FILTER_ID_MC | FILTER_ID_ME | FILTER_ID_MSK | FILTER_ID_NT | FILTER_ID_OB | \ - FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_SCE | FILTER_ID_SPK | FILTER_ID_SO | \ - FILTER_ID_TE | FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_WO | FILTER_ID_CF | FILTER_ID_WS | \ - FILTER_ID_LP | FILTER_ID_CV | FILTER_ID_PT | FILTER_ID_VO | FILTER_ID_SIM) + (FILTER_ID_AC | FILTER_ID_AR | FILTER_ID_BR | FILTER_ID_CA | FILTER_ID_CU_LEGACY | \ + FILTER_ID_GD | FILTER_ID_GR | FILTER_ID_IM | FILTER_ID_LA | FILTER_ID_LS | FILTER_ID_LT | \ + FILTER_ID_MA | FILTER_ID_MB | FILTER_ID_MC | FILTER_ID_ME | FILTER_ID_MSK | FILTER_ID_NT | \ + FILTER_ID_OB | FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_SCE | FILTER_ID_SPK | \ + FILTER_ID_SO | FILTER_ID_TE | FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_WO | FILTER_ID_CF | \ + FILTER_ID_WS | FILTER_ID_LP | FILTER_ID_CV | FILTER_ID_PT | FILTER_ID_VO | FILTER_ID_SIM) /** * This enum defines the index assigned to each type of IDs in the array returned by @@ -998,7 +998,7 @@ enum { /* Object data types. */ INDEX_ID_AR, INDEX_ID_ME, - INDEX_ID_CU, + INDEX_ID_CU_LEGACY, INDEX_ID_MB, INDEX_ID_CV, INDEX_ID_PT, diff --git a/source/blender/makesdna/DNA_ID_enums.h b/source/blender/makesdna/DNA_ID_enums.h index a6f950517eb..b0ca13615b8 100644 --- a/source/blender/makesdna/DNA_ID_enums.h +++ b/source/blender/makesdna/DNA_ID_enums.h @@ -40,46 +40,46 @@ enum eIconSizes { * and the first 2 bytes of #ID.name (for runtime checks, see #GS macro). */ typedef enum ID_Type { - ID_SCE = MAKE_ID2('S', 'C'), /* Scene */ - ID_LI = MAKE_ID2('L', 'I'), /* Library */ - ID_OB = MAKE_ID2('O', 'B'), /* Object */ - ID_ME = MAKE_ID2('M', 'E'), /* Mesh */ - ID_CU = MAKE_ID2('C', 'U'), /* Curve */ - ID_MB = MAKE_ID2('M', 'B'), /* MetaBall */ - ID_MA = MAKE_ID2('M', 'A'), /* Material */ - ID_TE = MAKE_ID2('T', 'E'), /* Tex (Texture) */ - ID_IM = MAKE_ID2('I', 'M'), /* Image */ - ID_LT = MAKE_ID2('L', 'T'), /* Lattice */ - ID_LA = MAKE_ID2('L', 'A'), /* Light */ - ID_CA = MAKE_ID2('C', 'A'), /* Camera */ - ID_IP = MAKE_ID2('I', 'P'), /* Ipo (depreciated, replaced by FCurves) */ - ID_KE = MAKE_ID2('K', 'E'), /* Key (shape key) */ - ID_WO = MAKE_ID2('W', 'O'), /* World */ - ID_SCR = MAKE_ID2('S', 'R'), /* Screen */ - ID_VF = MAKE_ID2('V', 'F'), /* VFont (Vector Font) */ - ID_TXT = MAKE_ID2('T', 'X'), /* Text */ - ID_SPK = MAKE_ID2('S', 'K'), /* Speaker */ - ID_SO = MAKE_ID2('S', 'O'), /* Sound */ - ID_GR = MAKE_ID2('G', 'R'), /* Collection */ - ID_AR = MAKE_ID2('A', 'R'), /* bArmature */ - ID_AC = MAKE_ID2('A', 'C'), /* bAction */ - ID_NT = MAKE_ID2('N', 'T'), /* bNodeTree */ - ID_BR = MAKE_ID2('B', 'R'), /* Brush */ - ID_PA = MAKE_ID2('P', 'A'), /* ParticleSettings */ - ID_GD = MAKE_ID2('G', 'D'), /* bGPdata, (Grease Pencil) */ - ID_WM = MAKE_ID2('W', 'M'), /* WindowManager */ - ID_MC = MAKE_ID2('M', 'C'), /* MovieClip */ - ID_MSK = MAKE_ID2('M', 'S'), /* Mask */ - ID_LS = MAKE_ID2('L', 'S'), /* FreestyleLineStyle */ - ID_PAL = MAKE_ID2('P', 'L'), /* Palette */ - ID_PC = MAKE_ID2('P', 'C'), /* PaintCurve */ - ID_CF = MAKE_ID2('C', 'F'), /* CacheFile */ - ID_WS = MAKE_ID2('W', 'S'), /* WorkSpace */ - ID_LP = MAKE_ID2('L', 'P'), /* LightProbe */ - ID_CV = MAKE_ID2('C', 'V'), /* Curves */ - ID_PT = MAKE_ID2('P', 'T'), /* PointCloud */ - ID_VO = MAKE_ID2('V', 'O'), /* Volume */ - ID_SIM = MAKE_ID2('S', 'I'), /* Simulation (geometry node groups) */ + ID_SCE = MAKE_ID2('S', 'C'), /* Scene */ + ID_LI = MAKE_ID2('L', 'I'), /* Library */ + ID_OB = MAKE_ID2('O', 'B'), /* Object */ + ID_ME = MAKE_ID2('M', 'E'), /* Mesh */ + ID_CU_LEGACY = MAKE_ID2('C', 'U'), /* Curve. ID_CV should be used in the future (see T95355). */ + ID_MB = MAKE_ID2('M', 'B'), /* MetaBall */ + ID_MA = MAKE_ID2('M', 'A'), /* Material */ + ID_TE = MAKE_ID2('T', 'E'), /* Tex (Texture) */ + ID_IM = MAKE_ID2('I', 'M'), /* Image */ + ID_LT = MAKE_ID2('L', 'T'), /* Lattice */ + ID_LA = MAKE_ID2('L', 'A'), /* Light */ + ID_CA = MAKE_ID2('C', 'A'), /* Camera */ + ID_IP = MAKE_ID2('I', 'P'), /* Ipo (depreciated, replaced by FCurves) */ + ID_KE = MAKE_ID2('K', 'E'), /* Key (shape key) */ + ID_WO = MAKE_ID2('W', 'O'), /* World */ + ID_SCR = MAKE_ID2('S', 'R'), /* Screen */ + ID_VF = MAKE_ID2('V', 'F'), /* VFont (Vector Font) */ + ID_TXT = MAKE_ID2('T', 'X'), /* Text */ + ID_SPK = MAKE_ID2('S', 'K'), /* Speaker */ + ID_SO = MAKE_ID2('S', 'O'), /* Sound */ + ID_GR = MAKE_ID2('G', 'R'), /* Collection */ + ID_AR = MAKE_ID2('A', 'R'), /* bArmature */ + ID_AC = MAKE_ID2('A', 'C'), /* bAction */ + ID_NT = MAKE_ID2('N', 'T'), /* bNodeTree */ + ID_BR = MAKE_ID2('B', 'R'), /* Brush */ + ID_PA = MAKE_ID2('P', 'A'), /* ParticleSettings */ + ID_GD = MAKE_ID2('G', 'D'), /* bGPdata, (Grease Pencil) */ + ID_WM = MAKE_ID2('W', 'M'), /* WindowManager */ + ID_MC = MAKE_ID2('M', 'C'), /* MovieClip */ + ID_MSK = MAKE_ID2('M', 'S'), /* Mask */ + ID_LS = MAKE_ID2('L', 'S'), /* FreestyleLineStyle */ + ID_PAL = MAKE_ID2('P', 'L'), /* Palette */ + ID_PC = MAKE_ID2('P', 'C'), /* PaintCurve */ + ID_CF = MAKE_ID2('C', 'F'), /* CacheFile */ + ID_WS = MAKE_ID2('W', 'S'), /* WorkSpace */ + ID_LP = MAKE_ID2('L', 'P'), /* LightProbe */ + ID_CV = MAKE_ID2('C', 'V'), /* Curves */ + ID_PT = MAKE_ID2('P', 'T'), /* PointCloud */ + ID_VO = MAKE_ID2('V', 'O'), /* Volume */ + ID_SIM = MAKE_ID2('S', 'I'), /* Simulation (geometry node groups) */ } ID_Type; /* Only used as 'placeholder' in .blend files for directly linked data-blocks. */ diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h index 2a6b86711ab..fa0898e6ea5 100644 --- a/source/blender/makesdna/DNA_action_types.h +++ b/source/blender/makesdna/DNA_action_types.h @@ -104,12 +104,14 @@ typedef struct bAnimVizSettings { short path_type; /** Number of frames between points indicated on the paths. */ short path_step; + /** #eMotionPath_Ranges. */ + short path_range; /** #eMotionPaths_ViewFlag. */ short path_viewflag; /** #eMotionPaths_BakeFlag. */ short path_bakeflag; - char _pad[6]; + char _pad[4]; /** Start and end frames of path-calculation range. */ int path_sf, path_ef; @@ -131,6 +133,14 @@ typedef enum eMotionPaths_Types { MOTIONPATH_TYPE_ACFRA = 1, } eMotionPath_Types; +/* bAnimVizSettings->path_range */ +typedef enum eMotionPath_Ranges { + /* Default is scene */ + MOTIONPATH_RANGE_SCENE = 0, + MOTIONPATH_RANGE_KEYS_SELECTED = 1, + MOTIONPATH_RANGE_KEYS_ALL = 2, +} eMotionPath_Ranges; + /* bAnimVizSettings->path_viewflag */ typedef enum eMotionPaths_ViewFlag { /* show frames on path */ diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h index 0667e508a82..bca177f6d7a 100644 --- a/source/blender/makesdna/DNA_brush_enums.h +++ b/source/blender/makesdna/DNA_brush_enums.h @@ -455,6 +455,12 @@ typedef enum eBrushUVSculptTool { UV_SCULPT_TOOL_PINCH = 2, } eBrushUVSculptTool; +/* Brush.curves_sculpt_tool. */ +typedef enum eBrushCurvesSculptTool { + CURVES_SCULPT_TOOL_TEST1 = 0, + CURVES_SCULPT_TOOL_TEST2 = 1, +} eBrushCurvesSculptTool; + /** When #BRUSH_ACCUMULATE is used */ #define SCULPT_TOOL_HAS_ACCUMULATE(t) \ ELEM(t, \ diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 77c49393029..fe80220b1dd 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -256,7 +256,9 @@ typedef struct Brush { char gpencil_sculpt_tool; /** Active grease pencil weight tool. */ char gpencil_weight_tool; - char _pad1[6]; + /** Active curves sculpt tool. */ + char curves_sculpt_tool; + char _pad1[5]; float autosmooth_factor; diff --git a/source/blender/makesdna/DNA_curve_types.h b/source/blender/makesdna/DNA_curve_types.h index 14a06f197bf..66206bcfddd 100644 --- a/source/blender/makesdna/DNA_curve_types.h +++ b/source/blender/makesdna/DNA_curve_types.h @@ -306,14 +306,6 @@ enum { CU_AUTOSPACE_EVALUATED = 2, }; -#if 0 /* Moved to overlay options in 2.8 */ -/* Curve.drawflag */ -enum { - CU_HIDE_HANDLES = 1 << 0, - CU_HIDE_NORMALS = 1 << 1, -}; -#endif - /* Curve.flag */ enum { CU_3D = 1 << 0, diff --git a/source/blender/makesdna/DNA_curves_types.h b/source/blender/makesdna/DNA_curves_types.h index 03a587c896b..98d2aa4b295 100644 --- a/source/blender/makesdna/DNA_curves_types.h +++ b/source/blender/makesdna/DNA_curves_types.h @@ -13,6 +13,33 @@ extern "C" { #endif +#ifdef __cplusplus +namespace blender::bke { +class CurvesGeometryRuntime; +} // namespace blender::bke +using CurvesGeometryRuntimeHandle = blender::bke::CurvesGeometryRuntime; +#else +typedef struct CurvesGeometryRuntimeHandle CurvesGeometryRuntimeHandle; +#endif + +typedef enum CurveType { + CURVE_TYPE_CATMULL_ROM = 0, + CURVE_TYPE_POLY = 1, + CURVE_TYPE_BEZIER = 2, + CURVE_TYPE_NURBS = 3, +} CurveType; + +typedef enum HandleType { + /** The handle can be moved anywhere, and doesn't influence the point's other handle. */ + BEZIER_HANDLE_FREE = 0, + /** The location is automatically calculated to be smooth. */ + BEZIER_HANDLE_AUTO = 1, + /** The location is calculated to point to the next/previous control point. */ + BEZIER_HANDLE_VECTOR = 2, + /** The location is constrained to point in the opposite direction as the other handle. */ + BEZIER_HANDLE_ALIGN = 3, +} HandleType; + /** * A reusable data structure for geometry consisting of many curves. All control point data is * stored contiguously for better efficiency. Data for each curve is stored as a slice of the @@ -34,13 +61,19 @@ typedef struct CurvesGeometry { float *radius; /** + * The type of each curve. #CurveType. + * \note This data is owned by #curve_data. + */ + int8_t *curve_type; + + /** * The start index of each curve in the point data. The size of each curve can be calculated by * subtracting the offset from the next offset. That is valid even for the last curve because * this array is allocated with a length one larger than the number of splines. * * \note This is *not* stored in #CustomData because its size is one larger than #curve_data. */ - int *offsets; + int *curve_offsets; /** * All attributes stored on control points (#ATTR_DOMAIN_POINT). @@ -60,6 +93,11 @@ typedef struct CurvesGeometry { * The number of curves in the data-block. */ int curve_size; + + /** + * Runtime data for curves, stored as a pointer to allow defining this as a C++ class. + */ + CurvesGeometryRuntimeHandle *runtime; } CurvesGeometry; typedef struct Curves { @@ -77,6 +115,15 @@ typedef struct Curves { short totcol; short _pad2[3]; + /** + * Used as base mesh when curves represent e.g. hair or fur. This surface is used in edit modes. + * When set, the curves will have attributes that indicate a position on this surface. This is + * used for deforming the curves when the surface is deformed dynamically. + * + * This is expected to be a mesh object. + */ + struct Object *surface; + /* Draw Cache. */ void *batch_cache; } Curves; diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h index 23406033126..2f9f63cb966 100644 --- a/source/blender/makesdna/DNA_customdata_types.h +++ b/source/blender/makesdna/DNA_customdata_types.h @@ -98,6 +98,10 @@ typedef enum CustomDataType { CD_MTFACE = 5, CD_MCOL = 6, CD_ORIGINDEX = 7, + /** + * Used for derived face corner normals on mesh `ldata`, since currently they are not computed + * lazily. Derived vertex and polygon normals are stored in #Mesh_Runtime. + */ CD_NORMAL = 8, CD_FACEMAP = 9, /* exclusive face group, each face can only be part of one */ CD_PROP_FLOAT = 10, diff --git a/source/blender/makesdna/DNA_fluid_types.h b/source/blender/makesdna/DNA_fluid_types.h index 953a15f7502..11780d99af8 100644 --- a/source/blender/makesdna/DNA_fluid_types.h +++ b/source/blender/makesdna/DNA_fluid_types.h @@ -611,7 +611,7 @@ typedef struct FluidDomainSettings { /* Fluid guiding options. */ float guide_alpha; /* Guiding weight scalar (determines strength). */ - int guide_beta; /* Guiding blur radius (affects size of vortices vortices). */ + int guide_beta; /* Guiding blur radius (affects size of vortices). */ float guide_vel_factor; /* Multiply guiding velocity by this factor. */ int guide_res[3]; /* Res for velocity guide grids - independent from base res. */ short guide_source; diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index 64099de5a5e..e30dd4e1c8f 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -649,9 +649,10 @@ typedef struct SimplifyGpencilModifierData { int layer_pass; /** Sample length */ float length; + /** Sample sharp threshold */ + float sharp_threshold; /** Merge distance */ float distance; - char _pad[4]; } SimplifyGpencilModifierData; typedef enum eSimplifyGpencil_Flag { diff --git a/source/blender/makesdna/DNA_ipo_types.h b/source/blender/makesdna/DNA_ipo_types.h index f40fb55e72e..70ee7c99d01 100644 --- a/source/blender/makesdna/DNA_ipo_types.h +++ b/source/blender/makesdna/DNA_ipo_types.h @@ -272,7 +272,7 @@ typedef struct Ipo { #define SEQ_FAC_SPEED 2 #define SEQ_FAC_OPACITY 3 -/* ********* Curve (ID_CU) *********** */ +/* ********* Curve (ID_CU_LEGACY) *********** */ #define CU_TOTIPO 1 #define CU_TOTNAM 1 diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index c566ad93335..d8a853681fd 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -116,27 +116,25 @@ typedef struct Mesh_Runtime { */ char wrapper_type_finalize; + int subsurf_resolution; /** * Settings for lazily evaluating the subdivision on the CPU if needed. These are * set in the modifier when GPU subdivision can be performed. */ char subsurf_apply_render; char subsurf_use_optimal_display; - char _pad[2]; - int subsurf_resolution; - - void *_pad2; /** - * Used to mark when derived data needs to be recalculated for a certain layer. - * Currently only normals. + * Caches for lazily computed vertex and polygon normals. These are stored here rather than in + * #CustomData because they can be calculated on a const mesh, and adding custom data layers on a + * const mesh is not thread-safe. */ + char vert_normals_dirty; + char poly_normals_dirty; + float (*vert_normals)[3]; + float (*poly_normals)[3]; - int64_t cd_dirty_vert; - int64_t cd_dirty_edge; - int64_t cd_dirty_loop; - int64_t cd_dirty_poly; - + void *_pad2; } Mesh_Runtime; typedef struct Mesh { diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index db5a3d69e4d..807a615f7f9 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -1604,6 +1604,10 @@ enum { MOD_WVG_MIX_DIF = 6, /** Average of both weights. */ MOD_WVG_MIX_AVG = 7, + /** Minimum of both weights. */ + MOD_WVG_MIX_MIN = 8, + /** Maximum of both weights. */ + MOD_WVG_MIX_MAX = 9, }; /** #WeightVGMixModifierData.mix_set (what vertices to affect). */ @@ -1694,7 +1698,7 @@ enum { MOD_WVG_PROXIMITY_GEOM_FACES = (1 << 2), MOD_WVG_PROXIMITY_INVERT_VGROUP_MASK = (1 << 3), MOD_WVG_PROXIMITY_INVERT_FALLOFF = (1 << 4), - MOD_WVG_PROXIMITY_WEIGHTS_NORMALIZE = (1 << 3), + MOD_WVG_PROXIMITY_WEIGHTS_NORMALIZE = (1 << 5), }; /* Defines common to all WeightVG modifiers. */ diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 963a34aa645..18b79a6fc25 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1610,6 +1610,11 @@ typedef struct NodeGeometryDeleteGeometry { int8_t mode; } NodeGeometryDeleteGeometry; +typedef struct NodeGeometryDuplicateElements { + /* AttributeDomain. */ + int8_t domain; +} NodeGeometryDuplicateElements; + typedef struct NodeGeometrySeparateGeometry { /* AttributeDomain. */ int8_t domain; diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index 96c60fcac97..9e0bf7dcc5a 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -471,7 +471,8 @@ typedef struct ObHook { enum { OB_EMPTY = 0, OB_MESH = 1, - OB_CURVE = 2, + /** Curve object is still used but replaced by "Curves" for the future (see T95355). */ + OB_CURVES_LEGACY = 2, OB_SURF = 3, OB_FONT = 4, OB_MBALL = 5, @@ -515,17 +516,26 @@ enum { OB_VOLUME)) #define OB_TYPE_SUPPORT_VGROUP(_type) (ELEM(_type, OB_MESH, OB_LATTICE, OB_GPENCIL)) #define OB_TYPE_SUPPORT_EDITMODE(_type) \ - (ELEM(_type, OB_MESH, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL, OB_LATTICE, OB_ARMATURE)) -#define OB_TYPE_SUPPORT_PARVERT(_type) (ELEM(_type, OB_MESH, OB_SURF, OB_CURVE, OB_LATTICE)) + (ELEM(_type, \ + OB_MESH, \ + OB_FONT, \ + OB_CURVES_LEGACY, \ + OB_SURF, \ + OB_MBALL, \ + OB_LATTICE, \ + OB_ARMATURE, \ + OB_CURVES)) +#define OB_TYPE_SUPPORT_PARVERT(_type) \ + (ELEM(_type, OB_MESH, OB_SURF, OB_CURVES_LEGACY, OB_LATTICE)) /** Matches #OB_TYPE_SUPPORT_EDITMODE. */ -#define OB_DATA_SUPPORT_EDITMODE(_type) (ELEM(_type, ID_ME, ID_CU, ID_MB, ID_LT, ID_AR)) +#define OB_DATA_SUPPORT_EDITMODE(_type) (ELEM(_type, ID_ME, ID_CU_LEGACY, ID_MB, ID_LT, ID_AR)) /* is this ID type used as object data */ #define OB_DATA_SUPPORT_ID(_id_type) \ (ELEM(_id_type, \ ID_ME, \ - ID_CU, \ + ID_CU_LEGACY, \ ID_MB, \ ID_LA, \ ID_SPK, \ @@ -540,7 +550,7 @@ enum { #define OB_DATA_SUPPORT_ID_CASE \ ID_ME: \ - case ID_CU: \ + case ID_CU_LEGACY: \ case ID_MB: \ case ID_LA: \ case ID_SPK: \ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 0d42abdb363..daecb3d7a31 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -141,7 +141,6 @@ typedef struct FFMpegCodecData { int audio_bitrate; int audio_mixrate; int audio_channels; - char _pad0[4]; float audio_volume; int gop_size; /** Only used if FFMPEG_USE_MAX_B_FRAMES flag is set. */ @@ -156,9 +155,7 @@ typedef struct FFMpegCodecData { int rc_buffer_size; int mux_packet_size; int mux_rate; - char _pad1[4]; - - IDProperty *properties; + void *_pad1; } FFMpegCodecData; /* ************************************************************* */ @@ -995,6 +992,10 @@ typedef struct Sculpt { struct Object *gravity_object; } Sculpt; +typedef struct CurvesSculpt { + Paint paint; +} CurvesSculpt; + typedef struct UvSculpt { Paint paint; } UvSculpt; @@ -1380,6 +1381,8 @@ typedef struct ToolSettings { GpSculptPaint *gp_sculptpaint; /** Gpencil weight paint. */ GpWeightPaint *gp_weightpaint; + /** Curves sculpt. */ + CurvesSculpt *curves_sculpt; /* Vertex group weight - used only for editmode, not weight * paint */ @@ -1988,7 +1991,7 @@ extern const char *RE_engine_id_CYCLES; ((v3d == NULL) || (((1 << (base)->object->type) & (v3d)->object_type_exclude_select) == 0)) && \ (((base)->flag & BASE_SELECTABLE) != 0)) #define BASE_SELECTED(v3d, base) (BASE_VISIBLE(v3d, base) && (((base)->flag & BASE_SELECTED) != 0)) -#define BASE_EDITABLE(v3d, base) (BASE_VISIBLE(v3d, base) && ((base)->object->id.lib == NULL)) +#define BASE_EDITABLE(v3d, base) (BASE_VISIBLE(v3d, base) && !ID_IS_LINKED((base)->object)) #define BASE_SELECTED_EDITABLE(v3d, base) \ (BASE_EDITABLE(v3d, base) && (((base)->flag & BASE_SELECTED) != 0)) diff --git a/source/blender/makesdna/DNA_userdef_enums.h b/source/blender/makesdna/DNA_userdef_enums.h index bb061e73c9c..e90aa0e0f07 100644 --- a/source/blender/makesdna/DNA_userdef_enums.h +++ b/source/blender/makesdna/DNA_userdef_enums.h @@ -10,7 +10,14 @@ extern "C" { #endif -/** #UserDef.dupflag */ +/** + * #UserDef.dupflag + * + * The flag tells #BKE_object_duplicate() whether to copy data linked to the object, + * or to reference the existing data. + * #U.dupflag should be used for default operations or you can construct a flag as Python does. + * If #eDupli_ID_Flags is 0 then no data will be copied (linked duplicate). + */ typedef enum eDupli_ID_Flags { USER_DUP_MESH = (1 << 0), USER_DUP_CURVE = (1 << 1), diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index e081be73a1c..80a107e4bae 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -1318,6 +1318,7 @@ typedef enum eNdof_Flag { NDOF_PANY_INVERT_AXIS = (1 << 13), NDOF_PANZ_INVERT_AXIS = (1 << 14), NDOF_TURNTABLE = (1 << 15), + NDOF_CAMERA_PAN_ZOOM = (1 << 16), } eNdof_Flag; #define NDOF_PIXELS_PER_SECOND 600.0f diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h index 626c2b2a81f..dabef04583b 100644 --- a/source/blender/makesdna/DNA_windowmanager_types.h +++ b/source/blender/makesdna/DNA_windowmanager_types.h @@ -295,9 +295,6 @@ typedef struct wmWindow { /** Storage for event system. */ struct wmEvent *eventstate; - /** Internal for wm_operators.c. */ - struct wmGesture *tweak; - /* Input Method Editor data - complex character input (especially for Asian character input) * Currently WIN32 and APPLE, runtime-only data. */ struct wmIMEData *ime_data; @@ -364,7 +361,9 @@ typedef struct wmKeyMapItem { /** Event code itself. */ short type; /** KM_ANY, KM_PRESS, KM_NOTHING etc. */ - short val; + int8_t val; + /** Use when `val == WM_CLICK_DRAG`, */ + int8_t direction; /** `oskey` also known as apple, windows-key or super, value denotes order of pressed. */ short shift, ctrl, alt, oskey; /** Raw-key modifier. */ @@ -422,7 +421,7 @@ enum { enum { KMI_TYPE_KEYBOARD = 0, KMI_TYPE_MOUSE = 1, - KMI_TYPE_TWEAK = 2, + /* 2 is deprecated, was tweak. */ KMI_TYPE_TEXTINPUT = 3, KMI_TYPE_TIMER = 4, KMI_TYPE_NDOF = 5, diff --git a/source/blender/makesdna/DNA_xr_types.h b/source/blender/makesdna/DNA_xr_types.h index bf77339a494..09eab0d7bf7 100644 --- a/source/blender/makesdna/DNA_xr_types.h +++ b/source/blender/makesdna/DNA_xr_types.h @@ -106,8 +106,23 @@ typedef enum eXrPoseFlag { XR_POSE_AIM = (1 << 1), } eXrPoseFlag; +/** + * The following user and component path lengths are dependent on OpenXR's XR_MAX_PATH_LENGTH + * (256). A user path will be combined with a component path to identify an action binding, and + * that combined path should also have a max of XR_MAX_PATH_LENGTH (e.g. user_path = + * /user/hand/left, component_path = /input/trigger/value, full_path = + * /user/hand/left/input/trigger/value). + */ +#define XR_MAX_USER_PATH_LENGTH 64 +#define XR_MAX_COMPONENT_PATH_LENGTH 192 + /* -------------------------------------------------------------------- */ +typedef struct XrComponentPath { + struct XrComponentPath *next, *prev; + char path[192]; /* XR_MAX_COMPONENT_PATH_LENGTH */ +} XrComponentPath; + typedef struct XrActionMapBinding { struct XrActionMapBinding *next, *prev; @@ -117,8 +132,7 @@ typedef struct XrActionMapBinding { /** OpenXR interaction profile path. */ char profile[256]; /** OpenXR component paths. */ - char component_path0[192]; - char component_path1[192]; + ListBase component_paths; /* XrComponentPath */ /** Input threshold/region. */ float float_threshold; @@ -132,6 +146,11 @@ typedef struct XrActionMapBinding { /* -------------------------------------------------------------------- */ +typedef struct XrUserPath { + struct XrUserPath *next, *prev; + char path[64]; /* XR_MAX_USER_PATH_LENGTH */ +} XrUserPath; + typedef struct XrActionMapItem { struct XrActionMapItem *next, *prev; @@ -142,8 +161,7 @@ typedef struct XrActionMapItem { char _pad[7]; /** OpenXR user paths. */ - char user_path0[64]; - char user_path1[64]; + ListBase user_paths; /* XrUserPath */ /** Operator to be called on XR events. */ char op[64]; /* OP_MAX_TYPENAME */ diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index eb25733a88a..bc4e7314512 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -997,7 +997,7 @@ bool RNA_property_editable_info(PointerRNA *ptr, PropertyRNA *prop, const char * /** * Same as RNA_property_editable(), except this checks individual items in an array. */ -bool RNA_property_editable_index(PointerRNA *ptr, PropertyRNA *prop, int index); +bool RNA_property_editable_index(PointerRNA *ptr, PropertyRNA *prop, const int index); /** * Without lib check, only checks the flag. diff --git a/source/blender/makesrna/RNA_enum_items.h b/source/blender/makesrna/RNA_enum_items.h index 4b68416f5d7..31db8e39e6f 100644 --- a/source/blender/makesrna/RNA_enum_items.h +++ b/source/blender/makesrna/RNA_enum_items.h @@ -87,10 +87,11 @@ 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_motionpath_display_type_items) +DEF_ENUM(rna_enum_motionpath_range_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_value_items) +DEF_ENUM(rna_enum_event_direction_items) DEF_ENUM(rna_enum_event_type_items) DEF_ENUM(rna_enum_event_type_mask_items) @@ -107,6 +108,7 @@ 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_curves_sculpt_tool_items); DEF_ENUM(rna_enum_brush_image_tool_items) DEF_ENUM(rna_enum_axis_xy_items) diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 90e979e9fbe..94ffa330064 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -35,7 +35,7 @@ const EnumPropertyItem rna_enum_id_type_items[] = { {ID_BR, "BRUSH", ICON_BRUSH_DATA, "Brush", ""}, {ID_CA, "CAMERA", ICON_CAMERA_DATA, "Camera", ""}, {ID_CF, "CACHEFILE", ICON_FILE, "Cache File", ""}, - {ID_CU, "CURVE", ICON_CURVE_DATA, "Curve", ""}, + {ID_CU_LEGACY, "CURVE", ICON_CURVE_DATA, "Curve", ""}, {ID_VF, "FONT", ICON_FONT_DATA, "Font", ""}, {ID_GD, "GREASEPENCIL", ICON_GREASEPENCIL, "Grease Pencil", ""}, {ID_GR, "COLLECTION", ICON_OUTLINER_COLLECTION, "Collection", ""}, @@ -126,7 +126,7 @@ const struct IDFilterEnumPropertyItem rna_enum_id_type_filter_items[] = { {FILTER_ID_BR, "filter_brush", ICON_BRUSH_DATA, "Brushes", "Show Brushes data-blocks"}, {FILTER_ID_CA, "filter_camera", ICON_CAMERA_DATA, "Cameras", "Show Camera data-blocks"}, {FILTER_ID_CF, "filter_cachefile", ICON_FILE, "Cache Files", "Show Cache File data-blocks"}, - {FILTER_ID_CU, "filter_curve", ICON_CURVE_DATA, "Curves", "Show Curve data-blocks"}, + {FILTER_ID_CU_LEGACY, "filter_curve", ICON_CURVE_DATA, "Curves", "Show Curve data-blocks"}, {FILTER_ID_GD, "filter_grease_pencil", ICON_GREASEPENCIL, @@ -348,7 +348,7 @@ short RNA_type_to_ID_code(const StructRNA *type) return ID_CA; } if (base_type == &RNA_Curve) { - return ID_CU; + return ID_CU_LEGACY; } if (base_type == &RNA_GreasePencil) { return ID_GD; @@ -472,7 +472,7 @@ StructRNA *ID_code_to_RNA_type(short idcode) return &RNA_Camera; case ID_CF: return &RNA_CacheFile; - case ID_CU: + case ID_CU_LEGACY: return &RNA_Curve; case ID_GD: return &RNA_GreasePencil; @@ -696,7 +696,7 @@ static ID *rna_ID_override_create(ID *id, Main *bmain, bool remap_local_usages) } static ID *rna_ID_override_hierarchy_create( - ID *id, Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_reference) + ID *id, Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_instance_hint) { if (!ID_IS_OVERRIDABLE_LIBRARY(id)) { return NULL; @@ -706,7 +706,7 @@ static ID *rna_ID_override_hierarchy_create( ID *id_root_override = NULL; BKE_lib_override_library_create( - bmain, scene, view_layer, NULL, id, id_reference, &id_root_override); + bmain, scene, view_layer, NULL, id, id, id_instance_hint, &id_root_override); WM_main_add_notifier(NC_ID | NA_ADDED, NULL); WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL); @@ -2057,7 +2057,7 @@ static void rna_def_ID(BlenderRNA *brna) "reference", "ID", "", - "Another ID (usually an Object or Collection) used to decide where to " + "Another ID (usually an Object or Collection) used as a hint to decide where to " "instantiate the new overrides"); func = RNA_def_function(srna, "override_template_create", "rna_ID_override_template_create"); diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 6c3b46c4408..7ccb8181b51 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -1898,59 +1898,63 @@ int RNA_property_ui_icon(const PropertyRNA *prop) return rna_ensure_property((PropertyRNA *)prop)->icon; } -bool RNA_property_editable(PointerRNA *ptr, PropertyRNA *prop_orig) +static bool rna_property_editable_do(PointerRNA *ptr, + PropertyRNA *prop_orig, + const int index, + const char **r_info) { ID *id = ptr->owner_id; - int flag; - const char *dummy_info; PropertyRNA *prop = rna_ensure_property(prop_orig); - flag = prop->editable ? prop->editable(ptr, &dummy_info) : prop->flag; - - return ( - (flag & PROP_EDITABLE) && (flag & PROP_REGISTER) == 0 && - (!id || ((!ID_IS_LINKED(id) || (prop->flag & PROP_LIB_EXCEPTION)) && - (!ID_IS_OVERRIDE_LIBRARY(id) || RNA_property_overridable_get(ptr, prop_orig))))); -} - -bool RNA_property_editable_info(PointerRNA *ptr, PropertyRNA *prop, const char **r_info) -{ - ID *id = ptr->owner_id; - int flag; - - PropertyRNA *prop_type = rna_ensure_property(prop); - *r_info = ""; - /* get flag */ - if (prop_type->editable) { - flag = prop_type->editable(ptr, r_info); + const char *info = ""; + const int flag = (prop->itemeditable != NULL && index >= 0) ? + prop->itemeditable(ptr, index) : + (prop->editable != NULL ? prop->editable(ptr, &info) : prop->flag); + if (r_info != NULL) { + *r_info = info; } - else { - flag = prop_type->flag; - if ((flag & PROP_EDITABLE) == 0 || (flag & PROP_REGISTER)) { + + /* Early return if the property itself is not editable. */ + if ((flag & PROP_EDITABLE) == 0 || (flag & PROP_REGISTER) != 0) { + if (r_info != NULL && (*r_info)[0] == '\0') { *r_info = N_("This property is for internal use only and can't be edited"); } + return false; } - /* property from linked data-block */ - if (id) { - if (ID_IS_LINKED(id) && (prop_type->flag & PROP_LIB_EXCEPTION) == 0) { - if (!(*r_info)[0]) { - *r_info = N_("Can't edit this property from a linked data-block"); - } - return false; + /* If there is no owning ID, the property is editable at this point. */ + if (id == NULL) { + return true; + } + + /* Handle linked or liboverride ID cases. */ + const bool is_linked_prop_exception = (prop->flag & PROP_LIB_EXCEPTION) != 0; + if (ID_IS_LINKED(id) && !is_linked_prop_exception) { + if (r_info != NULL && (*r_info)[0] == '\0') { + *r_info = N_("Can't edit this property from a linked data-block"); } - if (ID_IS_OVERRIDE_LIBRARY(id)) { - if (!RNA_property_overridable_get(ptr, prop)) { - if (!(*r_info)[0]) { - *r_info = N_("Can't edit this property from an override data-block"); - } - return false; - } + return false; + } + if (ID_IS_OVERRIDE_LIBRARY(id) && !RNA_property_overridable_get(ptr, prop_orig)) { + if (r_info != NULL && (*r_info)[0] == '\0') { + *r_info = N_("Can't edit this property from an override data-block"); } + return false; } - return ((flag & PROP_EDITABLE) && (flag & PROP_REGISTER) == 0); + /* At this point, property is owned by a local ID and therefore fully editable. */ + return true; +} + +bool RNA_property_editable(PointerRNA *ptr, PropertyRNA *prop) +{ + return rna_property_editable_do(ptr, prop, -1, NULL); +} + +bool RNA_property_editable_info(PointerRNA *ptr, PropertyRNA *prop, const char **r_info) +{ + return rna_property_editable_do(ptr, prop, -1, r_info); } bool RNA_property_editable_flag(PointerRNA *ptr, PropertyRNA *prop) @@ -1963,29 +1967,11 @@ bool RNA_property_editable_flag(PointerRNA *ptr, PropertyRNA *prop) return (flag & PROP_EDITABLE) != 0; } -bool RNA_property_editable_index(PointerRNA *ptr, PropertyRNA *prop, int index) +bool RNA_property_editable_index(PointerRNA *ptr, PropertyRNA *prop, const int index) { - ID *id; - int flag; - BLI_assert(index >= 0); - prop = rna_ensure_property(prop); - - flag = prop->flag; - - if (prop->editable) { - const char *dummy_info; - flag &= prop->editable(ptr, &dummy_info); - } - - if (prop->itemeditable) { - flag &= prop->itemeditable(ptr, index); - } - - id = ptr->owner_id; - - return (flag & PROP_EDITABLE) && (!id || !ID_IS_LINKED(id) || (prop->flag & PROP_LIB_EXCEPTION)); + return rna_property_editable_do(ptr, prop, index, NULL); } bool RNA_property_animateable(PointerRNA *ptr, PropertyRNA *prop) @@ -4918,7 +4904,7 @@ static char *rna_path_token_in_brackets(const char **path, } /** - * \return true when when the key in the path is correctly parsed and found in the collection + * \return true when the key in the path is correctly parsed and found in the collection * or when the path is empty. */ static bool rna_path_parse_collection_key(const char **path, diff --git a/source/blender/makesrna/intern/rna_access_compare_override.c b/source/blender/makesrna/intern/rna_access_compare_override.c index 1aa51c93ee8..fd65cc464f2 100644 --- a/source/blender/makesrna/intern/rna_access_compare_override.c +++ b/source/blender/makesrna/intern/rna_access_compare_override.c @@ -218,7 +218,7 @@ bool RNA_property_copy( /* IDprops: destination may not exist, if source does and is set, try to create it. */ /* NOTE: this is sort of quick hack/bandage to fix the issue, - * we need to rethink how IDProps are handled in 'diff' RNA code completely, imho... */ + * we need to rethink how IDProps are handled in 'diff' RNA code completely, IMHO. */ if (prop_src != NULL && prop_dst == NULL && RNA_property_is_set(fromptr, prop)) { BLI_assert(prop_src->magic != RNA_MAGIC); IDProperty *idp_dst = RNA_struct_idprops(ptr, true); diff --git a/source/blender/makesrna/intern/rna_animviz.c b/source/blender/makesrna/intern/rna_animviz.c index cb993931296..0453b327b45 100644 --- a/source/blender/makesrna/intern/rna_animviz.c +++ b/source/blender/makesrna/intern/rna_animviz.c @@ -36,6 +36,27 @@ const EnumPropertyItem rna_enum_motionpath_bake_location_items[] = { {0, NULL, 0, NULL, NULL}, }; +const EnumPropertyItem rna_enum_motionpath_display_type_items[] = { + {MOTIONPATH_TYPE_ACFRA, + "CURRENT_FRAME", + 0, + "Around Frame", + "Display Paths of poses within a fixed number of frames around the current frame"}, + {MOTIONPATH_TYPE_RANGE, + "RANGE", + 0, + "In Range", + "Display Paths of poses within specified range"}, + {0, NULL, 0, NULL, NULL}, +}; + +const EnumPropertyItem rna_enum_motionpath_range_items[] = { + {MOTIONPATH_RANGE_KEYS_ALL, "KEYS_ALL", 0, "All keys range", ""}, + {MOTIONPATH_RANGE_KEYS_SELECTED, "KEYS_SELECTED", 0, "Selected keys range", ""}, + {MOTIONPATH_RANGE_SCENE, "SCENE", 0, "Scene frame range", ""}, + {0, NULL, 0, NULL, NULL}, +}; + #ifdef RNA_RUNTIME static PointerRNA rna_AnimViz_motion_paths_get(PointerRNA *ptr) @@ -173,20 +194,6 @@ static void rna_def_animviz_paths(BlenderRNA *brna) StructRNA *srna; PropertyRNA *prop; - static const EnumPropertyItem prop_type_items[] = { - {MOTIONPATH_TYPE_ACFRA, - "CURRENT_FRAME", - 0, - "Around Frame", - "Display Paths of poses within a fixed number of frames around the current frame"}, - {MOTIONPATH_TYPE_RANGE, - "RANGE", - 0, - "In Range", - "Display Paths of poses within specified range"}, - {0, NULL, 0, NULL, NULL}, - }; - srna = RNA_def_struct(brna, "AnimVizMotionPaths", NULL); RNA_def_struct_sdna(srna, "bAnimVizSettings"); RNA_def_struct_nested(brna, srna, "AnimViz"); @@ -198,10 +205,16 @@ static void rna_def_animviz_paths(BlenderRNA *brna) /* Enums */ prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "path_type"); - RNA_def_property_enum_items(prop, prop_type_items); + RNA_def_property_enum_items(prop, rna_enum_motionpath_display_type_items); RNA_def_property_ui_text(prop, "Paths Type", "Type of range to show for Motion Paths"); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW_ANIMVIZ, NULL); + prop = RNA_def_property(srna, "range", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "path_range"); + RNA_def_property_enum_items(prop, rna_enum_motionpath_range_items); + RNA_def_property_ui_text(prop, "Paths Range", "Type of range to calculate for Motion Paths"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW_ANIMVIZ, NULL); + prop = RNA_def_property(srna, "bake_location", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "path_bakeflag"); RNA_def_property_enum_items(prop, rna_enum_motionpath_bake_location_items); diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index f3c7d2747ac..667114a3598 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -243,6 +243,12 @@ const EnumPropertyItem rna_enum_brush_gpencil_weight_types_items[] = { {0, NULL, 0, NULL, NULL}, }; +const EnumPropertyItem rna_enum_brush_curves_sculpt_tool_items[] = { + {CURVES_SCULPT_TOOL_TEST1, "TEST1", ICON_NONE, "Test 1", ""}, + {CURVES_SCULPT_TOOL_TEST2, "TEST2", ICON_NONE, "Test 2", ""}, + {0, NULL, 0, NULL, NULL}, +}; + #ifndef RNA_RUNTIME static EnumPropertyItem rna_enum_gpencil_brush_eraser_modes_items[] = { {GP_BRUSH_ERASER_SOFT, @@ -2312,6 +2318,11 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Grease Pencil Weight Paint Tool", ""); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + prop = RNA_def_property(srna, "curves_sculpt_tool", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_brush_curves_sculpt_tool_items); + RNA_def_property_ui_text(prop, "Curves Sculpt Tool", ""); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + /** End per mode tool properties. */ prop = RNA_def_property(srna, "direction", PROP_ENUM, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c index edcf121a8c4..114fe30acf7 100644 --- a/source/blender/makesrna/intern/rna_constraint.c +++ b/source/blender/makesrna/intern/rna_constraint.c @@ -2969,7 +2969,7 @@ static void rna_def_constraint_spline_ik(BlenderRNA *brna) PropertyRNA *prop; static const EnumPropertyItem splineik_xz_scale_mode[] = { - {CONSTRAINT_SPLINEIK_XZS_NONE, "NONE", 0, "None", "Don't scale the X and Z axes (Default)"}, + {CONSTRAINT_SPLINEIK_XZS_NONE, "NONE", 0, "None", "Don't scale the X and Z axes"}, {CONSTRAINT_SPLINEIK_XZS_ORIGINAL, "BONE_ORIGINAL", 0, diff --git a/source/blender/makesrna/intern/rna_context.c b/source/blender/makesrna/intern/rna_context.c index bc4bd9ad3d6..e723be2ab71 100644 --- a/source/blender/makesrna/intern/rna_context.c +++ b/source/blender/makesrna/intern/rna_context.c @@ -21,6 +21,7 @@ const EnumPropertyItem rna_enum_context_mode_items[] = { {CTX_MODE_EDIT_MESH, "EDIT_MESH", 0, "Mesh Edit", ""}, {CTX_MODE_EDIT_CURVE, "EDIT_CURVE", 0, "Curve Edit", ""}, + {CTX_MODE_EDIT_CURVES, "EDIT_CURVES", 0, "Curves Edit", ""}, {CTX_MODE_EDIT_SURFACE, "EDIT_SURFACE", 0, "Surface Edit", ""}, {CTX_MODE_EDIT_TEXT, "EDIT_TEXT", 0, "Text Edit", ""}, /* PARSKEL reuse will give issues */ diff --git a/source/blender/makesrna/intern/rna_curve.c b/source/blender/makesrna/intern/rna_curve.c index b37a6e5fd0e..891de95a2a2 100644 --- a/source/blender/makesrna/intern/rna_curve.c +++ b/source/blender/makesrna/intern/rna_curve.c @@ -441,7 +441,7 @@ static void rna_Curve_bevelObject_set(PointerRNA *ptr, if (ob) { /* If bevel object has got the save curve, as object, for which it's set as bevobj, * there could be infinity loop in #DispList calculation. */ - if (ob->type == OB_CURVE && ob->data != cu) { + if (ob->type == OB_CURVES_LEGACY && ob->data != cu) { cu->bevobj = ob; id_lib_extern((ID *)ob); } @@ -486,7 +486,7 @@ static bool rna_Curve_otherObject_poll(PointerRNA *ptr, PointerRNA value) Object *ob = (Object *)value.data; if (ob) { - if (ob->type == OB_CURVE && ob->data != cu) { + if (ob->type == OB_CURVES_LEGACY && ob->data != cu) { return 1; } } @@ -516,7 +516,7 @@ static void rna_Curve_taperObject_set(PointerRNA *ptr, if (ob) { /* If taper object has got the save curve, as object, for which it's set as bevobj, * there could be infinity loop in #DispList calculation. */ - if (ob->type == OB_CURVE && ob->data != cu) { + if (ob->type == OB_CURVES_LEGACY && ob->data != cu) { cu->taperobj = ob; id_lib_extern((ID *)ob); } diff --git a/source/blender/makesrna/intern/rna_curves.c b/source/blender/makesrna/intern/rna_curves.c index 8e7fb415c7d..7a1a368551f 100644 --- a/source/blender/makesrna/intern/rna_curves.c +++ b/source/blender/makesrna/intern/rna_curves.c @@ -16,6 +16,8 @@ #include "BLI_math_base.h" #include "BLI_string.h" +#include "WM_types.h" + #ifdef RNA_RUNTIME # include "BLI_math_vector.h" @@ -43,7 +45,7 @@ static void rna_Curves_curve_offset_data_begin(CollectionPropertyIterator *iter, { const Curves *curves = rna_curves(ptr); rna_iterator_array_begin(iter, - (void *)curves->geometry.offsets, + (void *)curves->geometry.curve_offsets, sizeof(int), curves->geometry.curve_size + 1, false, @@ -95,7 +97,7 @@ static char *rna_CurvePoint_path(PointerRNA *ptr) static int rna_CurveSlice_index_get(PointerRNA *ptr) { Curves *curves = rna_curves(ptr); - return (int)((int *)ptr->data - curves->geometry.offsets); + return (int)((int *)ptr->data - curves->geometry.curve_offsets); } static char *rna_CurveSlice_path(PointerRNA *ptr) @@ -220,7 +222,7 @@ static void rna_def_curves(BlenderRNA *brna) /* Point and Curve RNA API helpers. */ prop = RNA_def_property(srna, "curves", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "geometry.offsets", "geometry.curve_size"); + RNA_def_property_collection_sdna(prop, NULL, "geometry.curve_offsets", "geometry.curve_size"); RNA_def_property_struct_type(prop, "CurveSlice"); RNA_def_property_ui_text(prop, "Curves", "All curves in the data-block"); @@ -243,7 +245,7 @@ static void rna_def_curves(BlenderRNA *brna) RNA_define_verify_sdna(1); prop = RNA_def_property(srna, "curve_offset_data", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "geometry.offsets", NULL); + RNA_def_property_collection_sdna(prop, NULL, "geometry.curve_offsets", NULL); RNA_def_property_struct_type(prop, "IntAttributeValue"); RNA_def_property_collection_funcs(prop, "rna_Curves_curve_offset_data_begin", @@ -265,6 +267,13 @@ static void rna_def_curves(BlenderRNA *brna) RNA_def_property_collection_funcs( prop, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "rna_IDMaterials_assign_int"); + prop = RNA_def_property(srna, "surface", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "Object"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_Mesh_object_poll"); + RNA_def_property_ui_text(prop, "Surface", "Mesh object that the curves can be attached to"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); + /* attributes */ rna_def_attributes_common(srna); diff --git a/source/blender/makesrna/intern/rna_define.c b/source/blender/makesrna/intern/rna_define.c index 850accd0b24..5127b418b7f 100644 --- a/source/blender/makesrna/intern/rna_define.c +++ b/source/blender/makesrna/intern/rna_define.c @@ -4391,24 +4391,21 @@ void RNA_enum_item_add(EnumPropertyItem **items, int *totitem, const EnumPropert if (tot == 0) { *items = MEM_callocN(sizeof(EnumPropertyItem[8]), __func__); + /* Ensure we get crashes on missing calls to 'RNA_enum_item_end', see T74227. */ +#ifdef DEBUG + memset(*items, 0xff, sizeof(EnumPropertyItem[8])); +#endif } else if (tot >= 8 && (tot & (tot - 1)) == 0) { /* power of two > 8 */ *items = MEM_recallocN_id(*items, sizeof(EnumPropertyItem) * tot * 2, __func__); +#ifdef DEBUG + memset((*items) + tot, 0xff, sizeof(EnumPropertyItem) * tot); +#endif } (*items)[tot] = *item; *totitem = tot + 1; - - /* Ensure we get crashes on missing calls to 'RNA_enum_item_end', see T74227. */ -#ifdef DEBUG - static const EnumPropertyItem item_error = { - -1, POINTER_FROM_INT(-1), -1, POINTER_FROM_INT(-1), POINTER_FROM_INT(-1)}; - if (item != &item_error) { - RNA_enum_item_add(items, totitem, &item_error); - *totitem -= 1; - } -#endif } void RNA_enum_item_add_separator(EnumPropertyItem **items, int *totitem) diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c index f24803af654..8f418fcc7c7 100644 --- a/source/blender/makesrna/intern/rna_fcurve.c +++ b/source/blender/makesrna/intern/rna_fcurve.c @@ -1880,7 +1880,7 @@ static void rna_def_drivervar(BlenderRNA *brna) "SINGLE_PROP", ICON_RNA, "Single Property", - "Use the value from some RNA property (Default)"}, + "Use the value from some RNA property"}, {DVAR_TYPE_TRANSFORM_CHAN, "TRANSFORMS", ICON_DRIVER_TRANSFORM, diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 6a0e5156234..a619d179a33 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -1223,6 +1223,14 @@ static void rna_def_modifier_gpencilsimplify(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Length", "Length of each segment"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "sharp_threshold", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_float_sdna(prop, NULL, "sharp_threshold"); + RNA_def_property_range(prop, 0, M_PI); + RNA_def_property_ui_range(prop, 0, M_PI, 1.0, 1); + RNA_def_property_ui_text( + prop, "Sharp Threshold", "Preserve corners that have sharper angle than this threshold"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + /* Merge */ prop = RNA_def_property(srna, "distance", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_float_sdna(prop, NULL, "distance"); @@ -2482,7 +2490,8 @@ static void rna_def_modifier_gpencilhook(BlenderRNA *brna) prop = RNA_def_property(srna, "falloff_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, modifier_gphook_falloff_items); /* share the enum */ RNA_def_property_ui_text(prop, "Falloff Type", ""); - RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */ + RNA_def_property_translation_context(prop, + BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */ RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "falloff_radius", PROP_FLOAT, PROP_DISTANCE); diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index e40fafd2069..56e23278176 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -103,6 +103,7 @@ static void rna_Image_generated_update(Main *bmain, Scene *UNUSED(scene), Pointe { Image *ima = (Image *)ptr->owner_id; BKE_image_signal(bmain, ima, NULL, IMA_SIGNAL_FREE); + BKE_image_partial_update_mark_full_update(ima); } static void rna_Image_colormanage_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) @@ -141,6 +142,7 @@ static void rna_Image_views_format_update(Main *bmain, Scene *scene, PointerRNA } BKE_image_release_ibuf(ima, ibuf, lock); + BKE_image_partial_update_mark_full_update(ima); } static void rna_ImageUser_update(Main *bmain, Scene *scene, PointerRNA *ptr) diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 2c5f264f7e9..44bf51d9770 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -649,7 +649,7 @@ const char *rna_translate_ui_text(const char *text, struct PropertyRNA *prop, bool translate); -/* Internal functions that cycles uses so we need to declare (tsk tsk) */ +/* Internal functions that cycles uses so we need to declare (tsk!). */ void rna_RenderPass_rect_set(PointerRNA *ptr, const float *values); #ifdef RNA_RUNTIME diff --git a/source/blender/makesrna/intern/rna_key.c b/source/blender/makesrna/intern/rna_key.c index 51dcfd13657..7687dcbb11f 100644 --- a/source/blender/makesrna/intern/rna_key.c +++ b/source/blender/makesrna/intern/rna_key.c @@ -47,7 +47,7 @@ static Key *rna_ShapeKey_find_key(ID *id) { switch (GS(id->name)) { - case ID_CU: + case ID_CU_LEGACY: return ((Curve *)id)->key; case ID_KE: return (Key *)id; @@ -556,7 +556,7 @@ static void rna_ShapeKey_data_begin(CollectionPropertyIterator *iter, PointerRNA KeyBlock *kb = (KeyBlock *)ptr->data; int tot = kb->totelem, size = key->elemsize; - if (GS(key->from->name) == ID_CU && tot > 0) { + if (GS(key->from->name) == ID_CU_LEGACY && tot > 0) { Curve *cu = (Curve *)key->from; StructRNA *type = NULL; NurbInfo info = {0}; @@ -593,7 +593,7 @@ static int rna_ShapeKey_data_length(PointerRNA *ptr) KeyBlock *kb = (KeyBlock *)ptr->data; int tot = kb->totelem; - if (GS(key->from->name) == ID_CU) { + if (GS(key->from->name) == ID_CU_LEGACY) { tot = rna_ShapeKey_curve_find_index(key, tot); } @@ -613,7 +613,7 @@ static PointerRNA rna_ShapeKey_data_get(CollectionPropertyIterator *iter) return rna_pointer_inherit_refine(&iter->parent, point->type, point->data); } - if (GS(key->from->name) == ID_CU) { + if (GS(key->from->name) == ID_CU_LEGACY) { Curve *cu = (Curve *)key->from; type = rna_ShapeKey_curve_point_type(cu->nurb.first); @@ -635,7 +635,7 @@ int rna_ShapeKey_data_lookup_int(PointerRNA *ptr, int index, PointerRNA *r_ptr) return false; } - if (GS(key->from->name) == ID_CU) { + if (GS(key->from->name) == ID_CU_LEGACY) { NurbInfo info; rna_ShapeKey_NurbInfo_find_index(key, index, false, &info); diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c index e9d39d708a9..b239260c8eb 100644 --- a/source/blender/makesrna/intern/rna_main_api.c +++ b/source/blender/makesrna/intern/rna_main_api.c @@ -322,7 +322,7 @@ static Mesh *rna_Main_meshes_new_from_object(Main *bmain, { switch (object->type) { case OB_FONT: - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: case OB_MBALL: case OB_MESH: @@ -822,7 +822,7 @@ RNA_MAIN_ID_TAG_FUNCS_DEF(screens, screens, ID_SCR) RNA_MAIN_ID_TAG_FUNCS_DEF(window_managers, wm, ID_WM) RNA_MAIN_ID_TAG_FUNCS_DEF(images, images, ID_IM) RNA_MAIN_ID_TAG_FUNCS_DEF(lattices, lattices, ID_LT) -RNA_MAIN_ID_TAG_FUNCS_DEF(curves, curves, ID_CU) +RNA_MAIN_ID_TAG_FUNCS_DEF(curves, curves, ID_CU_LEGACY) RNA_MAIN_ID_TAG_FUNCS_DEF(metaballs, metaballs, ID_MB) RNA_MAIN_ID_TAG_FUNCS_DEF(fonts, fonts, ID_VF) RNA_MAIN_ID_TAG_FUNCS_DEF(textures, textures, ID_TE) diff --git a/source/blender/makesrna/intern/rna_mask.c b/source/blender/makesrna/intern/rna_mask.c index e8b8c6e3274..2247a16a7a0 100644 --- a/source/blender/makesrna/intern/rna_mask.c +++ b/source/blender/makesrna/intern/rna_mask.c @@ -1025,7 +1025,8 @@ static void rna_def_mask_layer(BlenderRNA *brna) RNA_def_property_enum_sdna(prop, NULL, "falloff"); RNA_def_property_enum_items(prop, rna_enum_proportional_falloff_curve_only_items); RNA_def_property_ui_text(prop, "Falloff", "Falloff type the feather"); - RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */ + RNA_def_property_translation_context(prop, + BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */ RNA_def_property_update(prop, NC_MASK | NA_EDITED, NULL); /* filling options */ diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index 6b3ae373295..87459587a9e 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -456,6 +456,7 @@ static void rna_MeshPolygon_flip(ID *id, MPoly *mp) BKE_mesh_polygon_flip(mp, me->mloop, &me->ldata); BKE_mesh_tessface_clear(me); BKE_mesh_runtime_clear_geometry(me); + BKE_mesh_normals_tag_dirty(me); } static void rna_MeshLoopTriangle_verts_get(PointerRNA *ptr, int *values) diff --git a/source/blender/makesrna/intern/rna_mesh_api.c b/source/blender/makesrna/intern/rna_mesh_api.c index ebacced8b3f..8447074a3ef 100644 --- a/source/blender/makesrna/intern/rna_mesh_api.c +++ b/source/blender/makesrna/intern/rna_mesh_api.c @@ -167,7 +167,7 @@ static void rna_Mesh_flip_normals(Mesh *mesh) { BKE_mesh_polygons_flip(mesh->mpoly, mesh->mloop, &mesh->ldata, mesh->totpoly); BKE_mesh_tessface_clear(mesh); - BKE_mesh_calc_normals(mesh); + BKE_mesh_normals_tag_dirty(mesh); BKE_mesh_runtime_clear_geometry(mesh); DEG_id_tag_update(&mesh->id, 0); diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 45d22981205..b12b33c67af 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -869,10 +869,10 @@ static void modifier_object_set(Object *self, Object **ob_p, int type, PointerRN RNA_MOD_OBJECT_SET(Armature, object, OB_ARMATURE); RNA_MOD_OBJECT_SET(Array, start_cap, OB_MESH); RNA_MOD_OBJECT_SET(Array, end_cap, OB_MESH); -RNA_MOD_OBJECT_SET(Array, curve_ob, OB_CURVE); +RNA_MOD_OBJECT_SET(Array, curve_ob, OB_CURVES_LEGACY); RNA_MOD_OBJECT_SET(Boolean, object, OB_MESH); RNA_MOD_OBJECT_SET(Cast, object, OB_EMPTY); -RNA_MOD_OBJECT_SET(Curve, object, OB_CURVE); +RNA_MOD_OBJECT_SET(Curve, object, OB_CURVES_LEGACY); RNA_MOD_OBJECT_SET(DataTransfer, ob_source, OB_MESH); RNA_MOD_OBJECT_SET(Lattice, object, OB_LATTICE); RNA_MOD_OBJECT_SET(Mask, ob_arm, OB_ARMATURE); @@ -1849,7 +1849,8 @@ static void rna_def_modifier_warp(BlenderRNA *brna) prop = RNA_def_property(srna, "falloff_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, modifier_warp_falloff_items); RNA_def_property_ui_text(prop, "Falloff Type", ""); - RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */ + RNA_def_property_translation_context(prop, + BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */ RNA_def_property_update(prop, 0, "rna_Modifier_update"); prop = RNA_def_property(srna, "falloff_radius", PROP_FLOAT, PROP_DISTANCE); @@ -2567,7 +2568,8 @@ static void rna_def_modifier_hook(BlenderRNA *brna) prop = RNA_def_property(srna, "falloff_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, modifier_warp_falloff_items); /* share the enum */ RNA_def_property_ui_text(prop, "Falloff Type", ""); - RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */ + RNA_def_property_translation_context(prop, + BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */ RNA_def_property_update(prop, 0, "rna_Modifier_update"); prop = RNA_def_property(srna, "falloff_radius", PROP_FLOAT, PROP_DISTANCE); @@ -4947,6 +4949,7 @@ static void rna_def_modifier_uvwarp(BlenderRNA *brna) static void rna_def_modifier_weightvg_mask(BlenderRNA *UNUSED(brna), StructRNA *srna, const char *mask_flags, + const int invert_vgroup_mask_flag, const char *mask_vgroup_setter, const char *mask_uvlayer_setter) { @@ -4992,7 +4995,7 @@ static void rna_def_modifier_weightvg_mask(BlenderRNA *UNUSED(brna), RNA_def_property_update(prop, 0, "rna_Modifier_update"); prop = RNA_def_property(srna, "invert_mask_vertex_group", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, mask_flags, MOD_WVG_EDIT_INVERT_VGROUP_MASK); + RNA_def_property_boolean_sdna(prop, NULL, mask_flags, invert_vgroup_mask_flag); RNA_def_property_ui_text(prop, "Invert", "Invert vertex group mask influence"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); @@ -5076,7 +5079,8 @@ static void rna_def_modifier_weightvgedit(BlenderRNA *brna) prop = RNA_def_property(srna, "falloff_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, weightvg_edit_falloff_type_items); RNA_def_property_ui_text(prop, "Falloff Type", "How weights are mapped to their new values"); - RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */ + RNA_def_property_translation_context(prop, + BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */ RNA_def_property_update(prop, 0, "rna_Modifier_update"); prop = RNA_def_property(srna, "invert_falloff", PROP_BOOLEAN, PROP_NONE); @@ -5148,6 +5152,7 @@ static void rna_def_modifier_weightvgedit(BlenderRNA *brna) rna_def_modifier_weightvg_mask(brna, srna, "edit_flags", + MOD_WVG_EDIT_INVERT_VGROUP_MASK, "rna_WeightVGEditModifier_mask_defgrp_name_set", "rna_WeightVGEditModifier_mask_tex_uvlayer_name_set"); } @@ -5166,6 +5171,8 @@ static void rna_def_modifier_weightvgmix(BlenderRNA *brna) "Difference", "Difference between VGroup A's and VGroup B's weights"}, {MOD_WVG_MIX_AVG, "AVG", 0, "Average", "Average value of VGroup A's and VGroup B's weights"}, + {MOD_WVG_MIX_MIN, "MIN", 0, "Minimum", "Minimum of VGroup A's and VGroup B's weights"}, + {MOD_WVG_MIX_MAX, "MAX", 0, "Maximum", "Maximum of VGroup A's and VGroup B's weights"}, {0, NULL, 0, NULL, NULL}, }; @@ -5263,6 +5270,7 @@ static void rna_def_modifier_weightvgmix(BlenderRNA *brna) rna_def_modifier_weightvg_mask(brna, srna, "flag", + MOD_WVG_MIX_INVERT_VGROUP_MASK, "rna_WeightVGMixModifier_mask_defgrp_name_set", "rna_WeightVGMixModifier_mask_tex_uvlayer_name_set"); } @@ -5364,7 +5372,8 @@ static void rna_def_modifier_weightvgproximity(BlenderRNA *brna) prop = RNA_def_property(srna, "falloff_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, weightvg_proximity_falloff_type_items); RNA_def_property_ui_text(prop, "Falloff Type", "How weights are mapped to their new values"); - RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */ + RNA_def_property_translation_context(prop, + BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */ RNA_def_property_update(prop, 0, "rna_Modifier_update"); prop = RNA_def_property(srna, "invert_falloff", PROP_BOOLEAN, PROP_NONE); @@ -5392,6 +5401,7 @@ static void rna_def_modifier_weightvgproximity(BlenderRNA *brna) rna_def_modifier_weightvg_mask(brna, srna, "proximity_flags", + MOD_WVG_PROXIMITY_INVERT_VGROUP_MASK, "rna_WeightVGProximityModifier_mask_defgrp_name_set", "rna_WeightVGProximityModifier_mask_tex_uvlayer_name_set"); } diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 3ae9ab3cec2..387166e77b4 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -7196,7 +7196,8 @@ static void def_cmp_dilate_erode(StructRNA *srna) RNA_def_property_enum_sdna(prop, NULL, "falloff"); RNA_def_property_enum_items(prop, rna_enum_proportional_falloff_curve_only_items); RNA_def_property_ui_text(prop, "Falloff", "Falloff type the feather"); - RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */ + RNA_def_property_translation_context(prop, + BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } @@ -8974,7 +8975,8 @@ static void def_cmp_keying(StructRNA *srna) RNA_def_property_enum_sdna(prop, NULL, "feather_falloff"); RNA_def_property_enum_items(prop, rna_enum_proportional_falloff_curve_only_items); RNA_def_property_ui_text(prop, "Feather Falloff", "Falloff type the feather"); - RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */ + RNA_def_property_translation_context(prop, + BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); prop = RNA_def_property(srna, "feather_distance", PROP_INT, PROP_NONE); @@ -11285,6 +11287,27 @@ static void def_geo_delete_geometry(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_geo_duplicate_elements(StructRNA *srna) +{ + PropertyRNA *prop; + + static const EnumPropertyItem domain_items[] = { + {ATTR_DOMAIN_POINT, "POINT", 0, "Point", ""}, + {ATTR_DOMAIN_EDGE, "EDGE", 0, "Edge", ""}, + {ATTR_DOMAIN_FACE, "FACE", 0, "Face", ""}, + {ATTR_DOMAIN_CURVE, "SPLINE", 0, "Spline", ""}, + {ATTR_DOMAIN_INSTANCE, "INSTANCE", 0, "Instance", ""}, + {0, NULL, 0, NULL, NULL}, + }; + RNA_def_struct_sdna_from(srna, "NodeGeometryDuplicateElements", "storage"); + + prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, domain_items); + RNA_def_property_enum_default(prop, ATTR_DOMAIN_POINT); + RNA_def_property_ui_text(prop, "Domain", "Which domain to duplicate"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + static void def_geo_string_to_curves(StructRNA *srna) { static const EnumPropertyItem rna_node_geometry_string_to_curves_overflow_items[] = { diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index 9c4ebf79a08..c598e63a32a 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -241,7 +241,7 @@ const EnumPropertyItem rna_enum_lightprobes_type_items[] = { /* used for 2 enums */ #define OBTYPE_CU_CURVE \ { \ - OB_CURVE, "CURVE", ICON_OUTLINER_OB_CURVE, "Curve", "" \ + OB_CURVES_LEGACY, "CURVE", ICON_OUTLINER_OB_CURVE, "Curve", "" \ } #define OBTYPE_CU_SURF \ { \ @@ -479,7 +479,7 @@ static void rna_Object_active_shape_update(Main *bmain, Scene *UNUSED(scene), Po BKE_editmesh_looptri_and_normals_calc(em); break; } - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: ED_curve_editnurb_load(bmain, ob); ED_curve_editnurb_make(ob); @@ -571,7 +571,7 @@ static void rna_Object_data_set(PointerRNA *ptr, PointerRNA value, struct Report ob->data = id; BKE_object_materials_test(G_MAIN, ob, id); - if (GS(id->name) == ID_CU) { + if (GS(id->name) == ID_CU_LEGACY) { BKE_curve_type_test(ob); } else if (ob->type == OB_ARMATURE) { @@ -590,7 +590,7 @@ static StructRNA *rna_Object_data_typef(PointerRNA *ptr) return &RNA_Image; case OB_MESH: return &RNA_Mesh; - case OB_CURVE: + case OB_CURVES_LEGACY: return &RNA_Curve; case OB_SURF: return &RNA_Curve; @@ -2182,7 +2182,7 @@ bool rna_Lattice_object_poll(PointerRNA *UNUSED(ptr), PointerRNA value) bool rna_Curve_object_poll(PointerRNA *UNUSED(ptr), PointerRNA value) { - return ((Object *)value.owner_id)->type == OB_CURVE; + return ((Object *)value.owner_id)->type == OB_CURVES_LEGACY; } bool rna_Armature_object_poll(PointerRNA *UNUSED(ptr), PointerRNA value) diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c index 9676be69d05..ece1c5e5815 100644 --- a/source/blender/makesrna/intern/rna_object_api.c +++ b/source/blender/makesrna/intern/rna_object_api.c @@ -407,7 +407,7 @@ static Mesh *rna_Object_to_mesh(Object *object, * rna_Main_meshes_new_from_object. */ switch (object->type) { case OB_FONT: - case OB_CURVE: + case OB_CURVES_LEGACY: case OB_SURF: case OB_MBALL: case OB_MESH: @@ -430,7 +430,7 @@ static Curve *rna_Object_to_curve(Object *object, Depsgraph *depsgraph, bool apply_modifiers) { - if (!ELEM(object->type, OB_FONT, OB_CURVE)) { + if (!ELEM(object->type, OB_FONT, OB_CURVES_LEGACY)) { BKE_report(reports, RPT_ERROR, "Object is not a curve or a text"); return NULL; } @@ -785,7 +785,7 @@ bool rna_Object_generate_gpencil_strokes(Object *ob, float scale_thickness, float sample) { - if (ob->type != OB_CURVE) { + if (ob->type != OB_CURVES_LEGACY) { BKE_reportf(reports, RPT_ERROR, "Object '%s' is not valid for this operation! Only curves are supported", diff --git a/source/blender/makesrna/intern/rna_object_force.c b/source/blender/makesrna/intern/rna_object_force.c index 9ca78033199..8579f188428 100644 --- a/source/blender/makesrna/intern/rna_object_force.c +++ b/source/blender/makesrna/intern/rna_object_force.c @@ -691,7 +691,7 @@ static void rna_FieldSettings_dependency_update(Main *bmain, Scene *scene, Point rna_FieldSettings_shape_update(bmain, scene, ptr); - if (ob->type == OB_CURVE && ob->pd->forcefield == PFIELD_GUIDE) { + if (ob->type == OB_CURVES_LEGACY && ob->pd->forcefield == PFIELD_GUIDE) { DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); } else { @@ -899,7 +899,7 @@ static const EnumPropertyItem *rna_Effector_shape_itemf(bContext *UNUSED(C), ob = (Object *)ptr->owner_id; - if (ob->type == OB_CURVE) { + if (ob->type == OB_CURVES_LEGACY) { if (ob->pd->forcefield == PFIELD_VORTEX) { return curve_vortex_shape_items; } diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 178029ef340..29b06060256 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -1466,18 +1466,6 @@ static void rna_FFmpegSettings_lossless_output_set(PointerRNA *ptr, bool value) else { rd->ffcodecdata.flags &= ~FFMPEG_LOSSLESS_OUTPUT; } - - BKE_ffmpeg_codec_settings_verify(rd); -} - -static void rna_FFmpegSettings_codec_settings_update(Main *UNUSED(bmain), - Scene *UNUSED(scene_unused), - PointerRNA *ptr) -{ - Scene *scene = (Scene *)ptr->owner_id; - RenderData *rd = &scene->r; - - BKE_ffmpeg_codec_settings_verify(rd); } # endif @@ -1659,7 +1647,7 @@ static void rna_Scene_mesh_quality_update(Main *bmain, Scene *UNUSED(scene), Poi Scene *scene = (Scene *)ptr->owner_id; FOREACH_SCENE_OBJECT_BEGIN (scene, ob) { - if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_VOLUME, OB_MBALL)) { + if (ELEM(ob->type, OB_MESH, OB_CURVES_LEGACY, OB_VOLUME, OB_MBALL)) { DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); } } @@ -2910,6 +2898,10 @@ static void rna_def_tool_settings(BlenderRNA *brna) RNA_def_property_struct_type(prop, "Sculpt"); RNA_def_property_ui_text(prop, "Sculpt", ""); + prop = RNA_def_property(srna, "curves_sculpt", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "CurvesSculpt"); + RNA_def_property_ui_text(prop, "Curves Sculpt", ""); + prop = RNA_def_property(srna, "use_auto_normalize", PROP_BOOLEAN, PROP_NONE); RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); RNA_def_property_boolean_sdna(prop, NULL, "auto_normalize", 1); @@ -3072,7 +3064,7 @@ static void rna_def_tool_settings(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Proportional Editing Falloff", "Falloff type for proportional editing mode"); /* Abusing id_curve :/ */ - RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); + RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE_LEGACY); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */ prop = RNA_def_property(srna, "proportional_size", PROP_FLOAT, PROP_DISTANCE); @@ -5727,8 +5719,6 @@ static void rna_def_scene_ffmpeg_settings(BlenderRNA *brna) RNA_def_property_enum_items(prop, ffmpeg_format_items); RNA_def_property_enum_default(prop, FFMPEG_MKV); RNA_def_property_ui_text(prop, "Container", "Output file container"); - RNA_def_property_update( - prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_FFmpegSettings_codec_settings_update"); prop = RNA_def_property(srna, "codec", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "codec"); @@ -5736,8 +5726,6 @@ static void rna_def_scene_ffmpeg_settings(BlenderRNA *brna) RNA_def_property_enum_items(prop, ffmpeg_codec_items); RNA_def_property_enum_default(prop, AV_CODEC_ID_H264); RNA_def_property_ui_text(prop, "Video Codec", "FFmpeg codec to use for video output"); - RNA_def_property_update( - prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_FFmpegSettings_codec_settings_update"); prop = RNA_def_property(srna, "video_bitrate", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "video_bitrate"); diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 1e7c67ef95e..473711fb74b 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -352,6 +352,12 @@ static bool rna_Brush_mode_with_tool_poll(PointerRNA *ptr, PointerRNA value) } mode = OB_MODE_WEIGHT_GPENCIL; } + else if (paint_contains_brush_slot(&ts->curves_sculpt->paint, tslot, &slot_index)) { + if (slot_index != brush->curves_sculpt_tool) { + return false; + } + mode = OB_MODE_SCULPT_CURVES; + } return brush->ob_mode & mode; } @@ -417,6 +423,11 @@ static char *rna_UvSculpt_path(PointerRNA *UNUSED(ptr)) return BLI_strdup("tool_settings.uv_sculpt"); } +static char *rna_CurvesSculpt_path(PointerRNA *UNUSED(ptr)) +{ + return BLI_strdup("tool_settings.curves_sculpt"); +} + static char *rna_GpPaint_path(PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.gpencil_paint"); @@ -1492,6 +1503,15 @@ static void rna_def_gpencil_sculpt(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); } +static void rna_def_curves_sculpt(BlenderRNA *brna) +{ + StructRNA *srna; + + srna = RNA_def_struct(brna, "CurvesSculpt", "Paint"); + RNA_def_struct_path_func(srna, "rna_CurvesSculpt_path"); + RNA_def_struct_ui_text(srna, "Curves Sculpt Paint", ""); +} + void RNA_def_sculpt_paint(BlenderRNA *brna) { /* *** Non-Animated *** */ @@ -1510,6 +1530,7 @@ void RNA_def_sculpt_paint(BlenderRNA *brna) rna_def_particle_edit(brna); rna_def_gpencil_guides(brna); rna_def_gpencil_sculpt(brna); + rna_def_curves_sculpt(brna); RNA_define_animate_sdna(true); } diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index c51f0c00498..2344aa42838 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -3263,8 +3263,8 @@ static struct IDFilterEnumPropertyItem rna_enum_space_file_id_filter_categories[ ICON_OUTLINER_COLLECTION, "Objects & Collections", "Show objects and collections"}, - {FILTER_ID_AR | FILTER_ID_CU | FILTER_ID_LT | FILTER_ID_MB | FILTER_ID_ME | FILTER_ID_CV | - FILTER_ID_PT | FILTER_ID_VO, + {FILTER_ID_AR | FILTER_ID_CU_LEGACY | FILTER_ID_LT | FILTER_ID_MB | FILTER_ID_ME | + FILTER_ID_CV | FILTER_ID_PT | FILTER_ID_VO, "category_geometry", ICON_NODETREE, "Geometry", @@ -4959,7 +4959,9 @@ static void rna_def_space_view3d(BlenderRNA *brna) const char *identifier[2]; } info[] = { {"Mesh", (1 << OB_MESH), {"show_object_viewport_mesh", "show_object_select_mesh"}}, - {"Curve", (1 << OB_CURVE), {"show_object_viewport_curve", "show_object_select_curve"}}, + {"Curve", + (1 << OB_CURVES_LEGACY), + {"show_object_viewport_curve", "show_object_select_curve"}}, {"Surface", (1 << OB_SURF), {"show_object_viewport_surf", "show_object_select_surf"}}, {"Meta", (1 << OB_MBALL), {"show_object_viewport_meta", "show_object_select_meta"}}, {"Font", (1 << OB_FONT), {"show_object_viewport_font", "show_object_select_font"}}, @@ -5467,7 +5469,7 @@ static void rna_def_space_sequencer_preview_overlay(BlenderRNA *brna) prop = RNA_def_property(srna, "show_cursor", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_PREVIEW_SHOW_2D_CURSOR); - RNA_def_property_ui_text(prop, "2D cursor", ""); + RNA_def_property_ui_text(prop, "2D Cursor", ""); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); } diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index e32bedf3ed0..2a759dde39a 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -6040,6 +6040,13 @@ static void rna_def_userdef_input(BlenderRNA *brna) "Helicopter Mode", "Device up/down directly controls the Z position of the 3D viewport"); + prop = RNA_def_property(srna, "ndof_lock_camera_pan_zoom", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "ndof_flag", NDOF_CAMERA_PAN_ZOOM); + RNA_def_property_ui_text( + prop, + "Lock Camera Pan/Zoom", + "Pan/zoom the camera view instead of leaving the camera view when orbiting"); + /* let Python know whether NDOF is enabled */ prop = RNA_def_boolean(srna, "use_ndof", true, "", ""); # else diff --git a/source/blender/makesrna/intern/rna_volume.c b/source/blender/makesrna/intern/rna_volume.c index ca3b09c61c5..5b323629a80 100644 --- a/source/blender/makesrna/intern/rna_volume.c +++ b/source/blender/makesrna/intern/rna_volume.c @@ -26,7 +26,6 @@ const EnumPropertyItem rna_enum_volume_grid_data_type_items[] = { {VOLUME_GRID_INT, "INT", 0, "Integer", "32-bit integer"}, {VOLUME_GRID_INT64, "INT64", 0, "Integer 64-bit", "64-bit integer"}, {VOLUME_GRID_MASK, "MASK", 0, "Mask", "No data, boolean mask of active voxels"}, - {VOLUME_GRID_STRING, "STRING", 0, "String", "Text string"}, {VOLUME_GRID_VECTOR_FLOAT, "VECTOR_FLOAT", 0, "Float Vector", "3D float vector"}, {VOLUME_GRID_VECTOR_DOUBLE, "VECTOR_DOUBLE", 0, "Double Vector", "3D double vector"}, {VOLUME_GRID_VECTOR_INT, "VECTOR_INT", 0, "Integer Vector", "3D integer vector"}, diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index b0021bd0f3d..8835591f303 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -28,13 +28,6 @@ #ifdef RNA_RUNTIME -static const EnumPropertyItem event_tweak_type_items[] = { - {EVT_TWEAK_L, "EVT_TWEAK_L", 0, "Left", ""}, - {EVT_TWEAK_M, "EVT_TWEAK_M", 0, "Middle", ""}, - {EVT_TWEAK_R, "EVT_TWEAK_R", 0, "Right", ""}, - {0, NULL, 0, NULL, NULL}, -}; - static const EnumPropertyItem event_mouse_type_items[] = { {LEFTMOUSE, "LEFTMOUSE", 0, "Left", ""}, {MIDDLEMOUSE, "MIDDLEMOUSE", 0, "Middle", ""}, @@ -156,10 +149,6 @@ const EnumPropertyItem rna_enum_event_type_items[] = { {WHEELINMOUSE, "WHEELINMOUSE", 0, "Wheel In", "WhIn"}, {WHEELOUTMOUSE, "WHEELOUTMOUSE", 0, "Wheel Out", "WhOut"}, {0, "", 0, NULL, NULL}, - {EVT_TWEAK_L, "EVT_TWEAK_L", 0, "Tweak Left", "TwkL"}, - {EVT_TWEAK_M, "EVT_TWEAK_M", 0, "Tweak Middle", "TwkM"}, - {EVT_TWEAK_R, "EVT_TWEAK_R", 0, "Tweak Right", "TwkR"}, - {0, "", 0, NULL, NULL}, {EVT_AKEY, "A", 0, "A", ""}, {EVT_BKEY, "B", 0, "B", ""}, {EVT_CKEY, "C", 0, "C", ""}, @@ -362,26 +351,7 @@ const EnumPropertyItem rna_enum_event_type_items[] = { * This is needed for `km.keymap_items.new` value argument, * to accept values from different types. */ -const EnumPropertyItem rna_enum_event_value_all_items[] = { - {KM_ANY, "ANY", 0, "Any", ""}, - {KM_PRESS, "PRESS", 0, "Press", ""}, - {KM_RELEASE, "RELEASE", 0, "Release", ""}, - {KM_CLICK, "CLICK", 0, "Click", ""}, - {KM_DBL_CLICK, "DOUBLE_CLICK", 0, "Double Click", ""}, - {KM_CLICK_DRAG, "CLICK_DRAG", 0, "Click Drag", ""}, - {EVT_GESTURE_N, "NORTH", 0, "North", ""}, - {EVT_GESTURE_NE, "NORTH_EAST", 0, "North-East", ""}, - {EVT_GESTURE_E, "EAST", 0, "East", ""}, - {EVT_GESTURE_SE, "SOUTH_EAST", 0, "South-East", ""}, - {EVT_GESTURE_S, "SOUTH", 0, "South", ""}, - {EVT_GESTURE_SW, "SOUTH_WEST", 0, "South-West", ""}, - {EVT_GESTURE_W, "WEST", 0, "West", ""}, - {EVT_GESTURE_NW, "NORTH_WEST", 0, "North-West", ""}, - {KM_NOTHING, "NOTHING", 0, "Nothing", ""}, - {0, NULL, 0, NULL, NULL}, -}; - -const EnumPropertyItem rna_enum_event_value_keymouse_items[] = { +const EnumPropertyItem rna_enum_event_value_items[] = { {KM_ANY, "ANY", 0, "Any", ""}, {KM_PRESS, "PRESS", 0, "Press", ""}, {KM_RELEASE, "RELEASE", 0, "Release", ""}, @@ -393,16 +363,16 @@ const EnumPropertyItem rna_enum_event_value_keymouse_items[] = { {0, NULL, 0, NULL, NULL}, }; -const EnumPropertyItem rna_enum_event_value_tweak_items[] = { +const EnumPropertyItem rna_enum_event_direction_items[] = { {KM_ANY, "ANY", 0, "Any", ""}, - {EVT_GESTURE_N, "NORTH", 0, "North", ""}, - {EVT_GESTURE_NE, "NORTH_EAST", 0, "North-East", ""}, - {EVT_GESTURE_E, "EAST", 0, "East", ""}, - {EVT_GESTURE_SE, "SOUTH_EAST", 0, "South-East", ""}, - {EVT_GESTURE_S, "SOUTH", 0, "South", ""}, - {EVT_GESTURE_SW, "SOUTH_WEST", 0, "South-West", ""}, - {EVT_GESTURE_W, "WEST", 0, "West", ""}, - {EVT_GESTURE_NW, "NORTH_WEST", 0, "North-West", ""}, + {KM_DIRECTION_N, "NORTH", 0, "North", ""}, + {KM_DIRECTION_NE, "NORTH_EAST", 0, "North-East", ""}, + {KM_DIRECTION_E, "EAST", 0, "East", ""}, + {KM_DIRECTION_SE, "SOUTH_EAST", 0, "South-East", ""}, + {KM_DIRECTION_S, "SOUTH", 0, "South", ""}, + {KM_DIRECTION_SW, "SOUTH_WEST", 0, "South-West", ""}, + {KM_DIRECTION_W, "WEST", 0, "West", ""}, + {KM_DIRECTION_NW, "NORTH_WEST", 0, "North-West", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -420,7 +390,6 @@ const EnumPropertyItem rna_enum_event_type_mask_items[] = { {EVT_TYPE_MASK_MOUSE_BUTTON, "MOUSE_BUTTON", 0, "Mouse Button", ""}, {EVT_TYPE_MASK_MOUSE, "MOUSE", 0, "Mouse", ""}, {EVT_TYPE_MASK_NDOF, "NDOF", 0, "NDOF", ""}, - {EVT_TYPE_MASK_TWEAK, "TWEAK", 0, "Tweak", ""}, {EVT_TYPE_MASK_ACTIONZONE, "ACTIONZONE", 0, "Action Zone", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -612,18 +581,6 @@ static PointerRNA rna_OperatorMacro_properties_get(PointerRNA *ptr) return result; } -static const EnumPropertyItem *rna_Event_value_itemf(bContext *UNUSED(C), - PointerRNA *ptr, - PropertyRNA *UNUSED(prop), - bool *UNUSED(r_free)) -{ - const wmEvent *event = ptr->data; - if (ISTWEAK(event->type)) { - return rna_enum_event_value_tweak_items; - } - return rna_enum_event_value_all_items; -} - static void rna_Event_ascii_get(PointerRNA *ptr, char *value) { const wmEvent *event = ptr->data; @@ -668,7 +625,7 @@ static int rna_Event_unicode_length(PointerRNA *ptr) static bool rna_Event_is_repeat_get(PointerRNA *ptr) { const wmEvent *event = ptr->data; - return event->is_repeat; + return (event->flag & WM_EVENT_IS_REPEAT) != 0; } static float rna_Event_pressure_get(PointerRNA *ptr) @@ -915,10 +872,6 @@ static void rna_wmKeyMapItem_map_type_set(PointerRNA *ptr, int value) kmi->type = EVT_AKEY; kmi->val = KM_PRESS; break; - case KMI_TYPE_TWEAK: - kmi->type = EVT_TWEAK_L; - kmi->val = KM_ANY; - break; case KMI_TYPE_MOUSE: kmi->type = LEFTMOUSE; kmi->val = KM_PRESS; @@ -969,9 +922,6 @@ static const EnumPropertyItem *rna_KeyMapItem_type_itemf(bContext *UNUSED(C), if (map_type == KMI_TYPE_MOUSE) { return event_mouse_type_items; } - if (map_type == KMI_TYPE_TWEAK) { - return event_tweak_type_items; - } if (map_type == KMI_TYPE_TIMER) { return event_timer_type_items; } @@ -986,24 +936,6 @@ static const EnumPropertyItem *rna_KeyMapItem_type_itemf(bContext *UNUSED(C), } } -static const EnumPropertyItem *rna_KeyMapItem_value_itemf(bContext *UNUSED(C), - PointerRNA *ptr, - PropertyRNA *UNUSED(prop), - bool *UNUSED(r_free)) -{ - int map_type = rna_wmKeyMapItem_map_type_get(ptr); - - if (map_type == KMI_TYPE_MOUSE || map_type == KMI_TYPE_KEYBOARD || map_type == KMI_TYPE_NDOF) { - return rna_enum_event_value_keymouse_items; - } - if (map_type == KMI_TYPE_TWEAK) { - return rna_enum_event_value_tweak_items; - } - else { - return rna_enum_event_value_all_items; - } -} - static const EnumPropertyItem *rna_KeyMapItem_propvalue_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), @@ -2106,8 +2038,7 @@ static void rna_def_event(BlenderRNA *brna) /* enums */ prop = RNA_def_property(srna, "value", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "val"); - RNA_def_property_enum_items(prop, rna_enum_event_value_all_items); - RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_Event_value_itemf"); + RNA_def_property_enum_items(prop, rna_enum_event_value_items); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Value", "The type of event, only applies to some"); @@ -2118,6 +2049,12 @@ static void rna_def_event(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Type", ""); + prop = RNA_def_property(srna, "direction", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "direction"); + RNA_def_property_enum_items(prop, rna_enum_event_direction_items); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Direction", "The direction (only applies to drag events)"); + /* keyboard */ prop = RNA_def_property(srna, "is_repeat", PROP_BOOLEAN, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); @@ -2194,23 +2131,23 @@ static void rna_def_event(BlenderRNA *brna) /* modifiers */ prop = RNA_def_property(srna, "shift", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "shift", 1); + RNA_def_property_boolean_sdna(prop, NULL, "modifier", KM_SHIFT); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Shift", "True when the Shift key is held"); RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_WINDOWMANAGER); prop = RNA_def_property(srna, "ctrl", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "ctrl", 1); + RNA_def_property_boolean_sdna(prop, NULL, "modifier", KM_CTRL); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Ctrl", "True when the Ctrl key is held"); prop = RNA_def_property(srna, "alt", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "alt", 1); + RNA_def_property_boolean_sdna(prop, NULL, "modifier", KM_ALT); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Alt", "True when the Alt/Option key is held"); prop = RNA_def_property(srna, "oskey", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "oskey", 1); + RNA_def_property_boolean_sdna(prop, NULL, "modifier", KM_OSKEY); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "OS Key", "True when the Cmd key is held"); @@ -2538,7 +2475,6 @@ static void rna_def_keyconfig(BlenderRNA *brna) static const EnumPropertyItem map_type_items[] = { {KMI_TYPE_KEYBOARD, "KEYBOARD", 0, "Keyboard", ""}, - {KMI_TYPE_TWEAK, "TWEAK", 0, "Tweak", ""}, {KMI_TYPE_MOUSE, "MOUSE", 0, "Mouse", ""}, {KMI_TYPE_NDOF, "NDOF", 0, "NDOF", ""}, {KMI_TYPE_TEXTINPUT, "TEXTINPUT", 0, "Text Input", ""}, @@ -2679,11 +2615,16 @@ static void rna_def_keyconfig(BlenderRNA *brna) prop = RNA_def_property(srna, "value", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "val"); - RNA_def_property_enum_items(prop, rna_enum_event_value_all_items); - RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_KeyMapItem_value_itemf"); + RNA_def_property_enum_items(prop, rna_enum_event_value_items); RNA_def_property_ui_text(prop, "Value", ""); RNA_def_property_update(prop, 0, "rna_KeyMapItem_update"); + prop = RNA_def_property(srna, "direction", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "direction"); + RNA_def_property_enum_items(prop, rna_enum_event_direction_items); + RNA_def_property_ui_text(prop, "Direction", "The direction (only applies to drag events)"); + RNA_def_property_update(prop, 0, "rna_KeyMapItem_update"); + prop = RNA_def_property(srna, "id", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "id"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); diff --git a/source/blender/makesrna/intern/rna_wm_api.c b/source/blender/makesrna/intern/rna_wm_api.c index 81cc72088a6..0589fa7a96e 100644 --- a/source/blender/makesrna/intern/rna_wm_api.c +++ b/source/blender/makesrna/intern/rna_wm_api.c @@ -258,6 +258,7 @@ static wmKeyMapItem *rna_KeyMap_item_new(wmKeyMap *km, int alt, int oskey, int keymodifier, + int direction, bool repeat, bool head) { @@ -275,7 +276,7 @@ static wmKeyMapItem *rna_KeyMap_item_new(wmKeyMap *km, WM_operator_bl_idname(idname_bl, idname); /* create keymap item */ - kmi = WM_keymap_add_item(km, idname_bl, type, value, modifier, keymodifier); + kmi = WM_keymap_add_item(km, idname_bl, type, value, modifier, keymodifier, direction); if (!repeat) { kmi->flag |= KMI_REPEAT_IGNORE; @@ -324,6 +325,7 @@ static wmKeyMapItem *rna_KeyMap_item_new_modal(wmKeyMap *km, int alt, int oskey, int keymodifier, + int direction, bool repeat) { /* only modal maps */ @@ -338,13 +340,14 @@ static wmKeyMapItem *rna_KeyMap_item_new_modal(wmKeyMap *km, /* not initialized yet, do delayed lookup */ if (!km->modal_items) { - kmi = WM_modalkeymap_add_item_str(km, type, value, modifier, keymodifier, propvalue_str); + kmi = WM_modalkeymap_add_item_str( + km, type, value, modifier, keymodifier, direction, propvalue_str); } else { if (RNA_enum_value_from_id(km->modal_items, propvalue_str, &propvalue) == 0) { BKE_report(reports, RPT_WARNING, "Property value not in enumeration"); } - kmi = WM_modalkeymap_add_item(km, type, value, modifier, keymodifier, propvalue); + kmi = WM_modalkeymap_add_item(km, type, value, modifier, keymodifier, direction, propvalue); } if (!repeat) { @@ -635,14 +638,23 @@ static wmEvent *rna_Window_event_add_simulate(wmWindow *win, wmEvent e = *win->eventstate; e.type = type; e.val = value; - e.is_repeat = false; + e.flag = 0; e.xy[0] = x; e.xy[1] = y; - e.shift = shift; - e.ctrl = ctrl; - e.alt = alt; - e.oskey = oskey; + e.modifier = 0; + if (shift) { + e.modifier |= KM_SHIFT; + } + if (ctrl) { + e.modifier |= KM_CTRL; + } + if (alt) { + e.modifier |= KM_ALT; + } + if (oskey) { + e.modifier |= KM_OSKEY; + } e.ascii = '\0'; e.utf8_buf[0] = '\0'; @@ -720,7 +732,7 @@ void RNA_api_window(StructRNA *srna) RNA_def_function_flag(func, FUNC_USE_REPORTS); parm = RNA_def_enum(func, "type", rna_enum_event_type_items, 0, "Type", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); - parm = RNA_def_enum(func, "value", rna_enum_event_value_all_items, 0, "Value", ""); + parm = RNA_def_enum(func, "value", rna_enum_event_value_items, 0, "Value", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); parm = RNA_def_string(func, "unicode", NULL, 0, "", ""); RNA_def_parameter_clear_flags(parm, PROP_NEVER_NULL, 0); @@ -1125,7 +1137,7 @@ void RNA_api_keymapitems(StructRNA *srna) RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); parm = RNA_def_enum(func, "type", rna_enum_event_type_items, 0, "Type", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); - parm = RNA_def_enum(func, "value", rna_enum_event_value_all_items, 0, "Value", ""); + parm = RNA_def_enum(func, "value", rna_enum_event_value_items, 0, "Value", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); RNA_def_boolean(func, "any", 0, "Any", ""); RNA_def_int(func, "shift", KM_NOTHING, KM_ANY, KM_MOD_HELD, "Shift", "", KM_ANY, KM_MOD_HELD); @@ -1133,6 +1145,7 @@ void RNA_api_keymapitems(StructRNA *srna) RNA_def_int(func, "alt", KM_NOTHING, KM_ANY, KM_MOD_HELD, "Alt", "", KM_ANY, KM_MOD_HELD); RNA_def_int(func, "oskey", KM_NOTHING, KM_ANY, KM_MOD_HELD, "OS Key", "", KM_ANY, KM_MOD_HELD); RNA_def_enum(func, "key_modifier", rna_enum_event_type_items, 0, "Key Modifier", ""); + RNA_def_enum(func, "direction", rna_enum_event_direction_items, KM_ANY, "Direction", ""); RNA_def_boolean(func, "repeat", false, "Repeat", "When set, accept key-repeat events"); RNA_def_boolean(func, "head", @@ -1149,7 +1162,7 @@ void RNA_api_keymapitems(StructRNA *srna) RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); parm = RNA_def_enum(func, "type", rna_enum_event_type_items, 0, "Type", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); - parm = RNA_def_enum(func, "value", rna_enum_event_value_all_items, 0, "Value", ""); + parm = RNA_def_enum(func, "value", rna_enum_event_value_items, 0, "Value", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); RNA_def_boolean(func, "any", 0, "Any", ""); RNA_def_int(func, "shift", KM_NOTHING, KM_ANY, KM_MOD_HELD, "Shift", "", KM_ANY, KM_MOD_HELD); @@ -1157,6 +1170,7 @@ void RNA_api_keymapitems(StructRNA *srna) RNA_def_int(func, "alt", KM_NOTHING, KM_ANY, KM_MOD_HELD, "Alt", "", KM_ANY, KM_MOD_HELD); RNA_def_int(func, "oskey", KM_NOTHING, KM_ANY, KM_MOD_HELD, "OS Key", "", KM_ANY, KM_MOD_HELD); RNA_def_enum(func, "key_modifier", rna_enum_event_type_items, 0, "Key Modifier", ""); + RNA_def_enum(func, "direction", rna_enum_event_direction_items, KM_ANY, "Direction", ""); RNA_def_boolean(func, "repeat", false, "Repeat", "When set, accept key-repeat events"); parm = RNA_def_pointer(func, "item", "KeyMapItem", "Item", "Added key map item"); RNA_def_function_return(func, parm); diff --git a/source/blender/makesrna/intern/rna_xr.c b/source/blender/makesrna/intern/rna_xr.c index 87d8bc8d844..9fe3153eb1e 100644 --- a/source/blender/makesrna/intern/rna_xr.c +++ b/source/blender/makesrna/intern/rna_xr.c @@ -46,6 +46,43 @@ static wmXrData *rna_XrSession_wm_xr_data_get(PointerRNA *ptr) /** \name XR Action Map * \{ */ +static XrComponentPath *rna_XrComponentPath_new(XrActionMapBinding *amb, const char *path_str) +{ +# ifdef WITH_XR_OPENXR + XrComponentPath *component_path = MEM_callocN(sizeof(XrComponentPath), __func__); + BLI_strncpy(component_path->path, path_str, sizeof(component_path->path)); + BLI_addtail(&amb->component_paths, component_path); + return component_path; +# else + UNUSED_VARS(amb, path_str); + return NULL; +# endif +} + +static void rna_XrComponentPath_remove(XrActionMapBinding *amb, PointerRNA *component_path_ptr) +{ +# ifdef WITH_XR_OPENXR + XrComponentPath *component_path = component_path_ptr->data; + int idx = BLI_findindex(&amb->component_paths, component_path); + if (idx != -1) { + BLI_freelinkN(&amb->component_paths, component_path); + } + RNA_POINTER_INVALIDATE(component_path_ptr); +# else + UNUSED_VARS(amb, component_path_ptr); +# endif +} + +static XrComponentPath *rna_XrComponentPath_find(XrActionMapBinding *amb, const char *path_str) +{ +# ifdef WITH_XR_OPENXR + return BLI_findstring(&amb->component_paths, path_str, offsetof(XrComponentPath, path)); +# else + UNUSED_VARS(amb, path_str); + return NULL; +# endif +} + static XrActionMapBinding *rna_XrActionMapBinding_new(XrActionMapItem *ami, const char *name, bool replace_existing) @@ -99,6 +136,28 @@ static XrActionMapBinding *rna_XrActionMapBinding_find(XrActionMapItem *ami, con # endif } +static void rna_XrActionMapBinding_component_paths_begin(CollectionPropertyIterator *iter, + PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapBinding *amb = (XrActionMapBinding *)ptr->data; + rna_iterator_listbase_begin(iter, &amb->component_paths, NULL); +# else + UNUSED_VARS(iter, ptr); +# endif +} + +static int rna_XrActionMapBinding_component_paths_length(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapBinding *amb = (XrActionMapBinding *)ptr->data; + return BLI_listbase_count(&amb->component_paths); +# else + UNUSED_VARS(ptr); + return 0; +# endif +} + static int rna_XrActionMapBinding_axis0_region_get(PointerRNA *ptr) { # ifdef WITH_XR_OPENXR @@ -174,6 +233,43 @@ static void rna_XrActionMapBinding_name_update(Main *bmain, Scene *UNUSED(scene) # endif } +static XrUserPath *rna_XrUserPath_new(XrActionMapItem *ami, const char *path_str) +{ +# ifdef WITH_XR_OPENXR + XrUserPath *user_path = MEM_callocN(sizeof(XrUserPath), __func__); + BLI_strncpy(user_path->path, path_str, sizeof(user_path->path)); + BLI_addtail(&ami->user_paths, user_path); + return user_path; +# else + UNUSED_VARS(ami, path_str); + return NULL; +# endif +} + +static void rna_XrUserPath_remove(XrActionMapItem *ami, PointerRNA *user_path_ptr) +{ +# ifdef WITH_XR_OPENXR + XrUserPath *user_path = user_path_ptr->data; + int idx = BLI_findindex(&ami->user_paths, user_path); + if (idx != -1) { + BLI_freelinkN(&ami->user_paths, user_path); + } + RNA_POINTER_INVALIDATE(user_path_ptr); +# else + UNUSED_VARS(ami, user_path_ptr); +# endif +} + +static XrUserPath *rna_XrUserPath_find(XrActionMapItem *ami, const char *path_str) +{ +# ifdef WITH_XR_OPENXR + return BLI_findstring(&ami->user_paths, path_str, offsetof(XrUserPath, path)); +# else + UNUSED_VARS(ami, path_str); + return NULL; +# endif +} + static XrActionMapItem *rna_XrActionMapItem_new(XrActionMap *am, const char *name, bool replace_existing) @@ -222,6 +318,27 @@ static XrActionMapItem *rna_XrActionMapItem_find(XrActionMap *am, const char *na # endif } +static void rna_XrActionMapItem_user_paths_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = (XrActionMapItem *)ptr->data; + rna_iterator_listbase_begin(iter, &ami->user_paths, NULL); +# else + UNUSED_VARS(iter, ptr); +# endif +} + +static int rna_XrActionMapItem_user_paths_length(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = (XrActionMapItem *)ptr->data; + return BLI_listbase_count(&ami->user_paths); +# else + UNUSED_VARS(ptr); + return 0; +# endif +} + static void rna_XrActionMapItem_op_name_get(PointerRNA *ptr, char *value) { # ifdef WITH_XR_OPENXR @@ -395,6 +512,27 @@ static void rna_XrActionMapItem_pose_is_controller_aim_set(PointerRNA *ptr, bool # endif } +static void rna_XrActionMapItem_bindings_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = (XrActionMapItem *)ptr->data; + rna_iterator_listbase_begin(iter, &ami->bindings, NULL); +# else + UNUSED_VARS(iter, ptr); +# endif +} + +static int rna_XrActionMapItem_bindings_length(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMapItem *ami = (XrActionMapItem *)ptr->data; + return BLI_listbase_count(&ami->bindings); +# else + UNUSED_VARS(ptr); + return 0; +# endif +} + static void rna_XrActionMapItem_name_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) { # ifdef WITH_XR_OPENXR @@ -471,6 +609,27 @@ static XrActionMap *rna_XrActionMap_find(PointerRNA *ptr, const char *name) # endif } +static void rna_XrActionMap_items_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMap *actionmap = (XrActionMap *)ptr->data; + rna_iterator_listbase_begin(iter, &actionmap->items, NULL); +# else + UNUSED_VARS(iter, ptr); +# endif +} + +static int rna_XrActionMap_items_length(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + XrActionMap *actionmap = (XrActionMap *)ptr->data; + return BLI_listbase_count(&actionmap->items); +# else + UNUSED_VARS(ptr); + return 0; +# endif +} + static void rna_XrActionMap_name_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) { # ifdef WITH_XR_OPENXR @@ -576,26 +735,8 @@ static bool rna_XrSessionState_action_create(bContext *C, { # 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; - } + if (BLI_listbase_is_empty(&ami->user_paths)) { + return false; } const bool is_float_action = (ami->type == XR_FLOAT_INPUT || ami->type == XR_VECTOR2F_INPUT); @@ -621,8 +762,7 @@ static bool rna_XrSessionState_action_create(bContext *C, actionmap->name, ami->name, ami->type, - count_subaction_paths, - subaction_paths, + &ami->user_paths, ot, op_properties, is_button_action ? ami->haptic_name : NULL, @@ -645,30 +785,10 @@ static bool rna_XrSessionState_action_binding_create(bContext *C, { # 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 int count_user_paths = BLI_listbase_count(&ami->user_paths); + const int count_component_paths = BLI_listbase_count(&amb->component_paths); + if (count_user_paths < 1 || (count_user_paths != count_component_paths)) { + return false; } const bool is_float_action = (ami->type == XR_FLOAT_INPUT || ami->type == XR_VECTOR2F_INPUT); @@ -695,9 +815,8 @@ static bool rna_XrSessionState_action_binding_create(bContext *C, actionmap->name, ami->name, amb->profile, - count_subaction_paths, - subaction_paths, - component_paths, + &ami->user_paths, + &amb->component_paths, is_float_action ? float_thresholds : NULL, is_button_action ? axis_flags : NULL, is_pose_action ? poses : NULL); @@ -954,6 +1073,18 @@ static void rna_XrSessionState_actionmaps_begin(CollectionPropertyIterator *iter # endif } +static int rna_XrSessionState_actionmaps_length(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + ListBase *lb = WM_xr_actionmaps_get(xr->runtime); + return BLI_listbase_count(lb); +# else + UNUSED_VARS(ptr); + return 0; +# endif +} + static int rna_XrSessionState_active_actionmap_get(PointerRNA *ptr) { # ifdef WITH_XR_OPENXR @@ -1240,6 +1371,42 @@ static const EnumPropertyItem rna_enum_xr_axis1_flags[] = { /** \name XR Action Map * \{ */ +static void rna_def_xr_component_paths(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + FunctionRNA *func; + PropertyRNA *parm; + + RNA_def_property_srna(cprop, "XrComponentPaths"); + srna = RNA_def_struct(brna, "XrComponentPaths", NULL); + RNA_def_struct_sdna(srna, "XrActionMapBinding"); + RNA_def_struct_ui_text(srna, "XR Component Paths", "Collection of OpenXR component paths"); + + func = RNA_def_function(srna, "new", "rna_XrComponentPath_new"); + parm = RNA_def_string( + func, "path", NULL, XR_MAX_COMPONENT_PATH_LENGTH, "Path", "OpenXR component path"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer( + func, "component_path", "XrComponentPath", "Component Path", "Added component path"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "remove", "rna_XrComponentPath_remove"); + parm = RNA_def_pointer(func, "component_path", "XrComponentPath", "Component Path", ""); + 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_XrComponentPath_find"); + parm = RNA_def_string( + func, "path", NULL, XR_MAX_COMPONENT_PATH_LENGTH, "Path", "OpenXR component path"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer(func, + "component_path", + "XrComponentPath", + "Component Path", + "The component path with the given path"); + RNA_def_function_return(func, parm); +} + static void rna_def_xr_actionmap_bindings(BlenderRNA *brna, PropertyRNA *cprop) { StructRNA *srna; @@ -1289,6 +1456,36 @@ static void rna_def_xr_actionmap_bindings(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_function_return(func, parm); } +static void rna_def_xr_user_paths(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + FunctionRNA *func; + PropertyRNA *parm; + + RNA_def_property_srna(cprop, "XrUserPaths"); + srna = RNA_def_struct(brna, "XrUserPaths", NULL); + RNA_def_struct_sdna(srna, "XrActionMapItem"); + RNA_def_struct_ui_text(srna, "XR User Paths", "Collection of OpenXR user paths"); + + func = RNA_def_function(srna, "new", "rna_XrUserPath_new"); + parm = RNA_def_string(func, "path", NULL, XR_MAX_USER_PATH_LENGTH, "Path", "OpenXR user path"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer(func, "user_path", "XrUserPath", "User Path", "Added user path"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "remove", "rna_XrUserPath_remove"); + parm = RNA_def_pointer(func, "user_path", "XrUserPath", "User Path", ""); + 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_XrUserPath_find"); + parm = RNA_def_string(func, "path", NULL, XR_MAX_USER_PATH_LENGTH, "Path", "OpenXR user path"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer( + func, "user_path", "XrUserPath", "User Path", "The user path with the given path"); + RNA_def_function_return(func, parm); +} + static void rna_def_xr_actionmap_items(BlenderRNA *brna, PropertyRNA *cprop) { StructRNA *srna; @@ -1404,6 +1601,15 @@ static void rna_def_xr_actionmap(BlenderRNA *brna) 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_collection_funcs(prop, + "rna_XrActionMap_items_begin", + "rna_iterator_listbase_next", + "rna_iterator_listbase_end", + "rna_iterator_listbase_get", + "rna_XrActionMap_items_length", + NULL, + NULL, + NULL); RNA_def_property_ui_text( prop, "Items", @@ -1414,6 +1620,15 @@ static void rna_def_xr_actionmap(BlenderRNA *brna) RNA_def_property_int_sdna(prop, NULL, "selitem"); RNA_def_property_ui_text(prop, "Selected Item", ""); + /* XrUserPath */ + srna = RNA_def_struct(brna, "XrUserPath", NULL); + RNA_def_struct_sdna(srna, "XrUserPath"); + RNA_def_struct_ui_text(srna, "XR User Path", ""); + + prop = RNA_def_property(srna, "path", PROP_STRING, PROP_NONE); + RNA_def_property_string_maxlength(prop, XR_MAX_USER_PATH_LENGTH); + RNA_def_property_ui_text(prop, "Path", "OpenXR user path"); + /* XrActionMapItem */ srna = RNA_def_struct(brna, "XrActionMapItem", NULL); RNA_def_struct_sdna(srna, "XrActionMapItem"); @@ -1429,13 +1644,19 @@ static void rna_def_xr_actionmap(BlenderRNA *brna) 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, "user_paths", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "XrUserPath"); + RNA_def_property_collection_funcs(prop, + "rna_XrActionMapItem_user_paths_begin", + "rna_iterator_listbase_next", + "rna_iterator_listbase_end", + "rna_iterator_listbase_get", + "rna_XrActionMapItem_user_paths_length", + NULL, + NULL, + NULL); + RNA_def_property_ui_text(prop, "User Paths", "OpenXR user paths"); + rna_def_xr_user_paths(brna, prop); prop = RNA_def_property(srna, "op", PROP_STRING, PROP_NONE); RNA_def_property_string_maxlength(prop, OP_MAX_TYPENAME); @@ -1520,6 +1741,15 @@ static void rna_def_xr_actionmap(BlenderRNA *brna) prop = RNA_def_property(srna, "bindings", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "XrActionMapBinding"); + RNA_def_property_collection_funcs(prop, + "rna_XrActionMapItem_bindings_begin", + "rna_iterator_listbase_next", + "rna_iterator_listbase_end", + "rna_iterator_listbase_get", + "rna_XrActionMapItem_bindings_length", + NULL, + NULL, + NULL); 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); @@ -1528,6 +1758,15 @@ static void rna_def_xr_actionmap(BlenderRNA *brna) RNA_def_property_int_sdna(prop, NULL, "selbinding"); RNA_def_property_ui_text(prop, "Selected Binding", "Currently selected binding"); + /* XrComponentPath */ + srna = RNA_def_struct(brna, "XrComponentPath", NULL); + RNA_def_struct_sdna(srna, "XrComponentPath"); + RNA_def_struct_ui_text(srna, "XR Component Path", ""); + + prop = RNA_def_property(srna, "path", PROP_STRING, PROP_NONE); + RNA_def_property_string_maxlength(prop, XR_MAX_COMPONENT_PATH_LENGTH); + RNA_def_property_ui_text(prop, "Path", "OpenXR component path"); + /* XrActionMapBinding */ srna = RNA_def_struct(brna, "XrActionMapBinding", NULL); RNA_def_struct_sdna(srna, "XrActionMapBinding"); @@ -1542,13 +1781,19 @@ static void rna_def_xr_actionmap(BlenderRNA *brna) 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, "component_paths", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "XrComponentPath"); + RNA_def_property_collection_funcs(prop, + "rna_XrActionMapBinding_component_paths_begin", + "rna_iterator_listbase_next", + "rna_iterator_listbase_end", + "rna_iterator_listbase_get", + "rna_XrActionMapBinding_component_paths_length", + NULL, + NULL, + NULL); + RNA_def_property_ui_text(prop, "Component Paths", "OpenXR component paths"); + rna_def_xr_component_paths(brna, prop); prop = RNA_def_property(srna, "threshold", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "float_threshold"); @@ -1812,7 +2057,7 @@ static void rna_def_xr_session_state(BlenderRNA *brna) 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"); + parm = RNA_def_string(func, "action_set", NULL, MAX_NAME, "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); @@ -1823,19 +2068,19 @@ static void rna_def_xr_session_state(BlenderRNA *brna) 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"); + parm = RNA_def_string(func, "action_set", NULL, MAX_NAME, "Action Set", "Action set name"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); parm = RNA_def_string(func, "grip_action", NULL, - 64, + MAX_NAME, "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, + MAX_NAME, "Aim Action", "Name of the action representing the controller aims"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); @@ -1847,11 +2092,12 @@ static void rna_def_xr_session_state(BlenderRNA *brna) 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"); + parm = RNA_def_string(func, "action_set_name", NULL, MAX_NAME, "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"); + parm = RNA_def_string(func, "action_name", NULL, MAX_NAME, "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"); + parm = RNA_def_string( + func, "user_path", NULL, XR_MAX_USER_PATH_LENGTH, "User Path", "OpenXR user path"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); parm = RNA_def_float_array( func, @@ -1871,15 +2117,15 @@ static void rna_def_xr_session_state(BlenderRNA *brna) 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"); + parm = RNA_def_string(func, "action_set_name", NULL, MAX_NAME, "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"); + parm = RNA_def_string(func, "action_name", NULL, MAX_NAME, "Action", "Action name"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); parm = RNA_def_string( func, "user_path", NULL, - 64, + XR_MAX_USER_PATH_LENGTH, "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); @@ -1922,15 +2168,15 @@ static void rna_def_xr_session_state(BlenderRNA *brna) 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"); + parm = RNA_def_string(func, "action_set_name", NULL, MAX_NAME, "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"); + parm = RNA_def_string(func, "action_name", NULL, MAX_NAME, "Action", "Action name"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); parm = RNA_def_string( func, "user_path", NULL, - 64, + XR_MAX_USER_PATH_LENGTH, "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); @@ -2068,16 +2314,16 @@ static void rna_def_xr_session_state(BlenderRNA *brna) "Additional scale multiplier to apply to base scale when determining viewer scale"); prop = RNA_def_property(srna, "actionmaps", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "XrActionMap"); RNA_def_property_collection_funcs(prop, "rna_XrSessionState_actionmaps_begin", "rna_iterator_listbase_next", "rna_iterator_listbase_end", "rna_iterator_listbase_get", - NULL, + "rna_XrSessionState_actionmaps_length", 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); diff --git a/source/blender/modifiers/intern/MOD_array.c b/source/blender/modifiers/intern/MOD_array.c index 2cc3bb8c916..ea42ac761ea 100644 --- a/source/blender/modifiers/intern/MOD_array.c +++ b/source/blender/modifiers/intern/MOD_array.c @@ -371,7 +371,7 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd, int tot_doubles; const bool use_merge = (amd->flags & MOD_ARR_MERGE) != 0; - const bool use_recalc_normals = (mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL) || use_merge; + const bool use_recalc_normals = BKE_mesh_vertex_normals_are_dirty(mesh) || use_merge; const bool use_offset_ob = ((amd->offset_type & MOD_ARR_OFF_OBJ) && amd->offset_ob != NULL); int start_cap_nverts = 0, start_cap_nedges = 0, start_cap_npolys = 0, start_cap_nloops = 0; @@ -819,7 +819,7 @@ static bool isDisabled(const struct Scene *UNUSED(scene), * In other cases it should be impossible to have a type mismatch. */ - if (amd->curve_ob && amd->curve_ob->type != OB_CURVE) { + if (amd->curve_ob && amd->curve_ob->type != OB_CURVES_LEGACY) { return true; } if (amd->start_cap && amd->start_cap->type != OB_MESH) { diff --git a/source/blender/modifiers/intern/MOD_curve.c b/source/blender/modifiers/intern/MOD_curve.c index ca5d59433f0..d1c7dd43f15 100644 --- a/source/blender/modifiers/intern/MOD_curve.c +++ b/source/blender/modifiers/intern/MOD_curve.c @@ -71,7 +71,7 @@ static bool isDisabled(const Scene *UNUSED(scene), ModifierData *md, bool UNUSED * * In other cases it should be impossible to have a type mismatch. */ - return !cmd->object || cmd->object->type != OB_CURVE; + return !cmd->object || cmd->object->type != OB_CURVES_LEGACY; } static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData) diff --git a/source/blender/modifiers/intern/MOD_displace.c b/source/blender/modifiers/intern/MOD_displace.c index 0411bcd7c34..ddbed4f498e 100644 --- a/source/blender/modifiers/intern/MOD_displace.c +++ b/source/blender/modifiers/intern/MOD_displace.c @@ -313,8 +313,7 @@ static void displaceModifier_do(DisplaceModifierData *dmd, if (CustomData_has_layer(ldata, CD_CUSTOMLOOPNORMAL)) { float(*clnors)[3] = NULL; - if ((mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL) || - !CustomData_has_layer(ldata, CD_NORMAL)) { + if (!CustomData_has_layer(ldata, CD_NORMAL)) { BKE_mesh_calc_normals_split(mesh); } diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index f54ed8f5a25..210a7edac54 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -454,6 +454,10 @@ static IDProperty *id_property_create_from_socket(const bNodeSocket &socket) ui_data->base.rna_subtype = PROP_COLOR; ui_data->default_array = (double *)MEM_mallocN(sizeof(double[4]), __func__); ui_data->default_array_len = 4; + ui_data->min = 0.0; + ui_data->max = FLT_MAX; + ui_data->soft_min = 0.0; + ui_data->soft_max = 1.0; for (const int i : IndexRange(4)) { ui_data->default_array[i] = double(value->value[i]); } @@ -1381,7 +1385,7 @@ static void add_attribute_search_button(const bContext &C, 0.0f, 0.0f, 0.0f, - ""); + socket.description); const Object *object = ED_object_context(&C); BLI_assert(object != nullptr); diff --git a/source/blender/modifiers/intern/MOD_normal_edit.c b/source/blender/modifiers/intern/MOD_normal_edit.c index e6aebbfeb56..3649808f4f0 100644 --- a/source/blender/modifiers/intern/MOD_normal_edit.c +++ b/source/blender/modifiers/intern/MOD_normal_edit.c @@ -535,8 +535,8 @@ static Mesh *normalEditModifier_do(NormalEditModifierData *enmd, CustomData *ldata = &result->ldata; - const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(mesh); - const float(*poly_normals)[3] = BKE_mesh_poly_normals_ensure(mesh); + const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(result); + const float(*poly_normals)[3] = BKE_mesh_poly_normals_ensure(result); clnors = CustomData_get_layer(ldata, CD_CUSTOMLOOPNORMAL); if (use_current_clnors) { diff --git a/source/blender/modifiers/intern/MOD_solidify_extrude.c b/source/blender/modifiers/intern/MOD_solidify_extrude.c index 0313c37283a..fdaf7bd41d1 100644 --- a/source/blender/modifiers/intern/MOD_solidify_extrude.c +++ b/source/blender/modifiers/intern/MOD_solidify_extrude.c @@ -953,7 +953,7 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex } /* must recalculate normals with vgroups since they can displace unevenly T26888. */ - if ((mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL) || do_rim || dvert) { + if (BKE_mesh_vertex_normals_are_dirty(mesh) || do_rim || dvert) { BKE_mesh_normals_tag_dirty(result); } else if (do_shell) { @@ -1009,9 +1009,9 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex #define SOLIDIFY_SIDE_NORMALS #ifdef SOLIDIFY_SIDE_NORMALS - /* NOTE(@sybren): due to the code setting cd_dirty_vert a few lines above, + /* NOTE(@sybren): due to the code setting normals dirty a few lines above, * do_side_normals is always false. */ - const bool do_side_normals = !(result->runtime.cd_dirty_vert & CD_MASK_NORMAL); + const bool do_side_normals = !BKE_mesh_vertex_normals_are_dirty(result); /* annoying to allocate these since we only need the edge verts, */ float(*edge_vert_nos)[3] = do_side_normals ? MEM_calloc_arrayN(numVerts, sizeof(float[3]), __func__) : diff --git a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c index ed7acef4cdc..ff25c1afd49 100644 --- a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c +++ b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c @@ -1405,21 +1405,26 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, for (uint j = 0; g->valid; j++, g++) { if (!g->is_singularity) { float *nor = g->no; + /* During vertex position calculation, the algorithm decides if it wants to disable the + * boundary fix to maintain correct thickness. If the used algorithm does not produce a + * free move direction (move_nor), it can use approximate_free_direction to decide on + * a movement direction based on the connected edges. */ float move_nor[3] = {0, 0, 0}; bool disable_boundary_fix = (smd->nonmanifold_boundary_mode == MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_NONE || (g->is_orig_closed || g->split)); + bool approximate_free_direction = false; /* Constraints Method. */ if (smd->nonmanifold_offset_mode == MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_CONSTRAINTS) { NewEdgeRef *first_edge = NULL; NewEdgeRef **edge_ptr = g->edges; /* Contains normal and offset [nx, ny, nz, ofs]. */ - float(*normals_queue)[4] = MEM_malloc_arrayN( - g->edges_len + 1, sizeof(*normals_queue), "normals_queue in solidify"); + float(*planes_queue)[4] = MEM_malloc_arrayN( + g->edges_len + 1, sizeof(*planes_queue), "planes_queue in solidify"); uint queue_index = 0; - float face_nors[3][3]; - float nor_ofs[3]; + float fallback_nor[3]; + float fallback_ofs = 0.0f; const bool cycle = (g->is_orig_closed && !g->split) || g->is_even_split; for (uint k = 0; k < g->edges_len; k++, edge_ptr++) { @@ -1436,17 +1441,17 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, } if (!null_faces[face->index]) { - /* And normal to the queue. */ - mul_v3_v3fl(normals_queue[queue_index], + /* And plane to the queue. */ + mul_v3_v3fl(planes_queue[queue_index], poly_nors[face->index], face->reversed ? -1 : 1); - normals_queue[queue_index++][3] = ofs; + planes_queue[queue_index++][3] = ofs; } else { /* Just use this approximate normal of the null face if there is no other * normal to use. */ - mul_v3_v3fl(face_nors[0], poly_nors[face->index], face->reversed ? -1 : 1); - nor_ofs[0] = ofs; + mul_v3_v3fl(fallback_nor, poly_nors[face->index], face->reversed ? -1 : 1); + fallback_ofs = ofs; } } } @@ -1455,131 +1460,173 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, } } } - uint face_nors_len = 0; - const float stop_explosion = 0.999f - fabsf(smd->offset_fac) * 0.05f; - while (queue_index > 0) { - if (face_nors_len == 0) { - if (queue_index <= 2) { - for (uint k = 0; k < queue_index; k++) { - copy_v3_v3(face_nors[k], normals_queue[k]); - nor_ofs[k] = normals_queue[k][3]; + if (queue_index > 2) { + /* Find the two most different normals. */ + float min_p = 2.0f; + uint min_n0 = 0; + uint min_n1 = 0; + for (uint k = 0; k < queue_index; k++) { + for (uint m = k + 1; m < queue_index; m++) { + float p = dot_v3v3(planes_queue[k], planes_queue[m]); + if (p < min_p) { + min_p = p; + min_n0 = k; + min_n1 = m; } - face_nors_len = queue_index; - queue_index = 0; } - else { - /* Find most different two normals. */ - float min_p = 2; - uint min_n0 = 0; - uint min_n1 = 0; - for (uint k = 0; k < queue_index; k++) { - for (uint m = k + 1; m < queue_index; m++) { - float p = dot_v3v3(normals_queue[k], normals_queue[m]); - if (p <= min_p + FLT_EPSILON) { - min_p = p; - min_n0 = m; - min_n1 = k; - } - } - } - copy_v3_v3(face_nors[0], normals_queue[min_n0]); - copy_v3_v3(face_nors[1], normals_queue[min_n1]); - nor_ofs[0] = normals_queue[min_n0][3]; - nor_ofs[1] = normals_queue[min_n1][3]; - face_nors_len = 2; - queue_index--; - memmove(normals_queue + min_n0, - normals_queue + min_n0 + 1, - (queue_index - min_n0) * sizeof(*normals_queue)); - queue_index--; - memmove(normals_queue + min_n1, - normals_queue + min_n1 + 1, - (queue_index - min_n1) * sizeof(*normals_queue)); - min_p = 1; - min_n1 = 0; - float max_p = -1; - for (uint k = 0; k < queue_index; k++) { - max_p = -1; - for (uint m = 0; m < face_nors_len; m++) { - float p = dot_v3v3(face_nors[m], normals_queue[k]); - if (p > max_p + FLT_EPSILON) { - max_p = p; - } - } - if (max_p <= min_p + FLT_EPSILON) { - min_p = max_p; - min_n1 = k; - } - } - if (min_p < 0.8) { - copy_v3_v3(face_nors[2], normals_queue[min_n1]); - nor_ofs[2] = normals_queue[min_n1][3]; - face_nors_len++; - queue_index--; - memmove(normals_queue + min_n1, - normals_queue + min_n1 + 1, - (queue_index - min_n1) * sizeof(*normals_queue)); + } + /* Put the two found normals, first in the array queue. */ + if (min_n1 != 0) { + swap_v4_v4(planes_queue[min_n0], planes_queue[0]); + swap_v4_v4(planes_queue[min_n1], planes_queue[1]); + } + else { + swap_v4_v4(planes_queue[min_n0], planes_queue[1]); + } + /* Find the third most important/different normal. */ + min_p = 1.0f; + min_n1 = 2; + float max_p = -1.0f; + for (uint k = 2; k < queue_index; k++) { + max_p = max_ff(dot_v3v3(planes_queue[0], planes_queue[k]), + dot_v3v3(planes_queue[1], planes_queue[k])); + if (max_p <= min_p) { + min_p = max_p; + min_n1 = k; + } + } + swap_v4_v4(planes_queue[min_n1], planes_queue[2]); + } + /* Remove/average duplicate normals in planes_queue. */ + while (queue_index > 2) { + uint best_n0 = 0; + uint best_n1 = 0; + float best_p = -1.0f; + float best_ofs_diff = 0.0f; + for (uint k = 0; k < queue_index; k++) { + for (uint m = k + 1; m < queue_index; m++) { + float p = dot_v3v3(planes_queue[m], planes_queue[k]); + float ofs_diff = fabsf(planes_queue[m][3] - planes_queue[k][3]); + if (p > best_p + FLT_EPSILON || (p >= best_p && ofs_diff < best_ofs_diff)) { + best_p = p; + best_ofs_diff = ofs_diff; + best_n0 = k; + best_n1 = m; } } } - else { - uint best = 0; - uint best_group = 0; - float best_p = -1.0f; - for (uint k = 0; k < queue_index; k++) { - for (uint m = 0; m < face_nors_len; m++) { - float p = dot_v3v3(face_nors[m], normals_queue[k]); - if (p > best_p + FLT_EPSILON) { - best_p = p; - best = m; - best_group = k; + /* Make sure there are no equal planes. This threshold is crucial for the + * methods below to work without numerical issues. */ + if (best_p < 0.98f) { + break; + } + add_v3_v3(planes_queue[best_n0], planes_queue[best_n1]); + normalize_v3(planes_queue[best_n0]); + planes_queue[best_n0][3] = (planes_queue[best_n0][3] + planes_queue[best_n1][3]) * + 0.5f; + queue_index--; + memmove(planes_queue + best_n1, + planes_queue + best_n1 + 1, + (queue_index - best_n1) * sizeof(*planes_queue)); + } + const uint size = queue_index; + /* If there is more than 2 planes at this vertex, the boundary fix should be disabled + * to stay at the correct thickness for all the faces. This is not very good in + * practice though, since that will almost always disable the boundary fix. Instead + * introduce a threshold which decides whether the boundary fix can be used without + * major thickness changes. If the following constant is 1.0, it would always + * prioritize correct thickness. At 0.7 the thickness is allowed to change a bit if + * necessary for the fix (~10%). Note this only applies if a boundary fix is used. */ + const float boundary_fix_threshold = 0.7f; + if (size > 3) { + /* Use the most general least squares method to find the best position. */ + float mat[3][3]; + zero_m3(mat); + for (int k = 0; k < 3; k++) { + for (int m = 0; m < size; m++) { + madd_v3_v3fl(mat[k], planes_queue[m], planes_queue[m][k]); + } + /* Add a small epsilon to ensure the invert is going to work. + * This addition makes the inverse more stable and the results + * seem to get more precise. */ + mat[k][k] += 5e-5f; + } + /* NOTE: this matrix invert fails if there is less than 3 different normals. */ + invert_m3(mat); + zero_v3(nor); + for (int k = 0; k < size; k++) { + madd_v3_v3fl(nor, planes_queue[k], planes_queue[k][3]); + } + mul_v3_m3v3(nor, mat, nor); + + if (!disable_boundary_fix) { + /* Figure out if the approximate boundary fix can get use here. */ + float greatest_angle_cos = 1.0f; + for (uint k = 0; k < 2; k++) { + for (uint m = 2; m < size; m++) { + float p = dot_v3v3(planes_queue[m], planes_queue[k]); + if (p < greatest_angle_cos) { + greatest_angle_cos = p; } } } - add_v3_v3(face_nors[best], normals_queue[best_group]); - normalize_v3(face_nors[best]); - nor_ofs[best] = (nor_ofs[best] + normals_queue[best_group][3]) * 0.5f; - queue_index--; - memmove(normals_queue + best_group, - normals_queue + best_group + 1, - (queue_index - best_group) * sizeof(*normals_queue)); + if (greatest_angle_cos > boundary_fix_threshold) { + approximate_free_direction = true; + } + else { + disable_boundary_fix = true; + } } } - MEM_freeN(normals_queue); - - /* When up to 3 constraint normals are found. */ - if (ELEM(face_nors_len, 2, 3)) { - const float q = dot_v3v3(face_nors[0], face_nors[1]); + else if (size > 1) { + /* When up to 3 constraint normals are found, there is a simple solution. */ + const float stop_explosion = 0.999f - fabsf(smd->offset_fac) * 0.05f; + const float q = dot_v3v3(planes_queue[0], planes_queue[1]); float d = 1.0f - q * q; - cross_v3_v3v3(move_nor, face_nors[0], face_nors[1]); + cross_v3_v3v3(move_nor, planes_queue[0], planes_queue[1]); + normalize_v3(move_nor); if (d > FLT_EPSILON * 10 && q < stop_explosion) { d = 1.0f / d; - mul_v3_fl(face_nors[0], (nor_ofs[0] - nor_ofs[1] * q) * d); - mul_v3_fl(face_nors[1], (nor_ofs[1] - nor_ofs[0] * q) * d); + mul_v3_fl(planes_queue[0], (planes_queue[0][3] - planes_queue[1][3] * q) * d); + mul_v3_fl(planes_queue[1], (planes_queue[1][3] - planes_queue[0][3] * q) * d); } else { d = 1.0f / (fabsf(q) + 1.0f); - mul_v3_fl(face_nors[0], nor_ofs[0] * d); - mul_v3_fl(face_nors[1], nor_ofs[1] * d); + mul_v3_fl(planes_queue[0], planes_queue[0][3] * d); + mul_v3_fl(planes_queue[1], planes_queue[1][3] * d); } - add_v3_v3v3(nor, face_nors[0], face_nors[1]); - if (face_nors_len == 3) { - float *free_nor = move_nor; - mul_v3_fl(face_nors[2], nor_ofs[2]); - d = dot_v3v3(face_nors[2], free_nor); - if (LIKELY(fabsf(d) > FLT_EPSILON)) { - sub_v3_v3v3(face_nors[0], nor, face_nors[2]); /* Override face_nor[0]. */ - mul_v3_fl(free_nor, dot_v3v3(face_nors[2], face_nors[0]) / d); - sub_v3_v3(nor, free_nor); + add_v3_v3v3(nor, planes_queue[0], planes_queue[1]); + if (size == 3) { + d = dot_v3v3(planes_queue[2], move_nor); + /* The following threshold ignores the third plane if it is almost orthogonal to + * the still free direction. */ + if (fabsf(d) > 0.02f) { + float tmp[3]; + madd_v3_v3v3fl(tmp, nor, planes_queue[2], -planes_queue[2][3]); + mul_v3_v3fl(tmp, move_nor, dot_v3v3(planes_queue[2], tmp) / d); + sub_v3_v3(nor, tmp); + /* Disable boundary fix if the constraints would be majorly unsatisfied. */ + if (fabsf(d) > 1.0f - boundary_fix_threshold) { + disable_boundary_fix = true; + } } + } + approximate_free_direction = false; + } + else if (size == 1) { + /* Face corner case. */ + mul_v3_v3fl(nor, planes_queue[0], planes_queue[0][3]); + if (g->edges_len > 2) { disable_boundary_fix = true; + approximate_free_direction = true; } } else { - BLI_assert(face_nors_len < 2); - mul_v3_v3fl(nor, face_nors[0], nor_ofs[0]); + /* Fallback case for null faces. */ + mul_v3_v3fl(nor, fallback_nor, fallback_ofs); disable_boundary_fix = true; } + MEM_freeN(planes_queue); } /* Fixed/Even Method. */ else { @@ -1707,26 +1754,29 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, } /* Set move_nor for boundary fix. */ if (!disable_boundary_fix && g->edges_len > 2) { - edge_ptr = g->edges + 1; - float tmp[3]; - uint k; - for (k = 1; k + 1 < g->edges_len; k++, edge_ptr++) { - MEdge *e = orig_medge + (*edge_ptr)->old_edge; - sub_v3_v3v3( - tmp, orig_mvert_co[vm[e->v1] == i ? e->v2 : e->v1], orig_mvert_co[i]); - add_v3_v3(move_nor, tmp); - } - if (k == 1) { - disable_boundary_fix = true; - } - else { - disable_boundary_fix = normalize_v3(move_nor) == 0.0f; - } + approximate_free_direction = true; } else { disable_boundary_fix = true; } } + if (approximate_free_direction) { + /* Set move_nor for boundary fix. */ + NewEdgeRef **edge_ptr = g->edges + 1; + float tmp[3]; + int k; + for (k = 1; k + 1 < g->edges_len; k++, edge_ptr++) { + MEdge *e = orig_medge + (*edge_ptr)->old_edge; + sub_v3_v3v3(tmp, orig_mvert_co[vm[e->v1] == i ? e->v2 : e->v1], orig_mvert_co[i]); + add_v3_v3(move_nor, tmp); + } + if (k == 1) { + disable_boundary_fix = true; + } + else { + disable_boundary_fix = normalize_v3(move_nor) == 0.0f; + } + } /* Fix boundary verts. */ if (!disable_boundary_fix) { /* Constraint normal, nor * constr_nor == 0 after this fix. */ @@ -1743,8 +1793,11 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, orig_mvert_co[i]); if (smd->nonmanifold_boundary_mode == MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_FLAT) { cross_v3_v3v3(constr_nor, e0, e1); + normalize_v3(constr_nor); } else { + BLI_assert(smd->nonmanifold_boundary_mode == + MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_ROUND); float f0[3]; float f1[3]; if (g->edges[0]->faces[0]->reversed) { @@ -1766,9 +1819,11 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, normalize_v3(n0); normalize_v3(n1); add_v3_v3v3(constr_nor, n0, n1); + normalize_v3(constr_nor); } float d = dot_v3v3(constr_nor, move_nor); - if (LIKELY(fabsf(d) > FLT_EPSILON)) { + /* Only allow the thickness to increase about 10 times. */ + if (fabsf(d) > 0.1f) { mul_v3_fl(move_nor, dot_v3v3(constr_nor, nor) / d); sub_v3_v3(nor, move_nor); } diff --git a/source/blender/modifiers/intern/MOD_subsurf.c b/source/blender/modifiers/intern/MOD_subsurf.c index cad36959f2b..b791e28d15b 100644 --- a/source/blender/modifiers/intern/MOD_subsurf.c +++ b/source/blender/modifiers/intern/MOD_subsurf.c @@ -234,7 +234,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * * assigned at this stage of modifier stack evaluation. */ const bool is_editmode = (mesh->edit_mesh != NULL); const int required_mode = BKE_subsurf_modifier_eval_required_mode(is_render_mode, is_editmode); - if (BKE_subsurf_modifier_can_do_gpu_subdiv_ex(scene, ctx->object, smd, required_mode, false)) { + if (BKE_subsurf_modifier_can_do_gpu_subdiv_ex( + scene, ctx->object, mesh, smd, required_mode, false)) { subdiv_cache_cpu_evaluation_settings(ctx, mesh, smd); return result; } @@ -246,9 +247,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * /* Happens on bad topology, but also on empty input mesh. */ return result; } - const bool use_clnors = (smd->flags & eSubsurfModifierFlag_UseCustomNormals) && - (mesh->flag & ME_AUTOSMOOTH) && - CustomData_has_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL); + const bool use_clnors = BKE_subsurf_modifier_use_custom_loop_normals(smd, mesh); if (use_clnors) { /* If custom normals are present and the option is turned on calculate the split * normals and clear flag so the normals get interpolated to the result mesh. */ @@ -412,6 +411,13 @@ static void panel_draw(const bContext *C, Panel *panel) uiItemR(layout, ptr, "show_only_control_edges", 0, NULL, ICON_NONE); + SubsurfModifierData *smd = ptr->data; + Object *ob = ob_ptr.data; + Mesh *mesh = ob->data; + if (BKE_subsurf_modifier_force_disable_gpu_evaluation_for_mesh(smd, mesh)) { + uiItemL(layout, "Autosmooth or custom normals detected, disabling GPU subdivision", ICON_INFO); + } + modifier_panel_end(layout, ptr); } diff --git a/source/blender/modifiers/intern/MOD_ui_common.c b/source/blender/modifiers/intern/MOD_ui_common.c index 94482742831..90652c180c6 100644 --- a/source/blender/modifiers/intern/MOD_ui_common.c +++ b/source/blender/modifiers/intern/MOD_ui_common.c @@ -324,7 +324,7 @@ static void modifier_panel_header(const bContext *C, Panel *panel) buttons_number++; } } /* Tessellation point for curve-typed objects. */ - else if (ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)) { + else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF, OB_FONT)) { /* Some modifiers can work with pre-tessellated curves only. */ if (ELEM(md->type, eModifierType_Hook, eModifierType_Softbody, eModifierType_MeshDeform)) { /* Add button (appearing to be ON) and add tip why this can't be changed. */ diff --git a/source/blender/modifiers/intern/MOD_util.c b/source/blender/modifiers/intern/MOD_util.c index 41b600e386b..a8c52108cc0 100644 --- a/source/blender/modifiers/intern/MOD_util.c +++ b/source/blender/modifiers/intern/MOD_util.c @@ -204,7 +204,7 @@ Mesh *MOD_deform_mesh_eval_get(Object *ob, BKE_mesh_orco_ensure(ob, mesh); } } - else if (ELEM(ob->type, OB_FONT, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_FONT, OB_CURVES_LEGACY, OB_SURF)) { /* TODO(sybren): get evaluated mesh from depsgraph once * that's properly generated for curves. */ mesh = BKE_mesh_new_nomain_from_curve(ob); diff --git a/source/blender/modifiers/intern/MOD_uvproject.c b/source/blender/modifiers/intern/MOD_uvproject.c index 51ec0a46d32..d6f493267f8 100644 --- a/source/blender/modifiers/intern/MOD_uvproject.c +++ b/source/blender/modifiers/intern/MOD_uvproject.c @@ -285,9 +285,6 @@ static Mesh *uvprojectModifier_do(UVProjectModifierData *umd, mesh->runtime.is_original = false; - /* Mark tessellated CD layers as dirty. */ - mesh->runtime.cd_dirty_vert |= CD_MASK_TESSLOOPNORMAL; - return mesh; } diff --git a/source/blender/modifiers/intern/MOD_volume_displace.cc b/source/blender/modifiers/intern/MOD_volume_displace.cc index 3e71d1fb106..5ce76046294 100644 --- a/source/blender/modifiers/intern/MOD_volume_displace.cc +++ b/source/blender/modifiers/intern/MOD_volume_displace.cc @@ -189,10 +189,8 @@ struct DisplaceGridOp { template<typename GridType> void operator()() { - if constexpr (blender::is_same_any_v<GridType, - openvdb::points::PointDataGrid, - openvdb::StringGrid, - openvdb::MaskGrid>) { + if constexpr (blender:: + is_same_any_v<GridType, openvdb::points::PointDataGrid, openvdb::MaskGrid>) { /* We don't support displacing these grid types yet. */ return; } diff --git a/source/blender/modifiers/intern/MOD_weightvgmix.c b/source/blender/modifiers/intern/MOD_weightvgmix.c index 1078ebfaeb2..1b6472e2d42 100644 --- a/source/blender/modifiers/intern/MOD_weightvgmix.c +++ b/source/blender/modifiers/intern/MOD_weightvgmix.c @@ -105,6 +105,12 @@ static float mix_weight(float weight, float weight2, char mix_mode) if (mix_mode == MOD_WVG_MIX_AVG) { return (weight + weight2) * 0.5f; } + if (mix_mode == MOD_WVG_MIX_MIN) { + return (weight < weight2 ? weight : weight2); + } + if (mix_mode == MOD_WVG_MIX_MAX) { + return (weight > weight2 ? weight : weight2); + } return weight2; } diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 86dde999ffc..cfcc30655b5 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -82,6 +82,7 @@ void register_node_type_geo_curve_to_mesh(void); void register_node_type_geo_curve_to_points(void); void register_node_type_geo_curve_trim(void); void register_node_type_geo_delete_geometry(void); +void register_node_type_geo_duplicate_elements(void); void register_node_type_geo_distribute_points_on_faces(void); void register_node_type_geo_dual_mesh(void); void register_node_type_geo_edge_split(void); @@ -100,6 +101,7 @@ void register_node_type_geo_input_mesh_edge_angle(void); void register_node_type_geo_input_mesh_edge_neighbors(void); void register_node_type_geo_input_mesh_edge_vertices(void); void register_node_type_geo_input_mesh_face_area(void); +void register_node_type_geo_input_mesh_face_is_planar(void); void register_node_type_geo_input_mesh_face_neighbors(void); void register_node_type_geo_input_mesh_island(void); void register_node_type_geo_input_mesh_vertex_neighbors(void); diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh index 3bcd4b87b15..4e78f6c1142 100644 --- a/source/blender/nodes/NOD_node_declaration.hh +++ b/source/blender/nodes/NOD_node_declaration.hh @@ -110,7 +110,7 @@ class SocketDeclaration { /** * Change the node such that the socket will become visible. The node type's update method * should be called afterwards. - * \note Note that this is not necessarily implemented for all node types. + * \note this is not necessarily implemented for all node types. */ void make_available(bNode &node) const; diff --git a/source/blender/nodes/NOD_socket_search_link.hh b/source/blender/nodes/NOD_socket_search_link.hh index e3927488612..7a1aff13020 100644 --- a/source/blender/nodes/NOD_socket_search_link.hh +++ b/source/blender/nodes/NOD_socket_search_link.hh @@ -16,7 +16,7 @@ struct bContext; namespace blender::nodes { /** - * Parameters for the operation operation of adding a node after the link drag search menu closes. + * Parameters for the operation of adding a node after the link drag search menu closes. */ class LinkSearchOpParams { private: diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 1f1d7e4dc3a..efd3c420330 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -338,6 +338,7 @@ DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "CU 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", "") DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, def_geo_delete_geometry, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "") +DefNode(GeometryNode, GEO_NODE_DUPLICATE_ELEMENTS, def_geo_duplicate_elements, "DUPLICATE_ELEMENTS", DuplicateElements, "Duplicate Elements", "") DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, def_geo_distribute_points_on_faces, "DISTRIBUTE_POINTS_ON_FACES", DistributePointsOnFaces, "Distribute Points on Faces", "") DefNode(GeometryNode, GEO_NODE_ACCUMULATE_FIELD, def_geo_accumulate_field, "ACCUMULATE_FIELD", AccumulateField, "Accumulate Field", "") DefNode(GeometryNode, GEO_NODE_DUAL_MESH, 0, "DUAL_MESH", DualMesh, "Dual Mesh", "") @@ -358,6 +359,7 @@ DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_ANGLE, 0, "MESH_EDGE_ANGLE", Inpu DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_NEIGHBORS, 0, "MESH_EDGE_NEIGHBORS", InputMeshEdgeNeighbors, "Edge Neighbors", "") DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_VERTICES, 0, "MESH_EDGE_VERTICES", InputMeshEdgeVertices, "Edge Vertices", "") DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_AREA, 0, "MESH_FACE_AREA", InputMeshFaceArea, "Face Area", "") +DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_IS_PLANAR, 0, "MESH_FACE_IS_PLANAR", InputMeshFaceIsPlanar, "Face is Planar", "") DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_NEIGHBORS, 0, "MESH_FACE_NEIGHBORS", InputMeshFaceNeighbors, "Face Neighbors", "") DefNode(GeometryNode, GEO_NODE_INPUT_MESH_ISLAND, 0, "MESH_ISLAND", InputMeshIsland, "Mesh Island", "") DefNode(GeometryNode, GEO_NODE_INPUT_MESH_VERTEX_NEIGHBORS, 0, "MESH_VERTEX_NEIGHBORS", InputMeshVertexNeighbors, "Vertex Neighbors", "") diff --git a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc index 15e7163d7db..162ef07a6dd 100644 --- a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc @@ -10,6 +10,7 @@ #include "BLI_assert.h" #include "BLI_dynstr.h" #include "BLI_hash_mm3.h" +#include "BLI_math_vector.h" #include "BLI_string_ref.hh" #include "BLI_utildefines.h" diff --git a/source/blender/nodes/function/nodes/node_fn_compare.cc b/source/blender/nodes/function/nodes/node_fn_compare.cc index 2e2e5227a65..e6fdf1820fa 100644 --- a/source/blender/nodes/function/nodes/node_fn_compare.cc +++ b/source/blender/nodes/function/nodes/node_fn_compare.cc @@ -3,6 +3,7 @@ #include <cmath> #include "BLI_listbase.h" +#include "BLI_math_vector.h" #include "BLI_string.h" #include "UI_interface.h" diff --git a/source/blender/nodes/function/nodes/node_fn_input_color.cc b/source/blender/nodes/function/nodes/node_fn_input_color.cc index f76bb904153..46787f7575d 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_color.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_color.cc @@ -2,6 +2,8 @@ #include "node_function_util.hh" +#include "BLI_math_vector.h" + #include "UI_interface.h" #include "UI_resources.h" diff --git a/source/blender/nodes/geometry/CMakeLists.txt b/source/blender/nodes/geometry/CMakeLists.txt index f38562a8926..48a83dc825b 100644 --- a/source/blender/nodes/geometry/CMakeLists.txt +++ b/source/blender/nodes/geometry/CMakeLists.txt @@ -98,6 +98,7 @@ set(SRC nodes/node_geo_curve_to_points.cc nodes/node_geo_curve_trim.cc nodes/node_geo_delete_geometry.cc + nodes/node_geo_duplicate_elements.cc nodes/node_geo_distribute_points_on_faces.cc nodes/node_geo_dual_mesh.cc nodes/node_geo_edge_split.cc @@ -116,6 +117,7 @@ set(SRC nodes/node_geo_input_mesh_edge_neighbors.cc nodes/node_geo_input_mesh_edge_vertices.cc nodes/node_geo_input_mesh_face_area.cc + nodes/node_geo_input_mesh_face_is_planar.cc nodes/node_geo_input_mesh_face_neighbors.cc nodes/node_geo_input_mesh_island.cc nodes/node_geo_input_mesh_vertex_neighbors.cc diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_endpoints.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_endpoints.cc index b83aa8b69a9..0980c2d6e72 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_endpoints.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_endpoints.cc @@ -137,15 +137,15 @@ static void node_geo_exec(GeoNodeExecParams params) geometry_set = geometry::realize_instances_legacy(geometry_set); - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { params.set_default_remaining_outputs(); return; } const CurveComponent &curve_component = *geometry_set.get_component_for_read<CurveComponent>(); - const CurveEval &curve = *curve_component.get_for_read(); - const Span<SplinePtr> splines = curve.splines(); - curve.assert_valid_point_attributes(); + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read()); + const Span<SplinePtr> splines = curve->splines(); + curve->assert_valid_point_attributes(); evaluate_splines(splines); @@ -167,9 +167,9 @@ static void node_geo_exec(GeoNodeExecParams params) end_result.get_component_for_write<PointCloudComponent>(); CurveToPointsResults start_attributes = curve_to_points_create_result_attributes( - start_point_component, curve); + start_point_component, *curve); CurveToPointsResults end_attributes = curve_to_points_create_result_attributes( - end_point_component, curve); + end_point_component, *curve); copy_endpoint_attributes(splines, offsets.as_span(), start_attributes, end_attributes); copy_spline_domain_attributes(curve_component, offsets.as_span(), start_point_component); diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc index 6deaf5b554a..2fe06a17adf 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc @@ -19,15 +19,15 @@ static void node_geo_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); geometry_set = geometry::realize_instances_legacy(geometry_set); - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { params.set_output("Curve", geometry_set); return; } /* Retrieve data for write access so we can avoid new allocations for the reversed data. */ CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>(); - CurveEval &curve = *curve_component.get_for_write(); - MutableSpan<SplinePtr> splines = curve.splines(); + std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read()); + MutableSpan<SplinePtr> splines = curve->splines(); const std::string selection_name = params.extract_input<std::string>("Selection"); VArray<bool> selection = curve_component.attribute_get_for_read( @@ -41,6 +41,8 @@ static void node_geo_exec(GeoNodeExecParams params) } }); + geometry_set.replace_curve(curve_eval_to_curves(*curve)); + params.set_output("Curve", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc index f0a201c5adf..729ccca5f04 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_select_by_handle_type.cc @@ -33,24 +33,24 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) node->storage = data; } -static BezierSpline::HandleType handle_type_from_input_type(const GeometryNodeCurveHandleType type) +static HandleType handle_type_from_input_type(const GeometryNodeCurveHandleType type) { switch (type) { case GEO_NODE_CURVE_HANDLE_AUTO: - return BezierSpline::HandleType::Auto; + return BEZIER_HANDLE_AUTO; case GEO_NODE_CURVE_HANDLE_ALIGN: - return BezierSpline::HandleType::Align; + return BEZIER_HANDLE_ALIGN; case GEO_NODE_CURVE_HANDLE_FREE: - return BezierSpline::HandleType::Free; + return BEZIER_HANDLE_FREE; case GEO_NODE_CURVE_HANDLE_VECTOR: - return BezierSpline::HandleType::Vector; + return BEZIER_HANDLE_VECTOR; } BLI_assert_unreachable(); - return BezierSpline::HandleType::Auto; + return BEZIER_HANDLE_AUTO; } static void select_curve_by_handle_type(const CurveEval &curve, - const BezierSpline::HandleType type, + const HandleType type, const GeometryNodeCurveHandleMode mode, const MutableSpan<bool> r_selection) { @@ -59,10 +59,10 @@ static void select_curve_by_handle_type(const CurveEval &curve, 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) { + if (spline.type() == CURVE_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(); + Span<int8_t> types_left = bezier_spline.handle_types_left(); + Span<int8_t> 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) || @@ -81,7 +81,7 @@ static void node_geo_exec(GeoNodeExecParams params) { const NodeGeometryCurveSelectHandles *storage = (const NodeGeometryCurveSelectHandles *)params.node().storage; - const BezierSpline::HandleType handle_type = handle_type_from_input_type( + const HandleType handle_type = handle_type_from_input_type( (GeometryNodeCurveHandleType)storage->handle_type); const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)storage->mode; @@ -89,9 +89,8 @@ static void node_geo_exec(GeoNodeExecParams params) geometry_set = geometry::realize_instances_legacy(geometry_set); CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>(); - const CurveEval *curve = curve_component.get_for_read(); - - if (curve != nullptr) { + if (curve_component.has_curves()) { + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read()); 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); diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc index b574b2e3cff..537c7c42610 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc @@ -31,20 +31,20 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) node->storage = data; } -static BezierSpline::HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type) +static HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type) { switch (type) { case GEO_NODE_CURVE_HANDLE_AUTO: - return BezierSpline::HandleType::Auto; + return BEZIER_HANDLE_AUTO; case GEO_NODE_CURVE_HANDLE_ALIGN: - return BezierSpline::HandleType::Align; + return BEZIER_HANDLE_ALIGN; case GEO_NODE_CURVE_HANDLE_FREE: - return BezierSpline::HandleType::Free; + return BEZIER_HANDLE_FREE; case GEO_NODE_CURVE_HANDLE_VECTOR: - return BezierSpline::HandleType::Vector; + return BEZIER_HANDLE_VECTOR; } BLI_assert_unreachable(); - return BezierSpline::HandleType::Auto; + return BEZIER_HANDLE_AUTO; } static void node_geo_exec(GeoNodeExecParams params) @@ -56,31 +56,31 @@ static void node_geo_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); geometry_set = geometry::realize_instances_legacy(geometry_set); - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { params.set_output("Curve", geometry_set); return; } /* Retrieve data for write access so we can avoid new allocations for the handles data. */ CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>(); - CurveEval &curve = *curve_component.get_for_write(); - MutableSpan<SplinePtr> splines = curve.splines(); + std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read()); + MutableSpan<SplinePtr> splines = curve->splines(); const std::string selection_name = params.extract_input<std::string>("Selection"); VArray<bool> selection = curve_component.attribute_get_for_read( selection_name, ATTR_DOMAIN_POINT, true); - const BezierSpline::HandleType new_handle_type = handle_type_from_input_type(type); + const HandleType new_handle_type = handle_type_from_input_type(type); int point_index = 0; bool has_bezier_spline = false; for (SplinePtr &spline : splines) { - if (spline->type() != Spline::Type::Bezier) { + if (spline->type() != CURVE_TYPE_BEZIER) { point_index += spline->positions().size(); continue; } BezierSpline &bezier_spline = static_cast<BezierSpline &>(*spline); - if (ELEM(new_handle_type, BezierSpline::HandleType::Free, BezierSpline::HandleType::Align)) { + if (ELEM(new_handle_type, BEZIER_HANDLE_FREE, BEZIER_HANDLE_ALIGN)) { /* In this case the automatically calculated handle types need to be "baked", because * they're possibly changing from a type that is calculated automatically to a type that * is positioned manually. */ @@ -101,6 +101,8 @@ static void node_geo_exec(GeoNodeExecParams params) bezier_spline.mark_cache_invalid(); } + geometry_set.replace_curve(curve_eval_to_curves(*curve)); + if (!has_bezier_spline) { params.error_message_add(NodeWarningType::Info, TIP_("No Bezier splines in input curve")); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc index b8696e1eb52..4e3b0839da7 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc @@ -148,8 +148,8 @@ static SplinePtr poly_to_bezier(const Spline &input) 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->handle_types_left().fill(BEZIER_HANDLE_VECTOR); + output->handle_types_right().fill(BEZIER_HANDLE_VECTOR); output->set_resolution(12); Spline::copy_base_settings(input, *output); output->attributes = input.attributes; @@ -166,8 +166,8 @@ static SplinePtr nurbs_to_bezier(const Spline &input) 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->handle_types_left().fill(BEZIER_HANDLE_ALIGN); + output->handle_types_right().fill(BEZIER_HANDLE_ALIGN); output->set_resolution(nurbs_spline.resolution()); Spline::copy_base_settings(input, *output); output->attributes.reallocate(output->size()); @@ -183,11 +183,11 @@ static SplinePtr nurbs_to_bezier(const Spline &input) static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params) { switch (input.type()) { - case Spline::Type::Bezier: + case CURVE_TYPE_BEZIER: return input.copy(); - case Spline::Type::Poly: + case CURVE_TYPE_POLY: return poly_to_bezier(input); - case Spline::Type::NURBS: + case CURVE_TYPE_NURBS: if (input.size() < 6) { params.error_message_add( NodeWarningType::Info, @@ -202,6 +202,10 @@ static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params } return nurbs_to_bezier(input); } + case CURVE_TYPE_CATMULL_ROM: { + BLI_assert_unreachable(); + return {}; + } } BLI_assert_unreachable(); return {}; @@ -210,12 +214,15 @@ static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params static SplinePtr convert_to_nurbs(const Spline &input) { switch (input.type()) { - case Spline::Type::NURBS: + case CURVE_TYPE_NURBS: return input.copy(); - case Spline::Type::Bezier: + case CURVE_TYPE_BEZIER: return bezier_to_nurbs(input); - case Spline::Type::Poly: + case CURVE_TYPE_POLY: return poly_to_nurbs(input); + case CURVE_TYPE_CATMULL_ROM: + BLI_assert_unreachable(); + return {}; } BLI_assert_unreachable(); return {}; @@ -229,40 +236,40 @@ static void node_geo_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); geometry_set = geometry::realize_instances_legacy(geometry_set); - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { 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::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component->get_for_read()); const std::string selection_name = params.extract_input<std::string>("Selection"); VArray<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()) { + 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])); + 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)); + 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])); + new_curve->add_spline(convert_to_nurbs(*curve->splines()[i])); break; } } else { - new_curve->add_spline(curve.splines()[i]->copy()); + new_curve->add_spline(curve->splines()[i]->copy()); } } - new_curve->attributes = curve.attributes; - params.set_output("Curve", GeometrySet::create_with_curve(new_curve.release())); + new_curve->attributes = curve->attributes; + params.set_output("Curve", GeometrySet::create_with_curves(curve_eval_to_curves(*new_curve))); } } // namespace blender::nodes::node_geo_legacy_curve_spline_type_cc diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_subdivide.cc index 8ae9df78936..03f7aec8838 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_subdivide.cc @@ -111,8 +111,8 @@ static void subdivide_bezier_segment(const BezierSpline &src, MutableSpan<float3> dst_positions, MutableSpan<float3> dst_handles_left, MutableSpan<float3> dst_handles_right, - MutableSpan<BezierSpline::HandleType> dst_type_left, - MutableSpan<BezierSpline::HandleType> dst_type_right) + MutableSpan<int8_t> dst_type_left, + MutableSpan<int8_t> dst_type_right) { const bool is_last_cyclic_segment = index == (src.size() - 1); const int next_index = is_last_cyclic_segment ? 0 : index + 1; @@ -122,10 +122,10 @@ static void subdivide_bezier_segment(const BezierSpline &src, if (src.segment_is_vector(index)) { if (is_last_cyclic_segment) { - dst_type_left.first() = BezierSpline::HandleType::Vector; + dst_type_left.first() = BEZIER_HANDLE_VECTOR; } - dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Vector); - dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Vector); + dst_type_left.slice(offset + 1, result_size).fill(BEZIER_HANDLE_VECTOR); + dst_type_right.slice(offset, result_size).fill(BEZIER_HANDLE_VECTOR); const float factor_delta = 1.0f / result_size; for (const int cut : IndexRange(result_size)) { @@ -136,10 +136,10 @@ static void subdivide_bezier_segment(const BezierSpline &src, } else { if (is_last_cyclic_segment) { - dst_type_left.first() = BezierSpline::HandleType::Free; + dst_type_left.first() = BEZIER_HANDLE_FREE; } - dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Free); - dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Free); + dst_type_left.slice(offset + 1, result_size).fill(BEZIER_HANDLE_FREE); + dst_type_right.slice(offset, result_size).fill(BEZIER_HANDLE_FREE); const int i_segment_last = is_last_cyclic_segment ? 0 : offset + result_size; @@ -187,8 +187,8 @@ static void subdivide_bezier_spline(const BezierSpline &src, MutableSpan<float3> dst_positions = dst.positions(); MutableSpan<float3> dst_handles_left = dst.handle_positions_left(); MutableSpan<float3> dst_handles_right = dst.handle_positions_right(); - MutableSpan<BezierSpline::HandleType> dst_type_left = dst.handle_types_left(); - MutableSpan<BezierSpline::HandleType> dst_type_right = dst.handle_types_right(); + MutableSpan<int8_t> dst_type_left = dst.handle_types_left(); + MutableSpan<int8_t> dst_type_right = dst.handle_types_right(); threading::parallel_for(IndexRange(src.size() - 1), 512, [&](IndexRange range) { for (const int i : range) { @@ -235,26 +235,30 @@ static void subdivide_builtin_attributes(const Spline &src_spline, subdivide_attribute<float>(src_spline.radii(), offsets, is_cyclic, dst_spline.radii()); subdivide_attribute<float>(src_spline.tilts(), offsets, is_cyclic, dst_spline.tilts()); switch (src_spline.type()) { - case Spline::Type::Poly: { + case CURVE_TYPE_POLY: { const PolySpline &src = static_cast<const PolySpline &>(src_spline); PolySpline &dst = static_cast<PolySpline &>(dst_spline); subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions()); break; } - case Spline::Type::Bezier: { + case CURVE_TYPE_BEZIER: { const BezierSpline &src = static_cast<const BezierSpline &>(src_spline); BezierSpline &dst = static_cast<BezierSpline &>(dst_spline); subdivide_bezier_spline(src, offsets, dst); dst.mark_cache_invalid(); break; } - case Spline::Type::NURBS: { + case CURVE_TYPE_NURBS: { const NURBSpline &src = static_cast<const NURBSpline &>(src_spline); NURBSpline &dst = static_cast<NURBSpline &>(dst_spline); subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions()); subdivide_attribute<float>(src.weights(), offsets, is_cyclic, dst.weights()); break; } + case CURVE_TYPE_CATMULL_ROM: { + BLI_assert_unreachable(); + break; + } } } @@ -338,7 +342,7 @@ static void node_geo_exec(GeoNodeExecParams params) geometry_set = geometry::realize_instances_legacy(geometry_set); - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { params.set_output("Geometry", geometry_set); return; } @@ -350,9 +354,11 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - std::unique_ptr<CurveEval> output_curve = subdivide_curve(*component.get_for_read(), cuts); + std::unique_ptr<CurveEval> output_curve = subdivide_curve( + *curves_to_curve_eval(*component.get_for_read()), cuts); - params.set_output("Geometry", GeometrySet::create_with_curve(output_curve.release())); + params.set_output("Geometry", + GeometrySet::create_with_curves(curve_eval_to_curves(*output_curve))); } } // namespace blender::nodes::node_geo_legacy_curve_subdivide_cc diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_to_points.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_to_points.cc index 64627e61910..f8fcc3cc363 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_to_points.cc @@ -283,19 +283,19 @@ static void node_geo_exec(GeoNodeExecParams params) geometry_set = geometry::realize_instances_legacy(geometry_set); - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { params.set_output("Geometry", GeometrySet()); return; } const CurveComponent &curve_component = *geometry_set.get_component_for_read<CurveComponent>(); - const CurveEval &curve = *curve_component.get_for_read(); - const Span<SplinePtr> splines = curve.splines(); - curve.assert_valid_point_attributes(); + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read()); + const Span<SplinePtr> splines = curve->splines(); + curve->assert_valid_point_attributes(); evaluate_splines(splines); - const Array<int> offsets = calculate_spline_point_offsets(params, mode, curve, splines); + const Array<int> offsets = calculate_spline_point_offsets(params, mode, *curve, splines); const int total_size = offsets.last(); if (total_size == 0) { params.set_output("Geometry", GeometrySet()); @@ -306,7 +306,7 @@ static void node_geo_exec(GeoNodeExecParams params) PointCloudComponent &point_component = result.get_component_for_write<PointCloudComponent>(); CurveToPointsResults new_attributes = curve_to_points_create_result_attributes(point_component, - curve); + *curve); switch (mode) { case GEO_NODE_CURVE_RESAMPLE_COUNT: case GEO_NODE_CURVE_RESAMPLE_LENGTH: diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_delete_geometry.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_delete_geometry.cc index a0b862546bc..ca98d83c137 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_delete_geometry.cc @@ -111,9 +111,9 @@ static void spline_copy_builtin_attributes(const Spline &spline, copy_data(spline.radii(), r_spline.radii(), mask); copy_data(spline.tilts(), r_spline.tilts(), mask); switch (spline.type()) { - case Spline::Type::Poly: + case CURVE_TYPE_POLY: break; - case Spline::Type::Bezier: { + case CURVE_TYPE_BEZIER: { const BezierSpline &src = static_cast<const BezierSpline &>(spline); BezierSpline &dst = static_cast<BezierSpline &>(r_spline); copy_data(src.handle_positions_left(), dst.handle_positions_left(), mask); @@ -122,12 +122,16 @@ static void spline_copy_builtin_attributes(const Spline &spline, copy_data(src.handle_types_right(), dst.handle_types_right(), mask); break; } - case Spline::Type::NURBS: { + case CURVE_TYPE_NURBS: { const NURBSpline &src = static_cast<const NURBSpline &>(spline); NURBSpline &dst = static_cast<NURBSpline &>(r_spline); copy_data(src.weights(), dst.weights(), mask); break; } + case CURVE_TYPE_CATMULL_ROM: { + BLI_assert_unreachable(); + break; + } } } @@ -231,9 +235,9 @@ static void delete_curve_selection(const CurveComponent &in_component, const bool invert) { std::unique_ptr<CurveEval> r_curve = curve_delete( - *in_component.get_for_read(), selection_name, invert); + *curves_to_curve_eval(*in_component.get_for_read()), selection_name, invert); if (r_curve) { - r_component.replace(r_curve.release()); + r_component.replace(curve_eval_to_curves(*r_curve)); } else { r_component.clear(); diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc index ff86a92f2c7..8991261a21a 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc @@ -48,7 +48,7 @@ static void node_geo_exec(GeoNodeExecParams params) std::unique_ptr<CurveEval> curve = geometry::mesh_to_curve_convert( component, IndexMask(selected_edge_indices)); - params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); + params.set_output("Curve", GeometrySet::create_with_curves(curve_eval_to_curves(*curve))); } } // namespace blender::nodes::node_geo_legacy_mesh_to_curve_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc index b6d677154d0..b3fe9d160b3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc @@ -84,7 +84,7 @@ static void node_geo_exec(GeoNodeExecParams params) break; } case GEO_COMPONENT_TYPE_CURVE: { - if (geometry_set.has_curve()) { + if (geometry_set.has_curves()) { const CurveComponent *component = geometry_set.get_component_for_read<CurveComponent>(); params.set_output("Point Count", component->attribute_domain_size(ATTR_DOMAIN_POINT)); params.set_output("Spline Count", component->attribute_domain_size(ATTR_DOMAIN_CURVE)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc index 6424fccbe04..cb7132d5ea2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc @@ -33,25 +33,18 @@ static void node_geo_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); Vector<std::string> attribute_names = params.extract_multi_input<std::string>("Attribute"); - if (geometry_set.has<MeshComponent>()) { - remove_attribute( - geometry_set.get_component_for_write<MeshComponent>(), params, attribute_names); - } - if (geometry_set.has<PointCloudComponent>()) { - remove_attribute( - geometry_set.get_component_for_write<PointCloudComponent>(), params, attribute_names); - } - if (geometry_set.has<CurveComponent>()) { - remove_attribute( - geometry_set.get_component_for_write<CurveComponent>(), params, attribute_names); - } - if (geometry_set.has<InstancesComponent>()) { - remove_attribute( - geometry_set.get_component_for_write<InstancesComponent>(), params, attribute_names); + for (const GeometryComponentType type : {GEO_COMPONENT_TYPE_MESH, + GEO_COMPONENT_TYPE_POINT_CLOUD, + GEO_COMPONENT_TYPE_CURVE, + GEO_COMPONENT_TYPE_INSTANCES}) { + if (geometry_set.has(type)) { + remove_attribute(geometry_set.get_component_for_write(type), params, attribute_names); + } } params.set_output("Geometry", geometry_set); } + } // namespace blender::nodes::node_geo_attribute_remove_cc void register_node_type_geo_attribute_remove() 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 59147e9b23f..412f35d62fd 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc @@ -161,9 +161,10 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) positions_span = varray.get_internal_span(); } - if (geometry_set.has_curve()) { - const CurveEval &curve = *geometry_set.get_curve_for_read(); - for (const SplinePtr &spline : curve.splines()) { + if (geometry_set.has_curves()) { + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *geometry_set.get_curves_for_read()); + for (const SplinePtr &spline : curve->splines()) { positions_span = spline->evaluated_positions(); total_size += positions_span.size(); count++; @@ -201,9 +202,10 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) offset += varray.size(); } - if (geometry_set.has_curve()) { - const CurveEval &curve = *geometry_set.get_curve_for_read(); - for (const SplinePtr &spline : curve.splines()) { + if (geometry_set.has_curves()) { + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *geometry_set.get_curves_for_read()); + for (const SplinePtr &spline : curve->splines()) { Span<float3> array = spline->evaluated_positions(); positions.as_mutable_span().slice(offset, array.size()).copy_from(array); offset += array.size(); @@ -272,8 +274,8 @@ static Mesh *convex_hull_from_instances(const GeometrySet &geometry_set) if (set.has_mesh()) { read_positions(*set.get_component_for_read<MeshComponent>(), transforms, &coords); } - if (set.has_curve()) { - read_curve_positions(*set.get_curve_for_read(), transforms, &coords); + if (set.has_curves()) { + read_curve_positions(*curves_to_curve_eval(*set.get_curves_for_read()), transforms, &coords); } } return hull_from_bullet(nullptr, coords); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc index 65aad0fcbf1..ce3058c7d42 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc @@ -59,10 +59,13 @@ class EndpointFieldInput final : public GeometryFieldInput { } const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - const CurveEval *curve = curve_component.get_for_read(); + if (!curve_component.has_curves()) { + return nullptr; + } - Array<int> control_point_offsets = curve->control_point_offsets(); + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read()); + Array<int> control_point_offsets = curve->control_point_offsets(); if (curve == nullptr || control_point_offsets.last() == 0) { return nullptr; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc index 9824b2b2ece..6702ee6c0aa 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc @@ -113,12 +113,13 @@ static Mesh *cdt_to_mesh(const blender::meshintersect::CDT_result<double> &resul static void curve_fill_calculate(GeometrySet &geometry_set, const GeometryNodeCurveFillMode mode) { - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { return; } - const CurveEval &curve = *geometry_set.get_curve_for_read(); - if (curve.splines().is_empty()) { + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *geometry_set.get_curves_for_read()); + if (curve->splines().is_empty()) { geometry_set.replace_curve(nullptr); return; } @@ -127,7 +128,7 @@ static void curve_fill_calculate(GeometrySet &geometry_set, const GeometryNodeCu CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES : CDT_INSIDE_WITH_HOLES; - const blender::meshintersect::CDT_result<double> results = do_cdt(curve, output_type); + const blender::meshintersect::CDT_result<double> results = do_cdt(*curve, output_type); Mesh *mesh = cdt_to_mesh(results); geometry_set.replace_mesh(mesh); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc index 94425ab48f4..24d72ad553b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc @@ -394,9 +394,9 @@ static void update_bezier_positions(const FilletData &fd, dst_spline.handle_positions_left()[end_i] = dst_spline.positions()[end_i] - handle_length * next_dir; dst_spline.handle_types_right()[i_dst] = dst_spline.handle_types_left()[end_i] = - BezierSpline::HandleType::Align; + BEZIER_HANDLE_ALIGN; dst_spline.handle_types_left()[i_dst] = dst_spline.handle_types_right()[end_i] = - BezierSpline::HandleType::Vector; + BEZIER_HANDLE_VECTOR; dst_spline.mark_cache_invalid(); /* Calculate the center of the radius to be formed. */ @@ -406,8 +406,8 @@ static void update_bezier_positions(const FilletData &fd, float radius; radius_vec = math::normalize_and_get_length(radius_vec, radius); - dst_spline.handle_types_right().slice(1, count - 2).fill(BezierSpline::HandleType::Align); - dst_spline.handle_types_left().slice(1, count - 2).fill(BezierSpline::HandleType::Align); + dst_spline.handle_types_right().slice(1, count - 2).fill(BEZIER_HANDLE_ALIGN); + dst_spline.handle_types_left().slice(1, count - 2).fill(BEZIER_HANDLE_ALIGN); /* For each of the vertices in between the end points. */ for (const int j : IndexRange(1, count - 2)) { @@ -512,12 +512,12 @@ static SplinePtr fillet_spline(const Spline &spline, copy_common_attributes_by_mapping(spline, *dst_spline_ptr, dst_to_src); switch (spline.type()) { - case Spline::Type::Bezier: { + case CURVE_TYPE_BEZIER: { const BezierSpline &src_spline = static_cast<const BezierSpline &>(spline); BezierSpline &dst_spline = static_cast<BezierSpline &>(*dst_spline_ptr); if (fillet_param.mode == GEO_NODE_CURVE_FILLET_POLY) { - dst_spline.handle_types_left().fill(BezierSpline::HandleType::Vector); - dst_spline.handle_types_right().fill(BezierSpline::HandleType::Vector); + dst_spline.handle_types_left().fill(BEZIER_HANDLE_VECTOR); + dst_spline.handle_types_right().fill(BEZIER_HANDLE_VECTOR); update_poly_positions(fd, dst_spline, src_spline, point_counts); } else { @@ -525,17 +525,21 @@ static SplinePtr fillet_spline(const Spline &spline, } break; } - case Spline::Type::Poly: { + case CURVE_TYPE_POLY: { update_poly_positions(fd, *dst_spline_ptr, spline, point_counts); break; } - case Spline::Type::NURBS: { + case CURVE_TYPE_NURBS: { const NURBSpline &src_spline = static_cast<const NURBSpline &>(spline); NURBSpline &dst_spline = static_cast<NURBSpline &>(*dst_spline_ptr); copy_attribute_by_mapping(src_spline.weights(), dst_spline.weights(), dst_to_src); update_poly_positions(fd, dst_spline, src_spline, point_counts); break; } + case CURVE_TYPE_CATMULL_ROM: { + BLI_assert_unreachable(); + break; + } } return dst_spline_ptr; @@ -568,7 +572,7 @@ static void calculate_curve_fillet(GeometrySet &geometry_set, const std::optional<Field<int>> &count_field, const bool limit_radius) { - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { return; } @@ -599,10 +603,10 @@ static void calculate_curve_fillet(GeometrySet &geometry_set, fillet_param.limit_radius = limit_radius; - const CurveEval &input_curve = *geometry_set.get_curve_for_read(); - std::unique_ptr<CurveEval> output_curve = fillet_curve(input_curve, fillet_param); + const std::unique_ptr<CurveEval> input_curve = curves_to_curve_eval(*component.get_for_read()); + std::unique_ptr<CurveEval> output_curve = fillet_curve(*input_curve, fillet_param); - geometry_set.replace_curve(output_curve.release()); + geometry_set.replace_curve(curve_eval_to_curves(*output_curve)); } static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc index 26a8ad2d988..ccd3a587e63 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc @@ -31,30 +31,30 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) node->storage = data; } -static BezierSpline::HandleType handle_type_from_input_type(const GeometryNodeCurveHandleType type) +static HandleType handle_type_from_input_type(const GeometryNodeCurveHandleType type) { switch (type) { case GEO_NODE_CURVE_HANDLE_AUTO: - return BezierSpline::HandleType::Auto; + return BEZIER_HANDLE_AUTO; case GEO_NODE_CURVE_HANDLE_ALIGN: - return BezierSpline::HandleType::Align; + return BEZIER_HANDLE_ALIGN; case GEO_NODE_CURVE_HANDLE_FREE: - return BezierSpline::HandleType::Free; + return BEZIER_HANDLE_FREE; case GEO_NODE_CURVE_HANDLE_VECTOR: - return BezierSpline::HandleType::Vector; + return BEZIER_HANDLE_VECTOR; } BLI_assert_unreachable(); - return BezierSpline::HandleType::Auto; + return BEZIER_HANDLE_AUTO; } static void select_by_handle_type(const CurveEval &curve, - const BezierSpline::HandleType type, + const HandleType type, const GeometryNodeCurveHandleMode mode, const MutableSpan<bool> r_selection) { int offset = 0; for (const SplinePtr &spline : curve.splines()) { - if (spline->type() != Spline::Type::Bezier) { + if (spline->type() != CURVE_TYPE_BEZIER) { r_selection.slice(offset, spline->size()).fill(false); offset += spline->size(); } @@ -71,11 +71,11 @@ static void select_by_handle_type(const CurveEval &curve, } class HandleTypeFieldInput final : public GeometryFieldInput { - BezierSpline::HandleType type_; + HandleType type_; GeometryNodeCurveHandleMode mode_; public: - HandleTypeFieldInput(BezierSpline::HandleType type, GeometryNodeCurveHandleMode mode) + HandleTypeFieldInput(HandleType type, GeometryNodeCurveHandleMode mode) : GeometryFieldInput(CPPType::get<bool>(), "Handle Type Selection node"), type_(type), mode_(mode) @@ -92,14 +92,14 @@ class HandleTypeFieldInput final : public GeometryFieldInput { } const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - const CurveEval *curve = curve_component.get_for_read(); + const Curves *curve = curve_component.get_for_read(); if (curve == nullptr) { return {}; } if (domain == ATTR_DOMAIN_POINT) { Array<bool> selection(mask.min_array_size()); - select_by_handle_type(*curve, type_, mode_, selection); + select_by_handle_type(*curves_to_curve_eval(*curve), type_, mode_, selection); return VArray<bool>::ForContainer(std::move(selection)); } return {}; @@ -124,7 +124,7 @@ class HandleTypeFieldInput final : public GeometryFieldInput { static void node_geo_exec(GeoNodeExecParams params) { const NodeGeometryCurveSelectHandles &storage = node_storage(params.node()); - const BezierSpline::HandleType handle_type = handle_type_from_input_type( + const HandleType handle_type = handle_type_from_input_type( (GeometryNodeCurveHandleType)storage.handle_type); const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)storage.mode; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc index 82621189964..d5769c691c8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc @@ -14,13 +14,13 @@ static void node_declare(NodeDeclarationBuilder &b) static void node_geo_exec(GeoNodeExecParams params) { GeometrySet curve_set = params.extract_input<GeometrySet>("Curve"); - if (!curve_set.has_curve()) { + if (!curve_set.has_curves()) { params.set_default_remaining_outputs(); return; } - const CurveEval &curve = *curve_set.get_curve_for_read(); + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_set.get_curves_for_read()); float length = 0.0f; - for (const SplinePtr &spline : curve.splines()) { + for (const SplinePtr &spline : curve->splines()) { length += spline->length(); } params.set_output("Length", length); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc index 9919e24473e..6c7d7ed375b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_arc.cc @@ -1,11 +1,15 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BKE_spline.hh" +#include <numeric> + #include "BLI_math_base_safe.h" + +#include "BKE_curves.hh" + #include "UI_interface.h" #include "UI_resources.h" + #include "node_geometry_util.hh" -#include <numeric> namespace blender::nodes::node_geo_curve_primitive_arc_cc { @@ -139,32 +143,24 @@ static bool colinear_f3_f3_f3(const float3 p1, const float3 p2, const float3 p3) return (ELEM(a, b, b * -1.0f)); } -static std::unique_ptr<CurveEval> create_arc_curve_from_points(const int resolution, - const float3 a, - const float3 b, - const float3 c, - float angle_offset, - const bool connect_center, - const bool invert_arc, - float3 &r_center, - float3 &r_normal, - float &r_radius) +static Curves *create_arc_curve_from_points(const int resolution, + const float3 a, + const float3 b, + const float3 c, + float angle_offset, + const bool connect_center, + const bool invert_arc, + float3 &r_center, + float3 &r_normal, + float &r_radius) { - std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); - - if (connect_center) { - spline->resize(resolution + 1); - } - else { - spline->resize(resolution); - } + const int size = connect_center ? resolution + 1 : resolution; + Curves *curves_id = bke::curves_new_nomain_single(size, CURVE_TYPE_POLY); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); const int stepcount = resolution - 1; const int centerpoint = resolution; - MutableSpan<float3> positions = spline->positions(); - spline->radii().fill(1.0f); - spline->tilts().fill(0.0f); + MutableSpan<float3> positions = curves.positions(); const bool is_colinear = colinear_f3_f3_f3(a, b, c); @@ -254,7 +250,7 @@ static std::unique_ptr<CurveEval> create_arc_curve_from_points(const int resolut } if (connect_center) { - spline->set_cyclic(true); + curves.cyclic().first() = true; positions[centerpoint] = center; } @@ -263,36 +259,26 @@ static std::unique_ptr<CurveEval> create_arc_curve_from_points(const int resolut normal = -normal; } - curve->add_spline(std::move(spline)); - curve->attributes.reallocate(curve->splines().size()); r_center = center; r_radius = radius; r_normal = normal; - return curve; + return curves_id; } -static std::unique_ptr<CurveEval> create_arc_curve_from_radius(const int resolution, - const float radius, - const float start_angle, - const float sweep_angle, - const bool connect_center, - const bool invert_arc) +static Curves *create_arc_curve_from_radius(const int resolution, + const float radius, + const float start_angle, + const float sweep_angle, + const bool connect_center, + const bool invert_arc) { - std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); - - if (connect_center) { - spline->resize(resolution + 1); - } - else { - spline->resize(resolution); - } + const int size = connect_center ? resolution + 1 : resolution; + Curves *curves_id = bke::curves_new_nomain_single(size, CURVE_TYPE_POLY); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); const int stepcount = resolution - 1; const int centerpoint = resolution; - MutableSpan<float3> positions = spline->positions(); - spline->radii().fill(1.0f); - spline->tilts().fill(0.0f); + MutableSpan<float3> positions = curves.positions(); const float sweep = (invert_arc) ? -(2.0f * M_PI - sweep_angle) : sweep_angle; @@ -305,13 +291,11 @@ static std::unique_ptr<CurveEval> create_arc_curve_from_radius(const int resolut } if (connect_center) { - spline->set_cyclic(true); + curves.cyclic().first() = true; positions[centerpoint] = float3(0.0f, 0.0f, 0.0f); } - curve->add_spline(std::move(spline)); - curve->attributes.reallocate(curve->splines().size()); - return curve; + return curves_id; } static void node_geo_exec(GeoNodeExecParams params) @@ -322,35 +306,35 @@ static void node_geo_exec(GeoNodeExecParams params) switch (mode) { case GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_POINTS: { - std::unique_ptr<CurveEval> curve; float3 r_center, r_normal; float r_radius; - curve = create_arc_curve_from_points(std::max(params.extract_input<int>("Resolution"), 2), - params.extract_input<float3>("Start"), - params.extract_input<float3>("Middle"), - params.extract_input<float3>("End"), - params.extract_input<float>("Offset Angle"), - params.extract_input<bool>("Connect Center"), - params.extract_input<bool>("Invert Arc"), - r_center, - r_normal, - r_radius); - params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); + Curves *curves = create_arc_curve_from_points( + std::max(params.extract_input<int>("Resolution"), 2), + params.extract_input<float3>("Start"), + params.extract_input<float3>("Middle"), + params.extract_input<float3>("End"), + params.extract_input<float>("Offset Angle"), + params.extract_input<bool>("Connect Center"), + params.extract_input<bool>("Invert Arc"), + r_center, + r_normal, + r_radius); + params.set_output("Curve", GeometrySet::create_with_curves(curves)); params.set_output("Center", r_center); params.set_output("Normal", r_normal); params.set_output("Radius", r_radius); break; } case GEO_NODE_CURVE_PRIMITIVE_ARC_TYPE_RADIUS: { - std::unique_ptr<CurveEval> curve; - curve = create_arc_curve_from_radius(std::max(params.extract_input<int>("Resolution"), 2), - params.extract_input<float>("Radius"), - params.extract_input<float>("Start Angle"), - params.extract_input<float>("Sweep Angle"), - params.extract_input<bool>("Connect Center"), - params.extract_input<bool>("Invert Arc")); - - params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); + Curves *curves = create_arc_curve_from_radius( + std::max(params.extract_input<int>("Resolution"), 2), + params.extract_input<float>("Radius"), + params.extract_input<float>("Start Angle"), + params.extract_input<float>("Sweep Angle"), + params.extract_input<bool>("Connect Center"), + params.extract_input<bool>("Invert Arc")); + + params.set_output("Curve", GeometrySet::create_with_curves(curves)); break; } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc index c6b9018f0db..78e1613b630 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc @@ -69,21 +69,30 @@ static std::unique_ptr<CurveEval> create_bezier_segment_curve( spline->resize(2); MutableSpan<float3> positions = spline->positions(); - spline->handle_types_left().fill(BezierSpline::HandleType::Align); - spline->handle_types_right().fill(BezierSpline::HandleType::Align); + spline->handle_types_left().fill(BEZIER_HANDLE_ALIGN); + spline->handle_types_right().fill(BEZIER_HANDLE_ALIGN); spline->radii().fill(1.0f); spline->tilts().fill(0.0f); positions.first() = start; positions.last() = end; + MutableSpan<float3> handles_right = spline->handle_positions_right(); + MutableSpan<float3> handles_left = spline->handle_positions_left(); + if (mode == GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT_POSITION) { - spline->set_handle_position_right(0, start_handle_right); - spline->set_handle_position_left(1, end_handle_left); + handles_left.first() = 2.0f * start - start_handle_right; + handles_right.first() = start_handle_right; + + handles_left.last() = end_handle_left; + handles_right.last() = 2.0f * end - end_handle_left; } else { - spline->set_handle_position_right(0, start + start_handle_right); - spline->set_handle_position_left(1, end + end_handle_left); + handles_left.first() = start - start_handle_right; + handles_right.first() = start + start_handle_right; + + handles_left.last() = end + end_handle_left; + handles_right.last() = end - end_handle_left; } curve->add_spline(std::move(spline)); @@ -104,7 +113,7 @@ static void node_geo_exec(GeoNodeExecParams params) params.extract_input<float3>("End Handle"), std::max(params.extract_input<int>("Resolution"), 1), mode); - params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); + params.set_output("Curve", GeometrySet::create_with_curves(curve_eval_to_curves(*curve))); } } // namespace blender::nodes::node_geo_curve_primitive_bezier_segment_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc index 44505b61a27..874e29dda86 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BKE_spline.hh" +#include "BKE_curves.hh" #include "UI_interface.h" #include "UI_resources.h" @@ -92,7 +92,7 @@ static bool colinear_f3_f3_f3(const float3 p1, const float3 p2, const float3 p3) return (ELEM(a, b, b * -1.0f)); } -static std::unique_ptr<CurveEval> create_point_circle_curve( +static Curves *create_point_circle_curve( const float3 p1, const float3 p2, const float3 p3, const int resolution, float3 &r_center) { if (colinear_f3_f3_f3(p1, p2, p3)) { @@ -100,11 +100,11 @@ static std::unique_ptr<CurveEval> create_point_circle_curve( return nullptr; } - std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); + Curves *curves_id = bke::curves_new_nomain_single(resolution, CURVE_TYPE_POLY); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); + curves.cyclic().first() = true; - spline->resize(resolution); - MutableSpan<float3> positions = spline->positions(); + MutableSpan<float3> positions = curves.positions(); float3 center; /* Midpoints of `P1->P2` and `P2->P3`. */ @@ -147,24 +147,17 @@ static std::unique_ptr<CurveEval> create_point_circle_curve( positions[i] = center + r * sin(theta) * v1 + r * cos(theta) * v4; } - spline->radii().fill(1.0f); - spline->tilts().fill(0.0f); - spline->set_cyclic(true); - curve->add_spline(std::move(spline)); - curve->attributes.reallocate(curve->splines().size()); - r_center = center; - return curve; + return curves_id; } -static std::unique_ptr<CurveEval> create_radius_circle_curve(const int resolution, - const float radius) +static Curves *create_radius_circle_curve(const int resolution, const float radius) { - std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); + Curves *curves_id = bke::curves_new_nomain_single(resolution, CURVE_TYPE_POLY); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); + curves.cyclic().first() = true; - spline->resize(resolution); - MutableSpan<float3> positions = spline->positions(); + MutableSpan<float3> positions = curves.positions(); const float theta_step = (2.0f * M_PI) / float(resolution); for (int i : IndexRange(resolution)) { @@ -173,12 +166,8 @@ static std::unique_ptr<CurveEval> create_radius_circle_curve(const int resolutio const float y = radius * sin(theta); positions[i] = float3(x, y, 0.0f); } - spline->radii().fill(1.0f); - spline->tilts().fill(0.0f); - spline->set_cyclic(true); - curve->add_spline(std::move(spline)); - curve->attributes.reallocate(curve->splines().size()); - return curve; + + return curves_id; } static void node_geo_exec(GeoNodeExecParams params) @@ -187,23 +176,23 @@ static void node_geo_exec(GeoNodeExecParams params) const GeometryNodeCurvePrimitiveCircleMode mode = (GeometryNodeCurvePrimitiveCircleMode) storage.mode; - std::unique_ptr<CurveEval> curve; + Curves *curves; if (mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS) { float3 center_point; - curve = create_point_circle_curve(params.extract_input<float3>("Point 1"), - params.extract_input<float3>("Point 2"), - params.extract_input<float3>("Point 3"), - std::max(params.extract_input<int>("Resolution"), 3), - center_point); + curves = create_point_circle_curve(params.extract_input<float3>("Point 1"), + params.extract_input<float3>("Point 2"), + params.extract_input<float3>("Point 3"), + std::max(params.extract_input<int>("Resolution"), 3), + center_point); params.set_output("Center", center_point); } else if (mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_RADIUS) { - curve = create_radius_circle_curve(std::max(params.extract_input<int>("Resolution"), 3), - params.extract_input<float>("Radius")); + curves = create_radius_circle_curve(std::max(params.extract_input<int>("Resolution"), 3), + params.extract_input<float>("Radius")); } - if (curve) { - params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); + if (curves) { + params.set_output("Curve", GeometrySet::create_with_curves(curves)); } else { params.set_default_remaining_outputs(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc index d11af3b1cc0..2e2f4254752 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BKE_spline.hh" +#include "BKE_curves.hh" #include "UI_interface.h" #include "UI_resources.h" @@ -60,39 +60,28 @@ static void node_update(bNodeTree *ntree, bNode *node) ntree, length_socket, mode == GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_DIRECTION); } -static std::unique_ptr<CurveEval> create_point_line_curve(const float3 start, const float3 end) +static Curves *create_point_line_curve(const float3 start, const float3 end) { - std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); - - spline->resize(2); - MutableSpan<float3> positions = spline->positions(); - positions[0] = start; - positions[1] = end; - spline->radii().fill(1.0f); - spline->tilts().fill(0.0f); - curve->add_spline(std::move(spline)); - curve->attributes.reallocate(curve->splines().size()); - return curve; + Curves *curves_id = bke::curves_new_nomain_single(2, CURVE_TYPE_POLY); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); + + curves.positions().first() = start; + curves.positions().last() = end; + + return curves_id; } -static std::unique_ptr<CurveEval> create_direction_line_curve(const float3 start, - const float3 direction, - const float length) +static Curves *create_direction_line_curve(const float3 start, + const float3 direction, + const float length) { - std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); - - spline->resize(2); - MutableSpan<float3> positions = spline->positions(); - positions[0] = start; - positions[1] = math::normalize(direction) * length + start; - - spline->radii().fill(1.0f); - spline->tilts().fill(0.0f); - curve->add_spline(std::move(spline)); - curve->attributes.reallocate(curve->splines().size()); - return curve; + Curves *curves_id = bke::curves_new_nomain_single(2, CURVE_TYPE_POLY); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); + + curves.positions().first() = start; + curves.positions().last() = math::normalize(direction) * length + start; + + return curves_id; } static void node_geo_exec(GeoNodeExecParams params) @@ -100,18 +89,18 @@ static void node_geo_exec(GeoNodeExecParams params) const NodeGeometryCurvePrimitiveLine &storage = node_storage(params.node()); const GeometryNodeCurvePrimitiveLineMode mode = (GeometryNodeCurvePrimitiveLineMode)storage.mode; - std::unique_ptr<CurveEval> curve; + Curves *curves = nullptr; if (mode == GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_POINTS) { - curve = create_point_line_curve(params.extract_input<float3>("Start"), - params.extract_input<float3>("End")); + curves = create_point_line_curve(params.extract_input<float3>("Start"), + params.extract_input<float3>("End")); } else if (mode == GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_DIRECTION) { - curve = create_direction_line_curve(params.extract_input<float3>("Start"), - params.extract_input<float3>("Direction"), - params.extract_input<float>("Length")); + curves = create_direction_line_curve(params.extract_input<float3>("Start"), + params.extract_input<float3>("Direction"), + params.extract_input<float>("Length")); } - params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); + params.set_output("Curve", GeometrySet::create_with_curves(curves)); } } // namespace blender::nodes::node_geo_curve_primitive_line_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc index 456f6e55c1e..37810ccaff5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BKE_spline.hh" +#include "BKE_curves.hh" #include "node_geometry_util.hh" namespace blender::nodes::node_geo_curve_primitive_quadratic_bezier_cc { @@ -28,18 +28,15 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Curve")); } -static std::unique_ptr<CurveEval> create_quadratic_bezier_curve(const float3 p1, - const float3 p2, - const float3 p3, - const int resolution) +static Curves *create_quadratic_bezier_curve(const float3 p1, + const float3 p2, + const float3 p3, + const int resolution) { - std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); + Curves *curves_id = bke::curves_new_nomain_single(resolution + 1, CURVE_TYPE_POLY); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); - spline->resize(resolution + 1); - MutableSpan<float3> positions = spline->positions(); - spline->radii().fill(1.0f); - spline->tilts().fill(0.0f); + MutableSpan<float3> positions = curves.positions(); const float step = 1.0f / resolution; for (const int i : IndexRange(resolution + 1)) { @@ -49,19 +46,17 @@ static std::unique_ptr<CurveEval> create_quadratic_bezier_curve(const float3 p1, positions[i] = math::interpolate(q1, q2, factor); } - curve->add_spline(std::move(spline)); - curve->attributes.reallocate(curve->splines().size()); - return curve; + return curves_id; } static void node_geo_exec(GeoNodeExecParams params) { - std::unique_ptr<CurveEval> curve = create_quadratic_bezier_curve( + Curves *curves = create_quadratic_bezier_curve( params.extract_input<float3>("Start"), params.extract_input<float3>("Middle"), params.extract_input<float3>("End"), std::max(params.extract_input<int>("Resolution"), 3)); - params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); + params.set_output("Curve", GeometrySet::create_with_curves(curves)); } } // namespace blender::nodes::node_geo_curve_primitive_quadratic_bezier_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc index b6a847eebf4..ad3123a6a4a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BKE_spline.hh" +#include "BKE_curves.hh" #include "UI_interface.h" #include "UI_resources.h" @@ -216,13 +216,11 @@ static void node_geo_exec(GeoNodeExecParams params) const NodeGeometryCurvePrimitiveQuad &storage = node_storage(params.node()); const GeometryNodeCurvePrimitiveQuadMode mode = (GeometryNodeCurvePrimitiveQuadMode)storage.mode; - std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); - spline->resize(4); - spline->radii().fill(1.0f); - spline->tilts().fill(0.0f); - spline->set_cyclic(true); - MutableSpan<float3> positions = spline->positions(); + Curves *curves_id = bke::curves_new_nomain_single(4, CURVE_TYPE_POLY); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); + curves.cyclic().first() = true; + + MutableSpan<float3> positions = curves.positions(); switch (mode) { case GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_RECTANGLE: @@ -262,9 +260,7 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - curve->add_spline(std::move(spline)); - curve->attributes.reallocate(curve->splines().size()); - params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); + params.set_output("Curve", GeometrySet::create_with_curves(curves_id)); } } // namespace blender::nodes::node_geo_curve_primitive_quadrilateral_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc index f448ddabd2b..22619577d04 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BKE_spline.hh" +#include "BKE_curves.hh" #include "node_geometry_util.hh" @@ -35,26 +35,23 @@ static void node_declare(NodeDeclarationBuilder &b) b.add_output<decl::Geometry>(N_("Curve")); } -static std::unique_ptr<CurveEval> create_spiral_curve(const float rotations, - const int resolution, - const float start_radius, - const float end_radius, - const float height, - const bool direction) +static Curves *create_spiral_curve(const float rotations, + const int resolution, + const float start_radius, + const float end_radius, + const float height, + const bool direction) { - std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); - const int totalpoints = std::max(int(resolution * rotations), 1); const float delta_radius = (end_radius - start_radius) / (float)totalpoints; const float delta_height = height / (float)totalpoints; const float delta_theta = (M_PI * 2 * rotations) / (float)totalpoints * (direction ? 1.0f : -1.0f); - spline->resize(totalpoints + 1); - MutableSpan<float3> positions = spline->positions(); - spline->radii().fill(1.0f); - spline->tilts().fill(0.0f); + Curves *curves_id = bke::curves_new_nomain_single(totalpoints + 1, CURVE_TYPE_POLY); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); + + MutableSpan<float3> positions = curves.positions(); for (const int i : IndexRange(totalpoints + 1)) { const float theta = i * delta_theta; @@ -66,9 +63,7 @@ static std::unique_ptr<CurveEval> create_spiral_curve(const float rotations, positions[i] = {x, y, z}; } - curve->add_spline(std::move(spline)); - curve->attributes.reallocate(curve->splines().size()); - return curve; + return curves_id; } static void node_geo_exec(GeoNodeExecParams params) @@ -79,14 +74,13 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - std::unique_ptr<CurveEval> curve = create_spiral_curve( - rotations, - std::max(params.extract_input<int>("Resolution"), 1), - params.extract_input<float>("Start Radius"), - params.extract_input<float>("End Radius"), - params.extract_input<float>("Height"), - params.extract_input<bool>("Reverse")); - params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); + Curves *curves = create_spiral_curve(rotations, + std::max(params.extract_input<int>("Resolution"), 1), + params.extract_input<float>("Start Radius"), + params.extract_input<float>("End Radius"), + params.extract_input<float>("Height"), + params.extract_input<bool>("Reverse")); + params.set_output("Curve", GeometrySet::create_with_curves(curves)); } } // namespace blender::nodes::node_geo_curve_primitive_spiral_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc index 5969af43bc1..e7e899881cf 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ -#include "BKE_spline.hh" +#include "BKE_curves.hh" #include "node_geometry_util.hh" @@ -33,19 +33,16 @@ static void node_declare(NodeDeclarationBuilder &b) .description(N_("An attribute field with a selection of the outer points")); } -static std::unique_ptr<CurveEval> create_star_curve(const float inner_radius, - const float outer_radius, - const float twist, - const int points) +static Curves *create_star_curve(const float inner_radius, + const float outer_radius, + const float twist, + const int points) { - std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); - std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); - spline->set_cyclic(true); + Curves *curves_id = bke::curves_new_nomain_single(points * 2, CURVE_TYPE_POLY); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); + curves.cyclic().first() = true; - spline->resize(points * 2); - MutableSpan<float3> positions = spline->positions(); - spline->radii().fill(1.0f); - spline->tilts().fill(0.0f); + MutableSpan<float3> positions = curves.positions(); const float theta_step = (2.0f * M_PI) / float(points); for (const int i : IndexRange(points)) { @@ -58,10 +55,7 @@ static std::unique_ptr<CurveEval> create_star_curve(const float inner_radius, positions[i * 2 + 1] = {inner_x, inner_y, 0.0f}; } - curve->add_spline(std::move(spline)); - curve->attributes.reallocate(curve->splines().size()); - - return curve; + return curves_id; } static void create_selection_output(CurveComponent &component, @@ -78,12 +72,11 @@ static void create_selection_output(CurveComponent &component, static void node_geo_exec(GeoNodeExecParams params) { - std::unique_ptr<CurveEval> curve = create_star_curve( - std::max(params.extract_input<float>("Inner Radius"), 0.0f), - std::max(params.extract_input<float>("Outer Radius"), 0.0f), - params.extract_input<float>("Twist"), - std::max(params.extract_input<int>("Points"), 3)); - GeometrySet output = GeometrySet::create_with_curve(curve.release()); + Curves *curves = create_star_curve(std::max(params.extract_input<float>("Inner Radius"), 0.0f), + std::max(params.extract_input<float>("Outer Radius"), 0.0f), + params.extract_input<float>("Twist"), + std::max(params.extract_input<int>("Points"), 3)); + GeometrySet output = GeometrySet::create_with_curves(curves); if (params.output_is_required("Outer Points")) { StrongAnonymousAttributeID attribute_output("Outer Points"); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index d2afeaa7094..c5814a9a1dd 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -158,7 +158,7 @@ static SplinePtr resample_spline_evaluated(const Spline &src) static std::unique_ptr<CurveEval> resample_curve(const CurveComponent *component, const SampleModeParam &mode_param) { - const CurveEval *input_curve = component->get_for_read(); + const std::unique_ptr<CurveEval> input_curve = curves_to_curve_eval(*component->get_for_read()); GeometryComponentFieldContext field_context{*component, ATTR_DOMAIN_CURVE}; const int domain_size = component->attribute_domain_size(ATTR_DOMAIN_CURVE); @@ -235,14 +235,14 @@ static std::unique_ptr<CurveEval> resample_curve(const CurveComponent *component static void geometry_set_curve_resample(GeometrySet &geometry_set, const SampleModeParam &mode_param) { - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { return; } std::unique_ptr<CurveEval> output_curve = resample_curve( geometry_set.get_component_for_read<CurveComponent>(), mode_param); - geometry_set.replace_curve(output_curve.release()); + geometry_set.replace_curve(curve_eval_to_curves(*output_curve)); } static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc index 0ef3230937b..8393f9615aa 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc @@ -20,7 +20,7 @@ static void node_geo_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { return; } @@ -34,13 +34,15 @@ static void node_geo_exec(GeoNodeExecParams params) selection_evaluator.evaluate(); const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); - CurveEval &curve = *component.get_for_write(); - MutableSpan<SplinePtr> splines = curve.splines(); + std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_write()); + MutableSpan<SplinePtr> splines = curve->splines(); threading::parallel_for(selection.index_range(), 128, [&](IndexRange range) { for (const int i : range) { splines[selection[i]]->reverse(); } }); + + component.replace(curve_eval_to_curves(*curve)); }); params.set_output("Curve", std::move(geometry_set)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc index 152828b284c..6661d03a851 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc @@ -122,12 +122,13 @@ class SampleCurveFunction : public fn::MultiFunction { } }; - if (!geometry_set_.has_curve()) { + if (!geometry_set_.has_curves()) { return return_default(); } const CurveComponent *curve_component = geometry_set_.get_component_for_read<CurveComponent>(); - const CurveEval *curve = curve_component->get_for_read(); + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *curve_component->get_for_read()); Span<SplinePtr> splines = curve->splines(); if (splines.is_empty()) { return return_default(); @@ -234,12 +235,13 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - const CurveEval *curve = component->get_for_read(); - if (curve == nullptr) { + if (!component->has_curves()) { params.set_default_remaining_outputs(); return; } + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component->get_for_read()); + if (curve->splines().is_empty()) { params.set_default_remaining_outputs(); return; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc index 3d7b2fddf72..e8da4154586 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc @@ -33,20 +33,20 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) node->storage = data; } -static BezierSpline::HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type) +static HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type) { switch (type) { case GEO_NODE_CURVE_HANDLE_AUTO: - return BezierSpline::HandleType::Auto; + return BEZIER_HANDLE_AUTO; case GEO_NODE_CURVE_HANDLE_ALIGN: - return BezierSpline::HandleType::Align; + return BEZIER_HANDLE_ALIGN; case GEO_NODE_CURVE_HANDLE_FREE: - return BezierSpline::HandleType::Free; + return BEZIER_HANDLE_FREE; case GEO_NODE_CURVE_HANDLE_VECTOR: - return BezierSpline::HandleType::Vector; + return BEZIER_HANDLE_VECTOR; } BLI_assert_unreachable(); - return BezierSpline::HandleType::Auto; + return BEZIER_HANDLE_AUTO; } static void node_geo_exec(GeoNodeExecParams params) @@ -60,14 +60,14 @@ static void node_geo_exec(GeoNodeExecParams params) bool has_bezier_spline = false; geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { return; } /* Retrieve data for write access so we can avoid new allocations for the handles data. */ CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>(); - CurveEval &curve = *curve_component.get_for_write(); - MutableSpan<SplinePtr> splines = curve.splines(); + std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read()); + MutableSpan<SplinePtr> splines = curve->splines(); GeometryComponentFieldContext field_context{curve_component, ATTR_DOMAIN_POINT}; const int domain_size = curve_component.attribute_domain_size(ATTR_DOMAIN_POINT); @@ -77,18 +77,18 @@ static void node_geo_exec(GeoNodeExecParams params) selection_evaluator.evaluate(); const VArray<bool> &selection = selection_evaluator.get_evaluated<bool>(0); - const BezierSpline::HandleType new_handle_type = handle_type_from_input_type(type); + const HandleType new_handle_type = handle_type_from_input_type(type); int point_index = 0; for (SplinePtr &spline : splines) { - if (spline->type() != Spline::Type::Bezier) { + if (spline->type() != CURVE_TYPE_BEZIER) { point_index += spline->positions().size(); continue; } has_bezier_spline = true; BezierSpline &bezier_spline = static_cast<BezierSpline &>(*spline); - if (ELEM(new_handle_type, BezierSpline::HandleType::Free, BezierSpline::HandleType::Align)) { + if (ELEM(new_handle_type, BEZIER_HANDLE_FREE, BEZIER_HANDLE_ALIGN)) { /* In this case the automatically calculated handle types need to be "baked", because * they're possibly changing from a type that is calculated automatically to a type that * is positioned manually. */ @@ -108,6 +108,8 @@ static void node_geo_exec(GeoNodeExecParams params) } bezier_spline.mark_cache_invalid(); } + + curve_component.replace(curve_eval_to_curves(*curve)); }); if (!has_bezier_spline) { params.error_message_add(NodeWarningType::Info, TIP_("No Bezier splines in input curve")); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc index a303be99242..3edaccba506 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc @@ -100,18 +100,22 @@ static Array<float> curve_length_point_domain(const CurveEval &curve) MutableSpan spline_factors{lengths.as_mutable_span().slice(offsets[i], spline.size())}; spline_factors.first() = 0.0f; switch (splines[i]->type()) { - case Spline::Type::Bezier: { + case CURVE_TYPE_BEZIER: { calculate_bezier_lengths(static_cast<const BezierSpline &>(spline), spline_factors); break; } - case Spline::Type::Poly: { + case CURVE_TYPE_POLY: { calculate_poly_length(static_cast<const PolySpline &>(spline), spline_factors); break; } - case Spline::Type::NURBS: { + case CURVE_TYPE_NURBS: { calculate_nurbs_lengths(static_cast<const NURBSpline &>(spline), spline_factors); break; } + case CURVE_TYPE_CATMULL_ROM: { + BLI_assert_unreachable(); + break; + } } } }); @@ -201,8 +205,9 @@ class CurveParameterFieldInput final : public GeometryFieldInput { { if (component.type() == GEO_COMPONENT_TYPE_CURVE) { const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - const CurveEval *curve = curve_component.get_for_read(); - if (curve) { + if (curve_component.has_curves()) { + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *curve_component.get_for_read()); return construct_curve_parameter_varray(*curve, mask, domain); } } @@ -234,8 +239,8 @@ class CurveLengthFieldInput final : public GeometryFieldInput { { if (component.type() == GEO_COMPONENT_TYPE_CURVE) { const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - const CurveEval *curve = curve_component.get_for_read(); - if (curve) { + if (curve_component.has_curves()) { + std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curve_component.get_for_read()); return construct_curve_length_varray(*curve, mask, domain); } } @@ -267,8 +272,9 @@ class IndexOnSplineFieldInput final : public GeometryFieldInput { { if (component.type() == GEO_COMPONENT_TYPE_CURVE) { const CurveComponent &curve_component = static_cast<const CurveComponent &>(component); - const CurveEval *curve = curve_component.get_for_read(); - if (curve) { + if (curve_component.has_curves()) { + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *curve_component.get_for_read()); return construct_index_on_spline_varray(*curve, mask, domain); } } 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 index 4e4cabd3c33..55610ec86ab 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc @@ -259,8 +259,8 @@ static SplinePtr poly_to_bezier(const Spline &input) 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->handle_types_left().fill(BEZIER_HANDLE_VECTOR); + output->handle_types_right().fill(BEZIER_HANDLE_VECTOR); output->set_resolution(12); Spline::copy_base_settings(input, *output); output->attributes = input.attributes; @@ -298,8 +298,8 @@ static SplinePtr nurbs_to_bezier(const Spline &input) nurbs_to_bezier_assign(nurbs_spline.tilts(), output->tilts(), knots_mode); scale_input_assign(handle_positions.as_span(), 2, 0, output->handle_positions_left()); scale_input_assign(handle_positions.as_span(), 2, 1, output->handle_positions_right()); - output->handle_types_left().fill(BezierSpline::HandleType::Align); - output->handle_types_right().fill(BezierSpline::HandleType::Align); + output->handle_types_left().fill(BEZIER_HANDLE_ALIGN); + output->handle_types_right().fill(BEZIER_HANDLE_ALIGN); output->set_resolution(nurbs_spline.resolution()); Spline::copy_base_settings(nurbs_spline, *output); output->attributes.reallocate(output->size()); @@ -315,11 +315,11 @@ static SplinePtr nurbs_to_bezier(const Spline &input) static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params) { switch (input.type()) { - case Spline::Type::Bezier: + case CURVE_TYPE_BEZIER: return input.copy(); - case Spline::Type::Poly: + case CURVE_TYPE_POLY: return poly_to_bezier(input); - case Spline::Type::NURBS: + case CURVE_TYPE_NURBS: if (input.size() < 4) { params.error_message_add( NodeWarningType::Info, @@ -327,6 +327,10 @@ static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params return input.copy(); } return nurbs_to_bezier(input); + case CURVE_TYPE_CATMULL_ROM: { + BLI_assert_unreachable(); + return {}; + } } BLI_assert_unreachable(); return {}; @@ -335,12 +339,15 @@ static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params static SplinePtr convert_to_nurbs(const Spline &input) { switch (input.type()) { - case Spline::Type::NURBS: + case CURVE_TYPE_NURBS: return input.copy(); - case Spline::Type::Bezier: + case CURVE_TYPE_BEZIER: return bezier_to_nurbs(input); - case Spline::Type::Poly: + case CURVE_TYPE_POLY: return poly_to_nurbs(input); + case CURVE_TYPE_CATMULL_ROM: + BLI_assert_unreachable(); + return {}; } BLI_assert_unreachable(); return {}; @@ -355,45 +362,48 @@ static void node_geo_exec(GeoNodeExecParams params) Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { return; } const CurveComponent *curve_component = geometry_set.get_component_for_read<CurveComponent>(); - const CurveEval &curve = *curve_component->get_for_read(); + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *curve_component->get_for_read()); GeometryComponentFieldContext field_context{*curve_component, ATTR_DOMAIN_CURVE}; const int domain_size = curve_component->attribute_domain_size(ATTR_DOMAIN_CURVE); + Span<SplinePtr> src_splines = curve->splines(); + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const VArray<bool> &selection = selection_evaluator.get_evaluated<bool>(0); std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>(); - new_curve->resize(curve.splines().size()); + new_curve->resize(src_splines.size()); - threading::parallel_for(curve.splines().index_range(), 512, [&](IndexRange range) { + threading::parallel_for(src_splines.index_range(), 512, [&](IndexRange range) { for (const int i : range) { if (selection[i]) { switch (output_type) { case GEO_NODE_SPLINE_TYPE_POLY: - new_curve->splines()[i] = convert_to_poly_spline(*curve.splines()[i]); + new_curve->splines()[i] = convert_to_poly_spline(*src_splines[i]); break; case GEO_NODE_SPLINE_TYPE_BEZIER: - new_curve->splines()[i] = convert_to_bezier(*curve.splines()[i], params); + new_curve->splines()[i] = convert_to_bezier(*src_splines[i], params); break; case GEO_NODE_SPLINE_TYPE_NURBS: - new_curve->splines()[i] = convert_to_nurbs(*curve.splines()[i]); + new_curve->splines()[i] = convert_to_nurbs(*src_splines[i]); break; } } else { - new_curve->splines()[i] = curve.splines()[i]->copy(); + new_curve->splines()[i] = src_splines[i]->copy(); } } }); - new_curve->attributes = curve.attributes; - geometry_set.replace_curve(new_curve.release()); + new_curve->attributes = curve->attributes; + geometry_set.replace_curve(curve_eval_to_curves(*new_curve)); }); params.set_output("Curve", std::move(geometry_set)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc index 9daf3ff0594..bbe57b2b3fa 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc @@ -93,8 +93,8 @@ static void subdivide_bezier_segment(const BezierSpline &src, MutableSpan<float3> dst_positions, MutableSpan<float3> dst_handles_left, MutableSpan<float3> dst_handles_right, - MutableSpan<BezierSpline::HandleType> dst_type_left, - MutableSpan<BezierSpline::HandleType> dst_type_right) + MutableSpan<int8_t> dst_type_left, + MutableSpan<int8_t> dst_type_right) { const bool is_last_cyclic_segment = index == (src.size() - 1); const int next_index = is_last_cyclic_segment ? 0 : index + 1; @@ -104,10 +104,10 @@ static void subdivide_bezier_segment(const BezierSpline &src, if (src.segment_is_vector(index)) { if (is_last_cyclic_segment) { - dst_type_left.first() = BezierSpline::HandleType::Vector; + dst_type_left.first() = BEZIER_HANDLE_VECTOR; } - dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Vector); - dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Vector); + dst_type_left.slice(offset + 1, result_size).fill(BEZIER_HANDLE_VECTOR); + dst_type_right.slice(offset, result_size).fill(BEZIER_HANDLE_VECTOR); const float factor_delta = 1.0f / result_size; for (const int cut : IndexRange(result_size)) { @@ -118,10 +118,10 @@ static void subdivide_bezier_segment(const BezierSpline &src, } else { if (is_last_cyclic_segment) { - dst_type_left.first() = BezierSpline::HandleType::Free; + dst_type_left.first() = BEZIER_HANDLE_FREE; } - dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Free); - dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Free); + dst_type_left.slice(offset + 1, result_size).fill(BEZIER_HANDLE_FREE); + dst_type_right.slice(offset, result_size).fill(BEZIER_HANDLE_FREE); const int i_segment_last = is_last_cyclic_segment ? 0 : offset + result_size; @@ -169,8 +169,8 @@ static void subdivide_bezier_spline(const BezierSpline &src, MutableSpan<float3> dst_positions = dst.positions(); MutableSpan<float3> dst_handles_left = dst.handle_positions_left(); MutableSpan<float3> dst_handles_right = dst.handle_positions_right(); - MutableSpan<BezierSpline::HandleType> dst_type_left = dst.handle_types_left(); - MutableSpan<BezierSpline::HandleType> dst_type_right = dst.handle_types_right(); + MutableSpan<int8_t> dst_type_left = dst.handle_types_left(); + MutableSpan<int8_t> dst_type_right = dst.handle_types_right(); threading::parallel_for(IndexRange(src.size() - 1), 512, [&](IndexRange range) { for (const int i : range) { @@ -217,26 +217,30 @@ static void subdivide_builtin_attributes(const Spline &src_spline, subdivide_attribute<float>(src_spline.radii(), offsets, is_cyclic, dst_spline.radii()); subdivide_attribute<float>(src_spline.tilts(), offsets, is_cyclic, dst_spline.tilts()); switch (src_spline.type()) { - case Spline::Type::Poly: { + case CURVE_TYPE_POLY: { const PolySpline &src = static_cast<const PolySpline &>(src_spline); PolySpline &dst = static_cast<PolySpline &>(dst_spline); subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions()); break; } - case Spline::Type::Bezier: { + case CURVE_TYPE_BEZIER: { const BezierSpline &src = static_cast<const BezierSpline &>(src_spline); BezierSpline &dst = static_cast<BezierSpline &>(dst_spline); subdivide_bezier_spline(src, offsets, dst); dst.mark_cache_invalid(); break; } - case Spline::Type::NURBS: { + case CURVE_TYPE_NURBS: { const NURBSpline &src = static_cast<const NURBSpline &>(src_spline); NURBSpline &dst = static_cast<NURBSpline &>(dst_spline); subdivide_attribute<float3>(src.positions(), offsets, is_cyclic, dst.positions()); subdivide_attribute<float>(src.weights(), offsets, is_cyclic, dst.weights()); break; } + case CURVE_TYPE_CATMULL_ROM: { + BLI_assert_unreachable(); + break; + } } } @@ -320,7 +324,7 @@ static void node_geo_exec(GeoNodeExecParams params) Field<int> cuts_field = params.extract_input<Field<int>>("Cuts"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { return; } @@ -336,9 +340,9 @@ static void node_geo_exec(GeoNodeExecParams params) if (cuts.is_single() && cuts.get_internal_single() < 1) { return; } - - std::unique_ptr<CurveEval> output_curve = subdivide_curve(*component.get_for_read(), cuts); - geometry_set.replace_curve(output_curve.release()); + std::unique_ptr<CurveEval> output_curve = subdivide_curve( + *curves_to_curve_eval(*component.get_for_read()), cuts); + geometry_set.replace_curve(curve_eval_to_curves(*output_curve)); }); params.set_output("Curve", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc index ed497b6fbe0..e7a8c61290b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc @@ -27,14 +27,16 @@ static void geometry_set_curve_to_mesh(GeometrySet &geometry_set, const GeometrySet &profile_set, const bool fill_caps) { - const CurveEval *curve = geometry_set.get_curve_for_read(); - const CurveEval *profile_curve = profile_set.get_curve_for_read(); + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *geometry_set.get_curves_for_read()); + const Curves *profile_curves = profile_set.get_curves_for_read(); - if (profile_curve == nullptr) { + if (profile_curves == nullptr) { Mesh *mesh = bke::curve_to_wire_mesh(*curve); geometry_set.replace_mesh(mesh); } else { + const std::unique_ptr<CurveEval> profile_curve = curves_to_curve_eval(*profile_curves); Mesh *mesh = bke::curve_to_mesh_sweep(*curve, *profile_curve, fill_caps); geometry_set.replace_mesh(mesh); } @@ -46,10 +48,10 @@ static void node_geo_exec(GeoNodeExecParams params) GeometrySet profile_set = params.extract_input<GeometrySet>("Profile Curve"); const bool fill_caps = params.extract_input<bool>("Fill Caps"); - bool has_curve = false; + bool has_curves = false; curve_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (geometry_set.has_curve()) { - has_curve = true; + if (geometry_set.has_curves()) { + has_curves = true; geometry_set_curve_to_mesh(geometry_set, profile_set, fill_caps); } geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc index 7481f7248a1..1eb18b2f910 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc @@ -321,15 +321,16 @@ static void node_geo_exec(GeoNodeExecParams params) attribute_outputs.rotation_id = StrongAnonymousAttributeID("Rotation"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); return; } - const CurveEval &curve = *geometry_set.get_curve_for_read(); - const Span<SplinePtr> splines = curve.splines(); - curve.assert_valid_point_attributes(); + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *geometry_set.get_curves_for_read()); + const Span<SplinePtr> splines = curve->splines(); + curve->assert_valid_point_attributes(); - const Array<int> offsets = calculate_spline_point_offsets(params, mode, curve, splines); + const Array<int> offsets = calculate_spline_point_offsets(params, mode, *curve, splines); const int total_size = offsets.last(); if (total_size == 0) { geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); @@ -339,7 +340,7 @@ static void node_geo_exec(GeoNodeExecParams params) geometry_set.replace_pointcloud(BKE_pointcloud_new_nomain(total_size)); PointCloudComponent &points = geometry_set.get_component_for_write<PointCloudComponent>(); ResultAttributes point_attributes = create_attributes_for_transfer( - points, curve, attribute_outputs); + points, *curve, attribute_outputs); switch (mode) { case GEO_NODE_CURVE_RESAMPLE_COUNT: @@ -351,7 +352,7 @@ static void node_geo_exec(GeoNodeExecParams params) break; } - copy_spline_domain_attributes(curve, offsets, points); + copy_spline_domain_attributes(*curve, offsets, points); if (!point_attributes.rotations.is_empty()) { curve_create_default_rotation_attribute( diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc index abc5b1649c7..a3dab1b50fe 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -367,15 +367,18 @@ static void trim_spline(SplinePtr &spline, const Spline::LookupResult end) { switch (spline->type()) { - case Spline::Type::Bezier: + case CURVE_TYPE_BEZIER: trim_bezier_spline(*spline, start, end); break; - case Spline::Type::Poly: + case CURVE_TYPE_POLY: trim_poly_spline(*spline, start, end); break; - case Spline::Type::NURBS: + case CURVE_TYPE_NURBS: spline = std::make_unique<PolySpline>(trim_nurbs_spline(*spline, start, end)); break; + case CURVE_TYPE_CATMULL_ROM: + BLI_assert_unreachable(); + spline = {}; } spline->mark_cache_invalid(); } @@ -400,8 +403,8 @@ static void to_single_point_bezier(Spline &spline, const Spline::LookupResult &l const BezierSpline::InsertResult new_point = bezier.calculate_segment_insertion( trim.left_index, trim.right_index, trim.factor); bezier.positions().first() = new_point.position; - bezier.handle_types_left().first() = BezierSpline::HandleType::Free; - bezier.handle_types_right().first() = BezierSpline::HandleType::Free; + bezier.handle_types_left().first() = BEZIER_HANDLE_FREE; + bezier.handle_types_right().first() = BEZIER_HANDLE_FREE; bezier.handle_positions_left().first() = new_point.left_handle; bezier.handle_positions_right().first() = new_point.right_handle; @@ -477,15 +480,18 @@ static PolySpline to_single_point_nurbs(const Spline &spline, const Spline::Look static void to_single_point_spline(SplinePtr &spline, const Spline::LookupResult &lookup) { switch (spline->type()) { - case Spline::Type::Bezier: + case CURVE_TYPE_BEZIER: to_single_point_bezier(*spline, lookup); break; - case Spline::Type::Poly: + case CURVE_TYPE_POLY: to_single_point_poly(*spline, lookup); break; - case Spline::Type::NURBS: + case CURVE_TYPE_NURBS: spline = std::make_unique<PolySpline>(to_single_point_nurbs(*spline, lookup)); break; + case CURVE_TYPE_CATMULL_ROM: + BLI_assert_unreachable(); + spline = {}; } } @@ -494,7 +500,7 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set, Field<float> &start_field, Field<float> &end_field) { - if (!geometry_set.has_curve()) { + if (!geometry_set.has_curves()) { return; } @@ -509,8 +515,8 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set, const blender::VArray<float> &starts = evaluator.get_evaluated<float>(0); const blender::VArray<float> &ends = evaluator.get_evaluated<float>(1); - CurveEval &curve = *geometry_set.get_curve_for_write(); - MutableSpan<SplinePtr> splines = curve.splines(); + std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*geometry_set.get_curves_for_read()); + MutableSpan<SplinePtr> splines = curve->splines(); threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { @@ -559,6 +565,8 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set, } } }); + + geometry_set.replace_curve(curve_eval_to_curves(*curve)); } static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc index 2eeb957c123..3baee8a25bb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -332,9 +332,9 @@ static void spline_copy_builtin_attributes(const Spline &spline, copy_data_based_on_mask(spline.radii(), r_spline.radii(), mask); copy_data_based_on_mask(spline.tilts(), r_spline.tilts(), mask); switch (spline.type()) { - case Spline::Type::Poly: + case CURVE_TYPE_POLY: break; - case Spline::Type::Bezier: { + case CURVE_TYPE_BEZIER: { const BezierSpline &src = static_cast<const BezierSpline &>(spline); BezierSpline &dst = static_cast<BezierSpline &>(r_spline); copy_data_based_on_mask(src.handle_positions_left(), dst.handle_positions_left(), mask); @@ -343,12 +343,16 @@ static void spline_copy_builtin_attributes(const Spline &spline, copy_data_based_on_mask(src.handle_types_right(), dst.handle_types_right(), mask); break; } - case Spline::Type::NURBS: { + case CURVE_TYPE_NURBS: { const NURBSpline &src = static_cast<const NURBSpline &>(spline); NURBSpline &dst = static_cast<NURBSpline &>(r_spline); copy_data_based_on_mask(src.weights(), dst.weights(), mask); break; } + case CURVE_TYPE_CATMULL_ROM: { + BLI_assert_unreachable(); + break; + } } } @@ -471,9 +475,9 @@ static void separate_curve_selection(GeometrySet &geometry_set, selection_evaluator.evaluate(); const VArray_Span<bool> &selection = selection_evaluator.get_evaluated<bool>(0); std::unique_ptr<CurveEval> r_curve = curve_separate( - *src_component.get_for_read(), selection, selection_domain, invert); + *curves_to_curve_eval(*src_component.get_for_read()), selection, selection_domain, invert); if (r_curve) { - geometry_set.replace_curve(r_curve.release()); + geometry_set.replace_curve(curve_eval_to_curves(*r_curve)); } else { geometry_set.replace_curve(nullptr); @@ -1282,7 +1286,7 @@ void separate_geometry(GeometrySet &geometry_set, some_valid_domain = true; } } - if (geometry_set.has_curve()) { + if (geometry_set.has_curves()) { if (ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) { file_ns::separate_curve_selection(geometry_set, selection_field, domain, invert); some_valid_domain = true; diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc index 51932d341bc..bdd4d74fe4b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc @@ -152,6 +152,7 @@ BLI_NOINLINE static void update_elimination_mask_for_close_points( } KDTree_3d *kdtree = build_kdtree(positions); + BLI_SCOPED_DEFER([&]() { BLI_kdtree_3d_free(kdtree); }); for (const int i : positions.index_range()) { if (elimination_mask[i]) { @@ -176,8 +177,6 @@ BLI_NOINLINE static void update_elimination_mask_for_close_points( }, &callback_data); } - - BLI_kdtree_3d_free(kdtree); } BLI_NOINLINE static void update_elimination_mask_based_on_density_factors( diff --git a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc index a526f4f9e65..5a2c32a6c8e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc @@ -272,7 +272,7 @@ static void create_vertex_poly_map(const Mesh &mesh, * boundary vertex, the first and last polygon have a boundary edge connected to the vertex. The * `r_shared_edges` array at index i is set to the index of the shared edge between the i-th and * `(i+1)-th` sorted polygon. Similarly the `r_sorted_corners` array at index i is set to the - * corner in the i-th sorted polygon. + * corner in the i-th sorted polygon. If the polygons couldn't be sorted, `false` is returned. * * How the faces are sorted (see diagrams below): * (For this explanation we'll assume all faces are oriented clockwise) @@ -321,7 +321,7 @@ static void create_vertex_poly_map(const Mesh &mesh, * - Finally if we are in the normal case we also need to add the last "shared edge" to close the * loop. */ -static void sort_vertex_polys(const Mesh &mesh, +static bool sort_vertex_polys(const Mesh &mesh, const int vertex_index, const bool boundary_vertex, const Span<EdgeType> edge_types, @@ -330,7 +330,7 @@ static void sort_vertex_polys(const Mesh &mesh, MutableSpan<int> r_sorted_corners) { if (connected_polygons.size() <= 2 && (!boundary_vertex || connected_polygons.size() == 0)) { - return; + return true; } /* For each polygon store the two corners whose edge contains the vertex. */ @@ -434,8 +434,11 @@ static void sort_vertex_polys(const Mesh &mesh, break; } } - - BLI_assert(j != connected_polygons.size()); + if (j == connected_polygons.size()) { + /* The vertex is not manifold because the polygons around the vertex don't form a loop, and + * hence can't be sorted. */ + return false; + } std::swap(connected_polygons[i + 1], connected_polygons[j]); std::swap(poly_vertex_corners[i + 1], poly_vertex_corners[j]); @@ -445,6 +448,7 @@ static void sort_vertex_polys(const Mesh &mesh, /* Shared edge between first and last polygon. */ r_shared_edges.last() = shared_edge_i; } + return true; } /** @@ -637,19 +641,26 @@ static void calc_dual_mesh(GeometrySet &geometry_set, } MutableSpan<int> loop_indices = vertex_poly_indices[i]; Array<int> sorted_corners(loop_indices.size()); + bool vertex_ok = true; if (vertex_types[i] == VertexType::Normal) { Array<int> shared_edges(loop_indices.size()); - sort_vertex_polys( + vertex_ok = sort_vertex_polys( mesh_in, i, false, edge_types, loop_indices, shared_edges, sorted_corners); - vertex_shared_edges[i] = shared_edges; + vertex_shared_edges[i] = std::move(shared_edges); } else { Array<int> shared_edges(loop_indices.size() - 1); - sort_vertex_polys( + vertex_ok = sort_vertex_polys( mesh_in, i, true, edge_types, loop_indices, shared_edges, sorted_corners); - vertex_shared_edges[i] = shared_edges; + vertex_shared_edges[i] = std::move(shared_edges); + } + if (!vertex_ok) { + /* The sorting failed which means that the vertex is non-manifold and should be ignored + * further on. */ + vertex_types[i] = VertexType::NonManifold; + continue; } - vertex_corners[i] = sorted_corners; + vertex_corners[i] = std::move(sorted_corners); } }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc new file mode 100644 index 00000000000..1ceab18c01b --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc @@ -0,0 +1,1119 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_map.hh" +#include "BLI_noise.hh" +#include "BLI_span.hh" +#include "BLI_task.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_pointcloud_types.h" + +#include "BKE_attribute_math.hh" +#include "BKE_mesh.h" +#include "BKE_pointcloud.h" +#include "BKE_spline.hh" + +#include "node_geometry_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes::node_geo_duplicate_elements_cc { + +NODE_STORAGE_FUNCS(NodeGeometryDuplicateElements); + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Geometry>(N_("Geometry")); + b.add_input<decl::Bool>(N_("Selection")).hide_value().default_value(true).supports_field(); + b.add_input<decl::Int>(N_("Amount")) + .min(0) + .default_value(1) + .supports_field() + .description(N_("The number of duplicates to create for each element")); + + b.add_output<decl::Geometry>(N_("Geometry")) + .description( + N_("The duplicated geometry only. The output does not contain the original geometry")); + b.add_output<decl::Int>(N_("Duplicate Index")) + .field_source() + .description(N_("The indices of the duplicates for each element")); +} + +static void node_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryDuplicateElements *data = MEM_cnew<NodeGeometryDuplicateElements>(__func__); + data->domain = ATTR_DOMAIN_POINT; + node->storage = data; +} + +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); +} + +struct IndexAttributes { + StrongAnonymousAttributeID duplicate_index; +}; + +/* -------------------------------------------------------------------- */ +/** \name Attribute Copy/Creation Functions + * \{ */ + +static void gather_attributes_without_id(const GeometrySet &geometry_set, + const GeometryComponentType component_type, + const Span<std::string> skip_attributes, + const bool include_instances, + Map<AttributeIDRef, AttributeKind> &r_gathered_attributes) +{ + geometry_set.gather_attributes_for_propagation( + {component_type}, component_type, include_instances, r_gathered_attributes); + for (const std::string &attribute : skip_attributes) { + r_gathered_attributes.remove(attribute); + } + r_gathered_attributes.remove("id"); +}; + +static IndexRange range_for_offsets_index(const Span<int> offsets, const int index) +{ + return {offsets[index], offsets[index + 1] - offsets[index]}; +} + +static Array<int> accumulate_counts_to_offsets(const IndexMask selection, + const VArray<int> &counts) +{ + Array<int> offsets(selection.size() + 1); + int dst_points_size = 0; + for (const int i_point : selection.index_range()) { + offsets[i_point] = dst_points_size; + dst_points_size += std::max(counts[selection[i_point]], 0); + } + offsets.last() = dst_points_size; + return offsets; +} + +/* Utility functions for threaded copying of attribute data where possible. */ +template<typename T> +static void threaded_slice_fill(Span<int> offsets, Span<T> src, MutableSpan<T> dst) +{ + BLI_assert(offsets.last() == dst.size()); + threading::parallel_for(IndexRange(offsets.size() - 1), 512, [&](IndexRange range) { + for (const int i : range) { + dst.slice(offsets[i], offsets[i + 1] - offsets[i]).fill(src[i]); + } + }); +} + +template<typename T> +static void threaded_mapped_copy(const Span<int> mapping, const Span<T> src, MutableSpan<T> dst) +{ + threading::parallel_for(mapping.index_range(), 512, [&](IndexRange range) { + for (const int i : range) { + dst[i] = src[mapping[i]]; + } + }); +} + +static void threaded_id_offset_copy(const Span<int> offsets, + const Span<int> src, + MutableSpan<int> dst) +{ + BLI_assert(offsets.last() == dst.size()); + threading::parallel_for(IndexRange(offsets.size() - 1), 512, [&](IndexRange range) { + for (const int i : range) { + dst[offsets[i]] = src[i]; + const int count = offsets[i + 1] - offsets[i]; + for (const int i_duplicate : IndexRange(1, count - 1)) { + dst[offsets[i] + i_duplicate] = noise::hash(src[i], i_duplicate); + } + } + }); +} + +/** Create the copy indices for the duplication domain. */ +static void create_duplicate_index_attribute(GeometryComponent &component, + const AttributeDomain output_domain, + const IndexMask selection, + const IndexAttributes &attributes, + const Span<int> offsets) +{ + OutputAttribute_Typed<int> copy_attribute = component.attribute_try_get_for_output_only<int>( + attributes.duplicate_index.get(), output_domain); + MutableSpan<int> duplicate_indices = copy_attribute.as_span(); + for (const int i : IndexRange(selection.size())) { + const IndexRange range = range_for_offsets_index(offsets, i); + MutableSpan<int> indices = duplicate_indices.slice(range); + for (const int i : indices.index_range()) { + indices[i] = i; + } + } + copy_attribute.save(); +} + +/** + * Copy the stable ids to the first duplicate and create new ids based on a hash of the original id + * and the duplicate number. This function is used for the point domain elements. + */ +static void copy_stable_id_point(const Span<int> offsets, + const GeometryComponent &src_component, + GeometryComponent &dst_component) +{ + ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id"); + if (!src_attribute) { + return; + } + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + "id", ATTR_DOMAIN_POINT, CD_PROP_INT32); + if (!dst_attribute) { + return; + } + + VArray_Span<int> src{src_attribute.varray.typed<int>()}; + MutableSpan<int> dst = dst_attribute.as_span<int>(); + threaded_id_offset_copy(offsets, src, dst); + dst_attribute.save(); +} + +/** + * Copy the stable ids to the first duplicate and create new ids based on a hash of the original id + * and the duplicate number. This function is used for points when duplicating the edge domain. + */ +static void copy_stable_id_edges(const Mesh &mesh, + const IndexMask selection, + const Span<int> edge_offsets, + const GeometryComponent &src_component, + GeometryComponent &dst_component) +{ + ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id"); + if (!src_attribute) { + return; + } + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + "id", ATTR_DOMAIN_POINT, CD_PROP_INT32); + if (!dst_attribute) { + return; + } + + Span<MEdge> edges(mesh.medge, mesh.totedge); + + VArray_Span<int> src{src_attribute.varray.typed<int>()}; + MutableSpan<int> dst = dst_attribute.as_span<int>(); + threading::parallel_for(IndexRange(selection.size()), 1024, [&](IndexRange range) { + for (const int i_edge : range) { + const IndexRange edge_range = range_for_offsets_index(edge_offsets, i_edge); + if (edge_range.size() == 0) { + continue; + } + const MEdge &edge = edges[i_edge]; + const IndexRange vert_range = {edge_range.start() * 2, edge_range.size() * 2}; + + dst[vert_range[0]] = src[edge.v1]; + dst[vert_range[1]] = src[edge.v2]; + for (const int i_duplicate : IndexRange(1, edge_range.size() - 1)) { + dst[vert_range[i_duplicate * 2]] = noise::hash(src[edge.v1], i_duplicate); + dst[vert_range[i_duplicate * 2 + 1]] = noise::hash(src[edge.v2], i_duplicate); + } + } + }); + dst_attribute.save(); +} + +/** + * Copy the stable ids to the first duplicate and create new ids based on a hash of the original id + * and the duplicate number. This function is used for points when duplicating the face domain. + * + * This function could be threaded in the future, but since it is only 1 attribute and the + * `face->edge->vert` mapping would mean creating a 1/1 mapping to allow for it, is it worth it? + */ +static void copy_stable_id_faces(const Mesh &mesh, + const IndexMask selection, + const Span<int> poly_offsets, + const Span<int> vert_mapping, + const GeometryComponent &src_component, + GeometryComponent &dst_component) +{ + ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id"); + if (!src_attribute) { + return; + } + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + "id", ATTR_DOMAIN_POINT, CD_PROP_INT32); + if (!dst_attribute) { + return; + } + + VArray_Span<int> src{src_attribute.varray.typed<int>()}; + MutableSpan<int> dst = dst_attribute.as_span<int>(); + + Span<MPoly> polys(mesh.mpoly, mesh.totpoly); + int loop_index = 0; + for (const int i_poly : selection.index_range()) { + const IndexRange range = range_for_offsets_index(poly_offsets, i_poly); + if (range.size() == 0) { + continue; + } + const MPoly &source = polys[i_poly]; + for ([[maybe_unused]] const int i_duplicate : IndexRange(range.size())) { + for ([[maybe_unused]] const int i_loops : IndexRange(source.totloop)) { + if (i_duplicate == 0) { + dst[loop_index] = src[vert_mapping[loop_index]]; + } + else { + dst[loop_index] = noise::hash(src[vert_mapping[loop_index]], i_duplicate); + } + loop_index++; + } + } + } + + dst_attribute.save(); +} + +/** + * Copy the stable ids to the first duplicate and create new ids based on a hash of the original id + * and the duplicate number. In the spline case, copy the entire spline's points to the + * destination, + * then loop over the remaining ones point by point, hashing their ids to the new ids. + */ +static void copy_stable_id_splines(const CurveEval &curve, + const IndexMask selection, + const Span<int> curve_offsets, + const GeometryComponent &src_component, + GeometryComponent &dst_component) +{ + ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read("id"); + if (!src_attribute) { + return; + } + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + "id", ATTR_DOMAIN_POINT, CD_PROP_INT32); + if (!dst_attribute) { + return; + } + + Array<int> control_point_offsets = curve.control_point_offsets(); + VArray_Span<int> src{src_attribute.varray.typed<int>()}; + MutableSpan<int> dst = dst_attribute.as_span<int>(); + + Array<int> curve_point_offsets(selection.size() + 1); + int dst_point_size = 0; + for (const int i_curve : selection.index_range()) { + const int spline_size = curve.splines()[i_curve]->size(); + const IndexRange curve_range = range_for_offsets_index(curve_offsets, i_curve); + + curve_point_offsets[i_curve] = dst_point_size; + dst_point_size += curve_range.size() * spline_size; + } + curve_point_offsets.last() = dst_point_size; + + threading::parallel_for(IndexRange(curve_point_offsets.size() - 1), 512, [&](IndexRange range) { + for (const int i_curve : range) { + const int spline_size = curve.splines()[i_curve]->size(); + const IndexRange curve_range = range_for_offsets_index(curve_offsets, i_curve); + + dst.slice(curve_point_offsets[i_curve], spline_size) + .copy_from(src.slice(control_point_offsets[i_curve], spline_size)); + for (const int i_duplicate : IndexRange(1, curve_range.size() - 1)) { + for (const int i_point : IndexRange(spline_size)) { + dst[curve_point_offsets[i_curve] + i_duplicate * spline_size + i_point] = noise::hash( + src[control_point_offsets[i_curve] + i_point], i_duplicate); + } + } + } + }); + dst_attribute.save(); +} + +/* The attributes for the point (also instance) duplicated elements are stored sequentially + * (1,1,1,2,2,2,3,3,3,etc) They can be copied by using a simple offset array. For each domain, if + * elements are ordered differently a custom function is called to copy the attributes. + */ + +static void copy_point_attributes_without_id(GeometrySet &geometry_set, + const GeometryComponentType component_type, + const bool include_instances, + const Span<int> offsets, + const GeometryComponent &src_component, + GeometryComponent &dst_component) +{ + Map<AttributeIDRef, AttributeKind> gathered_attributes; + gather_attributes_without_id( + geometry_set, component_type, {}, include_instances, gathered_attributes); + + for (const Map<AttributeIDRef, AttributeKind>::Item entry : gathered_attributes.items()) { + const AttributeIDRef attribute_id = entry.key; + ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id); + if (!src_attribute || src_attribute.domain != ATTR_DOMAIN_POINT) { + continue; + } + AttributeDomain out_domain = src_attribute.domain; + const CustomDataType data_type = bke::cpp_type_to_custom_data_type( + src_attribute.varray.type()); + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + attribute_id, out_domain, data_type); + if (!dst_attribute) { + continue; + } + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + VArray_Span<T> src = src_attribute.varray.typed<T>(); + MutableSpan<T> dst = dst_attribute.as_span<T>(); + threaded_slice_fill<T>(offsets, src, dst); + }); + dst_attribute.save(); + } +} + +/** + * Copies the attributes for spline duplicates. If copying the spline domain, the attributes are + * copied with an offset fill, otherwise a mapping is used. + */ +static void copy_spline_attributes_without_id(const GeometrySet &geometry_set, + const Span<int> point_mapping, + const Span<int> offsets, + const Span<std::string> attributes_to_ignore, + const GeometryComponent &src_component, + GeometryComponent &dst_component) +{ + Map<AttributeIDRef, AttributeKind> gathered_attributes; + gather_attributes_without_id( + geometry_set, GEO_COMPONENT_TYPE_CURVE, attributes_to_ignore, false, gathered_attributes); + + for (const Map<AttributeIDRef, AttributeKind>::Item entry : gathered_attributes.items()) { + + const AttributeIDRef attribute_id = entry.key; + ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id); + if (!src_attribute) { + continue; + } + + AttributeDomain out_domain = src_attribute.domain; + const CustomDataType data_type = bke::cpp_type_to_custom_data_type( + src_attribute.varray.type()); + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + attribute_id, out_domain, data_type); + if (!dst_attribute) { + continue; + } + + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + VArray_Span<T> src{src_attribute.varray.typed<T>()}; + MutableSpan<T> dst = dst_attribute.as_span<T>(); + + switch (out_domain) { + case ATTR_DOMAIN_CURVE: + threaded_slice_fill<T>(offsets, src, dst); + break; + case ATTR_DOMAIN_POINT: + threaded_mapped_copy<T>(point_mapping, src, dst); + break; + default: + break; + } + }); + dst_attribute.save(); + } +} + +/** + * Copies the attributes for edge duplicates. If copying the edge domain, the attributes are + * copied with an offset fill, for point domain a mapping is used. + */ +static void copy_edge_attributes_without_id(GeometrySet &geometry_set, + const Span<int> point_mapping, + const Span<int> offsets, + const GeometryComponent &src_component, + GeometryComponent &dst_component) +{ + Map<AttributeIDRef, AttributeKind> gathered_attributes; + gather_attributes_without_id( + geometry_set, GEO_COMPONENT_TYPE_MESH, {}, false, gathered_attributes); + + for (const Map<AttributeIDRef, AttributeKind>::Item entry : gathered_attributes.items()) { + const AttributeIDRef attribute_id = entry.key; + ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id); + if (!src_attribute) { + continue; + } + + const AttributeDomain out_domain = src_attribute.domain; + const CustomDataType data_type = bke::cpp_type_to_custom_data_type( + src_attribute.varray.type()); + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + attribute_id, out_domain, data_type); + if (!dst_attribute) { + continue; + } + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + VArray_Span<T> src{src_attribute.varray.typed<T>()}; + MutableSpan<T> dst = dst_attribute.as_span<T>(); + + switch (out_domain) { + case ATTR_DOMAIN_EDGE: + threaded_slice_fill<T>(offsets, src, dst); + break; + case ATTR_DOMAIN_POINT: + threaded_mapped_copy<T>(point_mapping, src, dst); + break; + default: + break; + } + }); + dst_attribute.save(); + } +} + +/** + * Copies the attributes for face duplicates. If copying the face domain, the attributes are + * copied with an offset fill, otherwise a mapping is used. + */ +static void copy_face_attributes_without_id(GeometrySet &geometry_set, + const Span<int> edge_mapping, + const Span<int> vert_mapping, + const Span<int> loop_mapping, + const Span<int> offsets, + const GeometryComponent &src_component, + GeometryComponent &dst_component) +{ + Map<AttributeIDRef, AttributeKind> gathered_attributes; + gather_attributes_without_id( + geometry_set, GEO_COMPONENT_TYPE_MESH, {}, false, gathered_attributes); + + for (const Map<AttributeIDRef, AttributeKind>::Item entry : gathered_attributes.items()) { + const AttributeIDRef attribute_id = entry.key; + ReadAttributeLookup src_attribute = src_component.attribute_try_get_for_read(attribute_id); + if (!src_attribute) { + continue; + } + + AttributeDomain out_domain = src_attribute.domain; + const CustomDataType data_type = bke::cpp_type_to_custom_data_type( + src_attribute.varray.type()); + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + attribute_id, out_domain, data_type); + if (!dst_attribute) { + continue; + } + + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + VArray_Span<T> src{src_attribute.varray.typed<T>()}; + MutableSpan<T> dst = dst_attribute.as_span<T>(); + + switch (out_domain) { + case ATTR_DOMAIN_FACE: + threaded_slice_fill<T>(offsets, src, dst); + break; + case ATTR_DOMAIN_EDGE: + threaded_mapped_copy<T>(edge_mapping, src, dst); + break; + case ATTR_DOMAIN_POINT: + threaded_mapped_copy<T>(vert_mapping, src, dst); + break; + case ATTR_DOMAIN_CORNER: + threaded_mapped_copy<T>(loop_mapping, src, dst); + break; + default: + break; + } + }); + dst_attribute.save(); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Duplication Functions + * \{ */ + +static void duplicate_splines(GeometrySet &geometry_set, + const Field<int> &count_field, + const Field<bool> &selection_field, + IndexAttributes &attributes) +{ + if (!geometry_set.has_curves()) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + } + geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES}); + + const GeometryComponent &src_component = *geometry_set.get_component_for_read( + GEO_COMPONENT_TYPE_CURVE); + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *geometry_set.get_curves_for_read()); + const int domain_size = src_component.attribute_domain_size(ATTR_DOMAIN_CURVE); + GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE}; + FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(count_field); + evaluator.set_selection(selection_field); + evaluator.evaluate(); + const VArray<int> counts = evaluator.get_evaluated<int>(0); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + + Array<int> curve_offsets(selection.size() + 1); + + int dst_splines_size = 0; + int dst_points_size = 0; + for (const int i_spline : selection.index_range()) { + int count = std::max(counts[selection[i_spline]], 0); + curve_offsets[i_spline] = dst_splines_size; + dst_splines_size += count; + dst_points_size += count * curve->splines()[selection[i_spline]]->size(); + } + curve_offsets.last() = dst_splines_size; + + Array<int> control_point_offsets = curve->control_point_offsets(); + Array<int> point_mapping(dst_points_size); + + std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>(); + int point_index = 0; + for (const int i_spline : selection.index_range()) { + const IndexRange spline_range = range_for_offsets_index(curve_offsets, i_spline); + for ([[maybe_unused]] const int i_duplicate : IndexRange(spline_range.size())) { + SplinePtr spline = curve->splines()[selection[i_spline]]->copy(); + for (const int i_point : IndexRange(curve->splines()[selection[i_spline]]->size())) { + point_mapping[point_index++] = control_point_offsets[selection[i_spline]] + i_point; + } + new_curve->add_spline(std::move(spline)); + } + } + new_curve->attributes.reallocate(new_curve->splines().size()); + + CurveComponent dst_component; + dst_component.replace(curve_eval_to_curves(*new_curve), GeometryOwnershipType::Editable); + + Vector<std::string> skip( + {"position", "radius", "resolution", "cyclic", "tilt", "handle_left", "handle_right"}); + + copy_spline_attributes_without_id( + geometry_set, point_mapping, curve_offsets, skip, src_component, dst_component); + + copy_stable_id_splines(*curve, selection, curve_offsets, src_component, dst_component); + + if (attributes.duplicate_index) { + create_duplicate_index_attribute( + dst_component, ATTR_DOMAIN_CURVE, selection, attributes, curve_offsets); + } + + geometry_set.replace_curve(dst_component.get_for_write()); +} + +static void duplicate_faces(GeometrySet &geometry_set, + const Field<int> &count_field, + const Field<bool> &selection_field, + IndexAttributes &attributes) +{ + if (!geometry_set.has_mesh()) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + } + geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); + + GeometryComponent &component = geometry_set.get_component_for_write(GEO_COMPONENT_TYPE_MESH); + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); + + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE}; + FieldEvaluator evaluator(field_context, domain_size); + + evaluator.add(count_field); + evaluator.set_selection(selection_field); + evaluator.evaluate(); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + const VArray<int> counts = evaluator.get_evaluated<int>(0); + + MeshComponent &mesh_component = static_cast<MeshComponent &>(component); + const Mesh &mesh = *mesh_component.get_for_read(); + Span<MVert> verts(mesh.mvert, mesh.totvert); + Span<MEdge> edges(mesh.medge, mesh.totedge); + Span<MPoly> polys(mesh.mpoly, mesh.totpoly); + Span<MLoop> loops(mesh.mloop, mesh.totloop); + + int total_polys = 0; + int total_loops = 0; + Array<int> offsets(selection.size() + 1); + for (const int i_selection : selection.index_range()) { + const int count = std::max(counts[selection[i_selection]], 0); + offsets[i_selection] = total_polys; + total_polys += count; + total_loops += count * polys[selection[i_selection]].totloop; + } + offsets[selection.size()] = total_polys; + + Array<int> vert_mapping(total_loops); + Array<int> edge_mapping(total_loops); + Array<int> loop_mapping(total_loops); + + Mesh *new_mesh = BKE_mesh_new_nomain(total_loops, total_loops, 0, total_loops, total_polys); + + MutableSpan<MVert> new_verts(new_mesh->mvert, new_mesh->totvert); + MutableSpan<MEdge> new_edges(new_mesh->medge, new_mesh->totedge); + MutableSpan<MLoop> new_loops(new_mesh->mloop, new_mesh->totloop); + MutableSpan<MPoly> new_poly(new_mesh->mpoly, new_mesh->totpoly); + + int poly_index = 0; + int loop_index = 0; + for (const int i_selection : selection.index_range()) { + const IndexRange poly_range = range_for_offsets_index(offsets, i_selection); + + const MPoly &source = polys[selection[i_selection]]; + for ([[maybe_unused]] const int i_duplicate : IndexRange(poly_range.size())) { + new_poly[poly_index] = source; + new_poly[poly_index].loopstart = loop_index; + for (const int i_loops : IndexRange(source.totloop)) { + const MLoop ¤t_loop = loops[source.loopstart + i_loops]; + loop_mapping[loop_index] = source.loopstart + i_loops; + new_verts[loop_index] = verts[current_loop.v]; + vert_mapping[loop_index] = current_loop.v; + new_edges[loop_index] = edges[current_loop.e]; + edge_mapping[loop_index] = current_loop.e; + new_edges[loop_index].v1 = loop_index; + if (i_loops + 1 != source.totloop) { + new_edges[loop_index].v2 = loop_index + 1; + } + else { + new_edges[loop_index].v2 = new_poly[poly_index].loopstart; + } + new_loops[loop_index].v = loop_index; + new_loops[loop_index].e = loop_index; + loop_index++; + } + poly_index++; + } + } + MeshComponent dst_component; + dst_component.replace(new_mesh, GeometryOwnershipType::Editable); + + copy_face_attributes_without_id(geometry_set, + edge_mapping, + vert_mapping, + loop_mapping, + offsets, + mesh_component, + dst_component); + + copy_stable_id_faces(mesh, selection, offsets, vert_mapping, mesh_component, dst_component); + mesh_component.replace(dst_component.get_for_write()); + + if (attributes.duplicate_index) { + create_duplicate_index_attribute( + dst_component, ATTR_DOMAIN_FACE, selection, attributes, offsets); + } +} + +static void duplicate_edges(GeometrySet &geometry_set, + const Field<int> &count_field, + const Field<bool> &selection_field, + IndexAttributes &attributes) +{ + if (!geometry_set.has_mesh()) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + }; + const GeometryComponent &src_component = *geometry_set.get_component_for_read( + GEO_COMPONENT_TYPE_MESH); + const int domain_size = src_component.attribute_domain_size(ATTR_DOMAIN_EDGE); + + GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_EDGE}; + FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(count_field); + evaluator.set_selection(selection_field); + evaluator.evaluate(); + const VArray<int> counts = evaluator.get_evaluated<int>(0); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + + Array<int> edge_offsets = accumulate_counts_to_offsets(selection, counts); + + const Mesh *mesh = geometry_set.get_mesh_for_read(); + Span<MVert> verts(mesh->mvert, mesh->totvert); + Span<MEdge> edges(mesh->medge, mesh->totedge); + + Mesh *new_mesh = BKE_mesh_new_nomain(edge_offsets.last() * 2, edge_offsets.last(), 0, 0, 0); + MutableSpan<MVert> new_verts(new_mesh->mvert, new_mesh->totvert); + MutableSpan<MEdge> new_edges(new_mesh->medge, new_mesh->totedge); + + Array<int> vert_orig_indices(edge_offsets.last() * 2); + threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) { + for (const int i_edge : range) { + const MEdge &edge = edges[i_edge]; + const IndexRange edge_range = range_for_offsets_index(edge_offsets, i_edge); + const IndexRange vert_range(edge_range.start() * 2, edge_range.size() * 2); + + for (const int i_duplicate : IndexRange(edge_range.size())) { + vert_orig_indices[vert_range[i_duplicate * 2]] = edge.v1; + vert_orig_indices[vert_range[i_duplicate * 2 + 1]] = edge.v2; + } + } + }); + + threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) { + for (const int i_edge : range) { + const IndexRange edge_range = range_for_offsets_index(edge_offsets, i_edge); + const IndexRange vert_range(edge_range.start() * 2, edge_range.size() * 2); + for (const int i_duplicate : IndexRange(edge_range.size())) { + MEdge &new_edge = new_edges[edge_range[i_duplicate]]; + new_edge.v1 = vert_range[i_duplicate * 2]; + new_edge.v2 = vert_range[i_duplicate * 2] + 1; + } + } + }); + + MeshComponent dst_component; + dst_component.replace(new_mesh, GeometryOwnershipType::Editable); + + copy_edge_attributes_without_id( + geometry_set, vert_orig_indices, edge_offsets, src_component, dst_component); + + copy_stable_id_edges(*mesh, selection, edge_offsets, src_component, dst_component); + + if (attributes.duplicate_index) { + create_duplicate_index_attribute( + dst_component, ATTR_DOMAIN_EDGE, selection, attributes, edge_offsets); + } + + MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); + mesh_component.replace(dst_component.get_for_write()); +} + +static void duplicate_points_curve(const GeometryComponentType component_type, + const Field<int> &count_field, + const Field<bool> &selection_field, + GeometrySet &geometry_set, + IndexAttributes &attributes) +{ + const GeometryComponent &src_component = *geometry_set.get_component_for_read(component_type); + const int domain_size = src_component.attribute_domain_size(ATTR_DOMAIN_POINT); + if (domain_size == 0) { + return; + } + + GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_POINT}; + FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(count_field); + evaluator.set_selection(selection_field); + evaluator.evaluate(); + const VArray<int> counts = evaluator.get_evaluated<int>(0); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + + Array<int> offsets = accumulate_counts_to_offsets(selection, counts); + + CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>(); + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *geometry_set.get_curves_for_read()); + Array<int> control_point_offsets = curve->control_point_offsets(); + std::unique_ptr<CurveEval> new_curve = std::make_unique<CurveEval>(); + + Array<int> parent(domain_size); + int spline = 0; + for (const int i_spline : IndexRange(domain_size)) { + if (i_spline == control_point_offsets[spline + 1]) { + spline++; + } + parent[i_spline] = spline; + } + + for (const int i_point : selection) { + const IndexRange point_range = range_for_offsets_index(offsets, i_point); + for ([[maybe_unused]] const int i_duplicate : IndexRange(point_range.size())) { + const SplinePtr &parent_spline = curve->splines()[parent[i_point]]; + switch (parent_spline->type()) { + case CurveType::CURVE_TYPE_BEZIER: { + std::unique_ptr<BezierSpline> spline = std::make_unique<BezierSpline>(); + spline->resize(1); + spline->set_resolution(2); + new_curve->add_spline(std::move(spline)); + break; + } + case CurveType::CURVE_TYPE_NURBS: { + std::unique_ptr<NURBSpline> spline = std::make_unique<NURBSpline>(); + spline->resize(1); + spline->set_resolution(2); + new_curve->add_spline(std::move(spline)); + break; + } + case CurveType::CURVE_TYPE_POLY: { + std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); + spline->resize(1); + new_curve->add_spline(std::move(spline)); + break; + } + case CurveType::CURVE_TYPE_CATMULL_ROM: { + /* Catmull Rom curves are not supported yet. */ + break; + } + } + } + } + new_curve->attributes.reallocate(new_curve->splines().size()); + CurveComponent dst_component; + dst_component.replace(curve_eval_to_curves(*new_curve), GeometryOwnershipType::Editable); + + copy_point_attributes_without_id( + geometry_set, GEO_COMPONENT_TYPE_CURVE, false, offsets, src_component, dst_component); + + copy_stable_id_point(offsets, src_component, dst_component); + + if (attributes.duplicate_index) { + create_duplicate_index_attribute( + dst_component, ATTR_DOMAIN_POINT, selection, attributes, offsets.as_span()); + } + + curve_component.replace(dst_component.get_for_write()); +} + +static void duplicate_points_mesh(const GeometryComponentType component_type, + const Field<int> &count_field, + const Field<bool> &selection_field, + GeometrySet &geometry_set, + IndexAttributes &attributes) +{ + const GeometryComponent &src_component = *geometry_set.get_component_for_read(component_type); + const int domain_size = src_component.attribute_domain_size(ATTR_DOMAIN_POINT); + + GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_POINT}; + FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(count_field); + evaluator.set_selection(selection_field); + evaluator.evaluate(); + const VArray<int> counts = evaluator.get_evaluated<int>(0); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + + Array<int> offsets = accumulate_counts_to_offsets(selection, counts); + + const Mesh *mesh = geometry_set.get_mesh_for_read(); + Span<MVert> src_verts(mesh->mvert, mesh->totvert); + + Mesh *new_mesh = BKE_mesh_new_nomain(offsets.last(), 0, 0, 0, 0); + MutableSpan<MVert> dst_verts(new_mesh->mvert, new_mesh->totvert); + + threaded_slice_fill<MVert>(offsets.as_span(), src_verts, dst_verts); + + MeshComponent dst_component; + dst_component.replace(new_mesh, GeometryOwnershipType::Editable); + copy_point_attributes_without_id( + geometry_set, GEO_COMPONENT_TYPE_MESH, false, offsets, src_component, dst_component); + + copy_stable_id_point(offsets, src_component, dst_component); + + if (attributes.duplicate_index) { + create_duplicate_index_attribute( + dst_component, ATTR_DOMAIN_POINT, selection, attributes, offsets.as_span()); + } + + MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); + mesh_component.replace(dst_component.get_for_write()); +} + +static void duplicate_points_pointcloud(const GeometryComponentType component_type, + const Field<int> &count_field, + const Field<bool> &selection_field, + GeometrySet &geometry_set, + IndexAttributes &attributes) +{ + const GeometryComponent &src_component = *geometry_set.get_component_for_read(component_type); + const int domain_size = src_component.attribute_domain_size(ATTR_DOMAIN_POINT); + + GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_POINT}; + FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(count_field); + evaluator.set_selection(selection_field); + evaluator.evaluate(); + const VArray<int> counts = evaluator.get_evaluated<int>(0); + const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + + Array<int> offsets = accumulate_counts_to_offsets(selection, counts); + + PointCloud *pointcloud = BKE_pointcloud_new_nomain(offsets.last()); + PointCloudComponent dst_component; + dst_component.replace(pointcloud, GeometryOwnershipType::Editable); + + copy_point_attributes_without_id( + geometry_set, GEO_COMPONENT_TYPE_POINT_CLOUD, false, offsets, src_component, dst_component); + + copy_stable_id_point(offsets, src_component, dst_component); + + if (attributes.duplicate_index) { + create_duplicate_index_attribute( + dst_component, ATTR_DOMAIN_POINT, selection, attributes, offsets); + } + geometry_set.replace_pointcloud(pointcloud); +} + +static void duplicate_points(GeometrySet &geometry_set, + const Field<int> &count_field, + const Field<bool> &selection_field, + IndexAttributes &attributes) +{ + if (!geometry_set.has_mesh() && !geometry_set.has_curves() && !geometry_set.has_pointcloud()) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + } + + Vector<GeometryComponentType> component_types = geometry_set.gather_component_types(true, true); + Vector<GeometryComponentType> types_to_keep; + for (const GeometryComponentType component_type : component_types) { + switch (component_type) { + case GEO_COMPONENT_TYPE_POINT_CLOUD: + types_to_keep.append(component_type); + duplicate_points_pointcloud( + component_type, count_field, selection_field, geometry_set, attributes); + break; + case GEO_COMPONENT_TYPE_MESH: + types_to_keep.append(component_type); + duplicate_points_mesh( + component_type, count_field, selection_field, geometry_set, attributes); + break; + case GEO_COMPONENT_TYPE_CURVE: + types_to_keep.append(component_type); + duplicate_points_curve( + component_type, count_field, selection_field, geometry_set, attributes); + break; + default: + break; + } + } + types_to_keep.append(GEO_COMPONENT_TYPE_INSTANCES); + geometry_set.keep_only(types_to_keep); +} + +static void duplicate_instances(GeometrySet &geometry_set, + const Field<int> &count_field, + const Field<bool> &selection_field, + IndexAttributes &attributes) +{ + if (!geometry_set.has_instances()) { + geometry_set.clear(); + return; + } + + const InstancesComponent &src_instances = + *geometry_set.get_component_for_read<InstancesComponent>(); + + const int domain_size = src_instances.attribute_domain_size(ATTR_DOMAIN_INSTANCE); + GeometryComponentFieldContext field_context{src_instances, ATTR_DOMAIN_INSTANCE}; + FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(count_field); + evaluator.set_selection(selection_field); + evaluator.evaluate(); + IndexMask selection = evaluator.get_evaluated_selection_as_mask(); + const VArray<int> counts = evaluator.get_evaluated<int>(0); + + Array<int> offsets = accumulate_counts_to_offsets(selection, counts); + + if (offsets.last() == 0) { + geometry_set.clear(); + return; + } + + GeometrySet instances_geometry; + InstancesComponent &dst_instances = + instances_geometry.get_component_for_write<InstancesComponent>(); + dst_instances.resize(offsets.last()); + for (const int i_selection : selection.index_range()) { + const int count = offsets[i_selection + 1] - offsets[i_selection]; + if (count == 0) { + continue; + } + const int old_handle = src_instances.instance_reference_handles()[i_selection]; + const InstanceReference reference = src_instances.references()[old_handle]; + const int new_handle = dst_instances.add_reference(reference); + const float4x4 transform = src_instances.instance_transforms()[i_selection]; + dst_instances.instance_transforms().slice(offsets[i_selection], count).fill(transform); + dst_instances.instance_reference_handles().slice(offsets[i_selection], count).fill(new_handle); + } + + copy_point_attributes_without_id( + geometry_set, GEO_COMPONENT_TYPE_INSTANCES, true, offsets, src_instances, dst_instances); + + if (attributes.duplicate_index) { + create_duplicate_index_attribute( + dst_instances, ATTR_DOMAIN_INSTANCE, selection, attributes, offsets); + } + + geometry_set.remove(GEO_COMPONENT_TYPE_INSTANCES); + geometry_set.add(dst_instances); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + + const NodeGeometryDuplicateElements &storage = node_storage(params.node()); + const AttributeDomain duplicate_domain = AttributeDomain(storage.domain); + + Field<int> count_field = params.extract_input<Field<int>>("Amount"); + Field<bool> selection_field = params.extract_input<Field<bool>>("Selection"); + IndexAttributes attributes; + if (params.output_is_required("Duplicate Index")) { + attributes.duplicate_index = StrongAnonymousAttributeID("duplicate_index"); + } + + if (duplicate_domain == ATTR_DOMAIN_INSTANCE) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + duplicate_instances(geometry_set, count_field, selection_field, attributes); + } + else { + if (geometry_set.is_empty()) { + params.set_default_remaining_outputs(); + return; + } + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + switch (duplicate_domain) { + case ATTR_DOMAIN_CURVE: + duplicate_splines(geometry_set, count_field, selection_field, attributes); + break; + case ATTR_DOMAIN_FACE: + duplicate_faces(geometry_set, count_field, selection_field, attributes); + break; + case ATTR_DOMAIN_EDGE: + duplicate_edges(geometry_set, count_field, selection_field, attributes); + break; + case ATTR_DOMAIN_POINT: + duplicate_points(geometry_set, count_field, selection_field, attributes); + break; + default: + BLI_assert_unreachable(); + break; + } + }); + } + + if (geometry_set.is_empty()) { + params.set_default_remaining_outputs(); + return; + } + + if (attributes.duplicate_index) { + params.set_output( + "Duplicate Index", + AnonymousAttributeFieldInput::Create<int>(std::move(attributes.duplicate_index), + params.attribute_producer_name())); + } + params.set_output("Geometry", geometry_set); +} + +} // namespace blender::nodes::node_geo_duplicate_elements_cc + +void register_node_type_geo_duplicate_elements() +{ + namespace file_ns = blender::nodes::node_geo_duplicate_elements_cc; + static bNodeType ntype; + geo_node_type_base( + &ntype, GEO_NODE_DUPLICATE_ELEMENTS, "Duplicate Elements", NODE_CLASS_GEOMETRY); + + node_type_storage(&ntype, + "NodeGeometryDuplicateElements", + node_free_standard_storage, + node_copy_standard_storage); + + node_type_init(&ntype, file_ns::node_init); + ntype.draw_buttons = file_ns::node_layout; + ntype.geometry_node_execute = file_ns::node_geo_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} + +/** \} */ diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc index 4e053dbc1a3..c03a340a0c8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc @@ -25,6 +25,9 @@ static Mesh *mesh_edge_split(const Mesh &mesh, const IndexMask selection) BMesh *bm = BM_mesh_create(&allocsize, &bmesh_create_params); BMeshFromMeshParams bmesh_from_mesh_params{}; + bmesh_from_mesh_params.cd_mask_extra.vmask = CD_MASK_ORIGINDEX; + bmesh_from_mesh_params.cd_mask_extra.emask = CD_MASK_ORIGINDEX; + bmesh_from_mesh_params.cd_mask_extra.pmask = CD_MASK_ORIGINDEX; BM_mesh_bm_from_me(bm, &mesh, &bmesh_from_mesh_params); BM_mesh_elem_table_ensure(bm, BM_EDGE); diff --git a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc index 5aeeb72961d..82584f6f413 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc @@ -302,7 +302,6 @@ static void extrude_mesh_vertices(MeshComponent &component, } BKE_mesh_runtime_clear_cache(&mesh); - BKE_mesh_normals_tag_dirty(&mesh); } static Array<Vector<int, 2>> mesh_calculate_polys_of_edge(const Mesh &mesh) @@ -626,7 +625,6 @@ static void extrude_mesh_edges(MeshComponent &component, } BKE_mesh_runtime_clear_cache(&mesh); - BKE_mesh_normals_tag_dirty(&mesh); } /** @@ -995,7 +993,6 @@ static void extrude_mesh_face_regions(MeshComponent &component, } BKE_mesh_runtime_clear_cache(&mesh); - BKE_mesh_normals_tag_dirty(&mesh); } /* Get the range into an array of extruded corners, edges, or vertices for a particular polygon. */ @@ -1263,7 +1260,6 @@ static void extrude_individual_mesh_faces(MeshComponent &component, } BKE_mesh_runtime_clear_cache(&mesh); - BKE_mesh_normals_tag_dirty(&mesh); } static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc new file mode 100644 index 00000000000..62af0476057 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_mesh.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_input_mesh_face_is_planar_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input<decl::Float>("Threshold") + .field_source() + .default_value(0.01f) + .subtype(PROP_DISTANCE) + .supports_field() + .description(N_("The distance a point can be from the surface before the face is no longer " + "considered planar")) + .min(0.0f); + b.add_output<decl::Bool>("Planar").field_source(); +} + +class PlanarFieldInput final : public GeometryFieldInput { + private: + Field<float> threshold_; + + public: + PlanarFieldInput(Field<float> threshold) + : GeometryFieldInput(CPPType::get<bool>(), "Planar"), threshold_(threshold) + { + category_ = Category::Generated; + } + + GVArray get_varray_for_context(const GeometryComponent &component, + const AttributeDomain domain, + [[maybe_unused]] IndexMask mask) const final + { + if (component.type() != GEO_COMPONENT_TYPE_MESH) { + return {}; + } + + const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); + const Mesh *mesh = mesh_component.get_for_read(); + if (mesh == nullptr) { + return {}; + } + + GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_FACE}; + fn::FieldEvaluator evaluator{context, mesh->totpoly}; + evaluator.add(threshold_); + evaluator.evaluate(); + const VArray<float> &thresholds = evaluator.get_evaluated<float>(0); + + Span<float3> poly_normals{(float3 *)BKE_mesh_poly_normals_ensure(mesh), mesh->totpoly}; + + auto planar_fn = [mesh, thresholds, poly_normals](const int i_poly) -> bool { + if (mesh->mpoly[i_poly].totloop <= 3) { + return true; + } + const int loopstart = mesh->mpoly[i_poly].loopstart; + const int loops = mesh->mpoly[i_poly].totloop; + Span<MLoop> poly_loops(&mesh->mloop[loopstart], loops); + float3 reference_normal = poly_normals[i_poly]; + + float min = FLT_MAX; + float max = FLT_MIN; + + for (const int i_loop : poly_loops.index_range()) { + const float3 vert = mesh->mvert[poly_loops[i_loop].v].co; + float dot = math::dot(reference_normal, vert); + if (dot > max) { + max = dot; + } + if (dot < min) { + min = dot; + } + } + return max - min < thresholds[i_poly] / 2.0f; + }; + + return component.attribute_try_adapt_domain<bool>( + VArray<bool>::ForFunc(mesh->totpoly, planar_fn), ATTR_DOMAIN_FACE, domain); + } + + uint64_t hash() const override + { + /* Some random constant hash. */ + return 2356235652; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast<const PlanarFieldInput *>(&other) != nullptr; + } +}; + +static void geo_node_exec(GeoNodeExecParams params) +{ + Field<float> threshold = params.extract_input<Field<float>>("Threshold"); + Field<bool> planar_field{std::make_shared<PlanarFieldInput>(threshold)}; + params.set_output("Planar", std::move(planar_field)); +} + +} // namespace blender::nodes::node_geo_input_mesh_face_is_planar_cc + +void register_node_type_geo_input_mesh_face_is_planar() +{ + namespace file_ns = blender::nodes::node_geo_input_mesh_face_is_planar_cc; + + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_INPUT_MESH_FACE_IS_PLANAR, "Face is Planar", NODE_CLASS_INPUT); + ntype.geometry_node_execute = file_ns::geo_node_exec; + ntype.declare = file_ns::node_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc index 4537721d173..f952e15fbbe 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc @@ -19,10 +19,10 @@ static void node_declare(NodeDeclarationBuilder &b) static VArray<float> construct_spline_length_gvarray(const CurveComponent &component, const AttributeDomain domain) { - const CurveEval *curve = component.get_for_read(); - if (curve == nullptr) { + if (!component.has_curves()) { return {}; } + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_read()); Span<SplinePtr> splines = curve->splines(); auto length_fn = [splines](int i) { return splines[i]->length(); }; @@ -76,10 +76,10 @@ class SplineLengthFieldInput final : public GeometryFieldInput { static VArray<int> construct_spline_count_gvarray(const CurveComponent &component, const AttributeDomain domain) { - const CurveEval *curve = component.get_for_read(); - if (curve == nullptr) { + if (!component.has_curves()) { return {}; } + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_read()); Span<SplinePtr> splines = curve->splines(); auto count_fn = [splines](int i) { return splines[i]->size(); }; diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc index 9ae99b4d83e..435dd969c03 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc @@ -52,18 +52,22 @@ static Array<float3> curve_tangent_point_domain(const CurveEval &curve) const Spline &spline = *splines[i]; MutableSpan spline_tangents{tangents.as_mutable_span().slice(offsets[i], spline.size())}; switch (splines[i]->type()) { - case Spline::Type::Bezier: { + case CURVE_TYPE_BEZIER: { calculate_bezier_tangents(static_cast<const BezierSpline &>(spline), spline_tangents); break; } - case Spline::Type::Poly: { + case CURVE_TYPE_POLY: { calculate_poly_tangents(static_cast<const PolySpline &>(spline), spline_tangents); break; } - case Spline::Type::NURBS: { + case CURVE_TYPE_NURBS: { calculate_nurbs_tangents(static_cast<const NURBSpline &>(spline), spline_tangents); break; } + case CURVE_TYPE_CATMULL_ROM: { + BLI_assert_unreachable(); + break; + } } } }); @@ -73,21 +77,12 @@ static Array<float3> curve_tangent_point_domain(const CurveEval &curve) static VArray<float3> construct_curve_tangent_gvarray(const CurveComponent &component, const AttributeDomain domain) { - const CurveEval *curve = component.get_for_read(); - if (curve == nullptr) { - return nullptr; + if (!component.has_curves()) { + return {}; } + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_read()); if (domain == ATTR_DOMAIN_POINT) { - const Span<SplinePtr> splines = curve->splines(); - - /* Use a reference to evaluated tangents if possible to avoid an allocation and a copy. - * This is only possible when there is only one poly spline. */ - if (splines.size() == 1 && splines.first()->type() == Spline::Type::Poly) { - const PolySpline &spline = static_cast<PolySpline &>(*splines.first()); - return VArray<float3>::ForSpan(spline.evaluated_tangents()); - } - Array<float3> tangents = curve_tangent_point_domain(*curve); return VArray<float3>::ForContainer(std::move(tangents)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc index df6d10991fb..61f719ade4e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc @@ -195,35 +195,24 @@ static void node_geo_exec(GeoNodeExecParams params) geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>(); + const Array<GeometryComponentType> types{ + GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}; + Map<AttributeIDRef, AttributeKind> attributes_to_propagate; geometry_set.gather_attributes_for_propagation( - {GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}, - GEO_COMPONENT_TYPE_INSTANCES, - false, - attributes_to_propagate); + types, GEO_COMPONENT_TYPE_INSTANCES, false, attributes_to_propagate); attributes_to_propagate.remove("position"); - if (geometry_set.has<MeshComponent>()) { - add_instances_from_component(instances, - *geometry_set.get_component_for_read<MeshComponent>(), - instance, - params, - attributes_to_propagate); - } - if (geometry_set.has<PointCloudComponent>()) { - add_instances_from_component(instances, - *geometry_set.get_component_for_read<PointCloudComponent>(), - instance, - params, - attributes_to_propagate); - } - if (geometry_set.has<CurveComponent>()) { - add_instances_from_component(instances, - *geometry_set.get_component_for_read<CurveComponent>(), - instance, - params, - attributes_to_propagate); + for (const GeometryComponentType type : types) { + if (geometry_set.has(type)) { + add_instances_from_component(instances, + *geometry_set.get_component_for_read(type), + instance, + params, + attributes_to_propagate); + } } + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc index 3284378a2cb..91cde52f9eb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc @@ -35,7 +35,7 @@ static void node_geo_exec(GeoNodeExecParams params) } std::unique_ptr<CurveEval> curve = geometry::mesh_to_curve_convert(component, selection); - geometry_set.replace_curve(curve.release()); + geometry_set.replace_curve(curve_eval_to_curves(*curve)); geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES}); }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc index 1731ba64b97..c99b51ffd4c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc @@ -198,17 +198,12 @@ static void initialize_volume_component_from_points(GeoNodeExecParams ¶ms, Vector<float3> positions; Vector<float> radii; - if (r_geometry_set.has<MeshComponent>()) { - gather_point_data_from_component( - params, *r_geometry_set.get_component_for_read<MeshComponent>(), positions, radii); - } - if (r_geometry_set.has<PointCloudComponent>()) { - gather_point_data_from_component( - params, *r_geometry_set.get_component_for_read<PointCloudComponent>(), positions, radii); - } - if (r_geometry_set.has<CurveComponent>()) { - gather_point_data_from_component( - params, *r_geometry_set.get_component_for_read<CurveComponent>(), positions, radii); + for (const GeometryComponentType type : + {GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}) { + if (r_geometry_set.has(type)) { + gather_point_data_from_component( + params, *r_geometry_set.get_component_for_read(type), positions, radii); + } } const float max_radius = *std::max_element(radii.begin(), radii.end()); diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc index 1797364ad72..231ef547a8b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc @@ -141,10 +141,13 @@ static void raycast_to_mesh(IndexMask mask, { BVHTreeFromMesh tree_data; BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_LOOPTRI, 4); + BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&tree_data); }); + if (tree_data.tree == nullptr) { - free_bvhtree_from_mesh(&tree_data); return; } + /* We shouldn't be rebuilding the BVH tree when calling this function in parallel. */ + BLI_assert(tree_data.cached); for (const int i : mask) { const float ray_length = ray_lengths[i]; @@ -197,10 +200,6 @@ static void raycast_to_mesh(IndexMask mask, } } } - - /* We shouldn't be rebuilding the BVH tree when calling this function in parallel. */ - BLI_assert(tree_data.cached); - free_bvhtree_from_mesh(&tree_data); } class RaycastFunction : public fn::MultiFunction { diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc index fb648baad08..301410f5126 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc @@ -35,7 +35,7 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node) } static void set_position_in_component(const GeometryNodeCurveHandleMode mode, - GeometryComponent &component, + CurveComponent &component, const Field<bool> &selection_field, const Field<float3> &position_field, const Field<float3> &offset_field) @@ -53,37 +53,31 @@ static void set_position_in_component(const GeometryNodeCurveHandleMode mode, evaluator.evaluate(); const IndexMask selection = evaluator.get_evaluated_selection_as_mask(); - CurveComponent *curve_component = static_cast<CurveComponent *>(&component); - CurveEval *curve = curve_component->get_for_write(); - - StringRef side = mode & GEO_NODE_CURVE_HANDLE_LEFT ? "handle_left" : "handle_right"; + std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component.get_for_read()); int current_point = 0; int current_mask = 0; - for (const SplinePtr &spline : curve->splines()) { - if (spline->type() == Spline::Type::Bezier) { + if (spline->type() == CURVE_TYPE_BEZIER) { BezierSpline &bezier = static_cast<BezierSpline &>(*spline); - for (int i : bezier.positions().index_range()) { + + bezier.ensure_auto_handles(); + for (const int i : bezier.positions().index_range()) { if (current_mask < selection.size() && selection[current_mask] == current_point) { if (mode & GEO_NODE_CURVE_HANDLE_LEFT) { - if (bezier.handle_types_left()[i] == BezierSpline::HandleType::Vector) { - bezier.ensure_auto_handles(); - bezier.handle_types_left()[i] = BezierSpline::HandleType::Free; + if (bezier.handle_types_left()[i] == BEZIER_HANDLE_VECTOR) { + bezier.handle_types_left()[i] = BEZIER_HANDLE_FREE; } - else if (bezier.handle_types_left()[i] == BezierSpline::HandleType::Auto) { - bezier.ensure_auto_handles(); - bezier.handle_types_left()[i] = BezierSpline::HandleType::Align; + else if (bezier.handle_types_left()[i] == BEZIER_HANDLE_AUTO) { + bezier.handle_types_left()[i] = BEZIER_HANDLE_ALIGN; } } else { - if (bezier.handle_types_right()[i] == BezierSpline::HandleType::Vector) { - bezier.ensure_auto_handles(); - bezier.handle_types_right()[i] = BezierSpline::HandleType::Free; + if (bezier.handle_types_right()[i] == BEZIER_HANDLE_VECTOR) { + bezier.handle_types_right()[i] = BEZIER_HANDLE_FREE; } - else if (bezier.handle_types_right()[i] == BezierSpline::HandleType::Auto) { - bezier.ensure_auto_handles(); - bezier.handle_types_right()[i] = BezierSpline::HandleType::Align; + else if (bezier.handle_types_right()[i] == BEZIER_HANDLE_AUTO) { + bezier.handle_types_right()[i] = BEZIER_HANDLE_ALIGN; } } current_mask++; @@ -104,15 +98,35 @@ static void set_position_in_component(const GeometryNodeCurveHandleMode mode, const VArray<float3> &positions_input = evaluator.get_evaluated<float3>(0); const VArray<float3> &offsets_input = evaluator.get_evaluated<float3>(1); - OutputAttribute_Typed<float3> positions = component.attribute_try_get_for_output<float3>( - side, ATTR_DOMAIN_POINT, {0, 0, 0}); - MutableSpan<float3> position_mutable = positions.as_span(); - - for (int i : selection) { - position_mutable[i] = positions_input[i] + offsets_input[i]; + current_point = 0; + current_mask = 0; + for (const SplinePtr &spline : curve->splines()) { + if (spline->type() == CURVE_TYPE_BEZIER) { + BezierSpline &bezier = static_cast<BezierSpline &>(*spline); + for (const int i : bezier.positions().index_range()) { + if (current_mask < selection.size() && selection[current_mask] == current_point) { + if (mode & GEO_NODE_CURVE_HANDLE_LEFT) { + bezier.set_handle_position_left(i, positions_input[i] + offsets_input[i]); + } + else { + bezier.set_handle_position_right(i, positions_input[i] + offsets_input[i]); + } + current_mask++; + } + current_point++; + } + } + else { + for ([[maybe_unused]] int i : spline->positions().index_range()) { + if (current_mask < selection.size() && selection[current_mask] == current_point) { + current_mask++; + } + current_point++; + } + } } - positions.save(); + component.replace(curve_eval_to_curves(*curve), GeometryOwnershipType::Owned); } static void node_geo_exec(GeoNodeExecParams params) @@ -127,9 +141,11 @@ static void node_geo_exec(GeoNodeExecParams params) bool has_bezier = false; geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (geometry_set.has_curve() && - geometry_set.get_curve_for_read()->has_spline_with_type(Spline::Type::Bezier)) { - has_bezier = true; + if (geometry_set.has_curves()) { + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *geometry_set.get_curves_for_read()); + has_bezier = curve->has_spline_with_type(CURVE_TYPE_BEZIER); + set_position_in_component(mode, geometry_set.get_component_for_write<CurveComponent>(), selection_field, diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc index 2f59d008df0..a23a6c09551 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc @@ -44,7 +44,7 @@ static void node_geo_exec(GeoNodeExecParams params) Field<float> radii_field = params.extract_input<Field<float>>("Radius"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (geometry_set.has_curve()) { + if (geometry_set.has_curves()) { set_radius_in_component( geometry_set.get_component_for_write<CurveComponent>(), selection_field, radii_field); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc index b94782b8c4c..1155c97dc38 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc @@ -40,7 +40,7 @@ static void node_geo_exec(GeoNodeExecParams params) Field<float> tilt_field = params.extract_input<Field<float>>("Tilt"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (geometry_set.has_curve()) { + if (geometry_set.has_curves()) { set_tilt_in_component( geometry_set.get_component_for_write<CurveComponent>(), selection_field, tilt_field); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc index 361a75ee0a8..eb035aa9b6b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc @@ -61,6 +61,41 @@ static void set_computed_position_and_offset(GeometryComponent &component, } break; } + case GEO_COMPONENT_TYPE_CURVE: { + if (component.attribute_exists("handle_right") && + component.attribute_exists("handle_left")) { + OutputAttribute_Typed<float3> handle_right_attribute = + component.attribute_try_get_for_output<float3>( + "handle_right", ATTR_DOMAIN_POINT, {0, 0, 0}); + OutputAttribute_Typed<float3> handle_left_attribute = + component.attribute_try_get_for_output<float3>( + "handle_left", ATTR_DOMAIN_POINT, {0, 0, 0}); + MutableSpan<float3> handle_right = handle_right_attribute.as_span(); + MutableSpan<float3> handle_left = handle_left_attribute.as_span(); + + MutableSpan<float3> out_positions_span = positions.as_span(); + devirtualize_varray2( + in_positions, in_offsets, [&](const auto in_positions, const auto in_offsets) { + threading::parallel_for( + selection.index_range(), grain_size, [&](const IndexRange range) { + for (const int i : selection.slice(range)) { + const float3 new_position = in_positions[i] + in_offsets[i]; + const float3 delta = new_position - out_positions_span[i]; + handle_right[i] += delta; + handle_left[i] += delta; + out_positions_span[i] = new_position; + } + }); + }); + + handle_right_attribute.save(); + handle_left_attribute.save(); + break; + } + else { + ATTR_FALLTHROUGH; + } + } default: { MutableSpan<float3> out_positions_span = positions.as_span(); if (in_positions.is_same(positions.varray())) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc index 70e363064cd..dc7f3b1343a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc @@ -40,7 +40,7 @@ static void node_geo_exec(GeoNodeExecParams params) Field<bool> cyclic_field = params.extract_input<Field<bool>>("Cyclic"); geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (geometry_set.has_curve()) { + if (geometry_set.has_curves()) { set_cyclic_in_component( geometry_set.get_component_for_write<CurveComponent>(), selection_field, cyclic_field); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc index e4702035eec..da8d7bcf255 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc @@ -43,10 +43,12 @@ static void node_geo_exec(GeoNodeExecParams params) bool only_poly = true; geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { - if (geometry_set.has_curve()) { + if (geometry_set.has_curves()) { if (only_poly) { - for (const SplinePtr &spline : geometry_set.get_curve_for_read()->splines()) { - if (ELEM(spline->type(), Spline::Type::Bezier, Spline::Type::NURBS)) { + const std::unique_ptr<CurveEval> curve = curves_to_curve_eval( + *geometry_set.get_curves_for_read()); + for (const SplinePtr &spline : curve->splines()) { + if (ELEM(spline->type(), CURVE_TYPE_BEZIER, CURVE_TYPE_NURBS)) { only_poly = false; break; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc index ddc0bb2bc11..bc34a1a6f2c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc @@ -298,7 +298,8 @@ static Map<int, int> create_curve_instances(GeoNodeExecParams ¶ms, layout.pivot_points.add_new(layout.char_codes[i], pivot_point); } - GeometrySet geometry_set_curve = GeometrySet::create_with_curve(curve_eval.release()); + GeometrySet geometry_set_curve = GeometrySet::create_with_curves( + curve_eval_to_curves(*curve_eval)); handles.add_new(layout.char_codes[i], instance_component.add_reference(std::move(geometry_set_curve))); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc index 789478873f6..2d5b0e58367 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc @@ -778,7 +778,7 @@ static void node_geo_exec(GeoNodeExecParams params) break; } case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST: { - if (geometry.has_curve() && !geometry.has_mesh() && !geometry.has_pointcloud()) { + if (geometry.has_curves() && !geometry.has_mesh() && !geometry.has_pointcloud()) { params.error_message_add(NodeWarningType::Error, TIP_("The source geometry must contain a mesh or a point cloud")); return return_default(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc index 5950a2a16d2..95cec8eab11 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc @@ -10,6 +10,7 @@ #include "DNA_pointcloud_types.h" #include "DNA_volume_types.h" +#include "BKE_curves.hh" #include "BKE_mesh.h" #include "BKE_pointcloud.h" #include "BKE_spline.hh" @@ -125,8 +126,10 @@ static void translate_geometry_set(GeometrySet &geometry, const float3 translation, const Depsgraph &depsgraph) { - if (CurveEval *curve = geometry.get_curve_for_write()) { + if (Curves *curves = geometry.get_curves_for_write()) { + std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curves); curve->translate(translation); + geometry.replace_curve(curve_eval_to_curves(*curve)); } if (Mesh *mesh = geometry.get_mesh_for_write()) { translate_mesh(*mesh, translation); @@ -146,8 +149,10 @@ void transform_geometry_set(GeometrySet &geometry, const float4x4 &transform, const Depsgraph &depsgraph) { - if (CurveEval *curve = geometry.get_curve_for_write()) { + if (Curves *curves = geometry.get_curves_for_write()) { + std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*curves); curve->transform(transform); + geometry.replace_curve(curve_eval_to_curves(*curve)); } if (Mesh *mesh = geometry.get_mesh_for_write()) { transform_mesh(*mesh, transform); diff --git a/source/blender/nodes/intern/node_common.cc b/source/blender/nodes/intern/node_common.cc index d6a4af7ef39..c4befd5828c 100644 --- a/source/blender/nodes/intern/node_common.cc +++ b/source/blender/nodes/intern/node_common.cc @@ -171,7 +171,7 @@ static void group_verify_socket_list(bNodeTree &node_tree, BLI_addtail(&verify_lb, matching_socket); } else { - /* If there was no socket withe the same identifier already, simply create a new socket + /* If there was no socket with the same identifier already, simply create a new socket * based on the interface socket, which will already add it to the new list. */ add_new_socket_from_interface(node_tree, node, *interface_socket, in_out); } @@ -326,7 +326,7 @@ void ntree_update_reroute_nodes(bNodeTree *ntree) } /* Propagate socket types from right to left. This affects reroute nodes that haven't been - * changed in the the loop above. */ + * changed in the loop above. */ for (bNode *start_node : nodes_linked_with_reroutes) { LISTBASE_FOREACH (bNodeSocket *, input_socket, &start_node->inputs) { propagate_reroute_type_from_start_socket(input_socket, links_map, reroute_types); diff --git a/source/blender/nodes/shader/nodes/node_shader_ambient_occlusion.cc b/source/blender/nodes/shader/nodes/node_shader_ambient_occlusion.cc index 8a3b0258b55..a24b4379e69 100644 --- a/source/blender/nodes/shader/nodes/node_shader_ambient_occlusion.cc +++ b/source/blender/nodes/shader/nodes/node_shader_ambient_occlusion.cc @@ -38,7 +38,7 @@ static int node_shader_gpu_ambient_occlusion(GPUMaterial *mat, GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE); - float inverted = node->custom2 ? 1.0f : 0.0f; + float inverted = (node->custom2 & SHD_AO_INSIDE) ? 1.0f : 0.0f; float f_samples = divide_ceil_u(node->custom1, 4); return GPU_stack_link(mat, diff --git a/source/blender/python/gpu/gpu_py_state.c b/source/blender/python/gpu/gpu_py_state.c index 2c5012216f2..e3ffd3cc823 100644 --- a/source/blender/python/gpu/gpu_py_state.c +++ b/source/blender/python/gpu/gpu_py_state.c @@ -151,7 +151,7 @@ static PyObject *pygpu_state_depth_test_set(PyObject *UNUSED(self), PyObject *va } PyDoc_STRVAR(pygpu_state_depth_test_get_doc, - ".. function:: blend_depth_test_get()\n" + ".. function:: depth_test_get()\n" "\n" " Current depth_test equation.\n" "\n"); @@ -179,7 +179,7 @@ static PyObject *pygpu_state_depth_mask_set(PyObject *UNUSED(self), PyObject *va } PyDoc_STRVAR(pygpu_state_depth_mask_get_doc, - ".. function:: depth_mask_set_get()\n" + ".. function:: depth_mask_get()\n" "\n" " Writing status in the depth component.\n"); static PyObject *pygpu_state_depth_mask_get(PyObject *UNUSED(self)) @@ -326,7 +326,7 @@ static PyObject *pygpu_state_front_facing_set(PyObject *UNUSED(self), PyObject * } PyDoc_STRVAR(pygpu_state_program_point_size_set_doc, - ".. function:: use_program_point_size(enable)\n" + ".. function:: program_point_size_set(enable)\n" "\n" " If enabled, the derived point size is taken from the (potentially clipped) " "shader builtin gl_PointSize.\n" diff --git a/source/blender/python/intern/bpy_capi_utils.h b/source/blender/python/intern/bpy_capi_utils.h index 4a2e5fdebb7..223c6ad5f7e 100644 --- a/source/blender/python/intern/bpy_capi_utils.h +++ b/source/blender/python/intern/bpy_capi_utils.h @@ -6,8 +6,8 @@ #pragma once -#if PY_VERSION_HEX < 0x03090000 -# error "Python 3.9 or greater is required, you'll need to update your Python." +#if PY_VERSION_HEX < 0x030a0000 +# error "Python 3.10 or greater is required, you'll need to update your Python." #endif #ifdef __cplusplus diff --git a/source/blender/python/mathutils/mathutils.c b/source/blender/python/mathutils/mathutils.c index 89fb89cfd41..8ed156a7e55 100644 --- a/source/blender/python/mathutils/mathutils.c +++ b/source/blender/python/mathutils/mathutils.c @@ -76,11 +76,7 @@ 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_bvhtree.c b/source/blender/python/mathutils/mathutils_bvhtree.c index 8adde14d82f..ead255a6716 100644 --- a/source/blender/python/mathutils/mathutils_bvhtree.c +++ b/source/blender/python/mathutils/mathutils_bvhtree.c @@ -1166,10 +1166,8 @@ static PyObject *C_BVHTree_FromObject(PyObject *UNUSED(cls), PyObject *args, PyO tree = BLI_bvhtree_new((int)tris_len, epsilon, PY_BVH_TREE_TYPE_DEFAULT, PY_BVH_AXIS_DEFAULT); if (tree) { orig_index = MEM_mallocN(sizeof(*orig_index) * (size_t)tris_len, __func__); - CustomData *pdata = &mesh->pdata; - orig_normal = CustomData_get_layer(pdata, CD_NORMAL); /* can be NULL */ - if (orig_normal) { - orig_normal = MEM_dupallocN(orig_normal); + if (!BKE_mesh_poly_normals_are_dirty(mesh)) { + orig_normal = MEM_dupallocN(BKE_mesh_poly_normals_ensure(mesh)); } for (i = 0; i < tris_len; i++, lt++) { diff --git a/source/blender/render/intern/bake.c b/source/blender/render/intern/bake.c index 4971071316e..596adafb2c9 100644 --- a/source/blender/render/intern/bake.c +++ b/source/blender/render/intern/bake.c @@ -468,7 +468,9 @@ static TriTessFace *mesh_calc_tri_tessface(Mesh *me, bool tangent, Mesh *me_eval looptri = MEM_mallocN(sizeof(*looptri) * tottri, __func__); triangles = MEM_callocN(sizeof(TriTessFace) * tottri, __func__); - const float(*precomputed_normals)[3] = CustomData_get_layer(&me->pdata, CD_NORMAL); + const float(*precomputed_normals)[3] = BKE_mesh_poly_normals_are_dirty(me) ? + NULL : + BKE_mesh_poly_normals_ensure(me); const bool calculate_normal = precomputed_normals ? false : true; if (precomputed_normals != NULL) { diff --git a/source/blender/render/intern/multires_bake.c b/source/blender/render/intern/multires_bake.c index d99b2e729d1..a5c13c26590 100644 --- a/source/blender/render/intern/multires_bake.c +++ b/source/blender/render/intern/multires_bake.c @@ -467,7 +467,6 @@ static void do_multires_bake(MultiresBakeRender *bkr, MPoly *mpoly = dm->getPolyArray(dm); MLoop *mloop = dm->getLoopArray(dm); MLoopUV *mloopuv = dm->getLoopDataArray(dm, CD_MLOOPUV); - const float *precomputed_normals = dm->getPolyDataArray(dm, CD_NORMAL); float *pvtangent = NULL; ListBase threads; @@ -482,6 +481,7 @@ static void do_multires_bake(MultiresBakeRender *bkr, memcpy(temp_mesh->mpoly, dm->getPolyArray(dm), temp_mesh->totpoly * sizeof(*temp_mesh->mpoly)); memcpy(temp_mesh->mloop, dm->getLoopArray(dm), temp_mesh->totloop * sizeof(*temp_mesh->mloop)); const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(temp_mesh); + const float(*poly_normals)[3] = BKE_mesh_poly_normals_ensure(temp_mesh); if (require_tangent) { if (CustomData_get_layer_index(&dm->loopData, CD_TANGENT) == -1) { @@ -497,7 +497,7 @@ static void do_multires_bake(MultiresBakeRender *bkr, NULL, 0, vert_normals, - (const float(*)[3])CustomData_get_layer(&dm->polyData, CD_NORMAL), + poly_normals, (const float(*)[3])dm->getLoopDataArray(dm, CD_NORMAL), (const float(*)[3])dm->getVertDataArray(dm, CD_ORCO), /* may be nullptr */ /* result */ @@ -542,7 +542,7 @@ static void do_multires_bake(MultiresBakeRender *bkr, handle->data.mlooptri = mlooptri; handle->data.mloop = mloop; handle->data.pvtangent = pvtangent; - handle->data.precomputed_normals = precomputed_normals; /* don't strictly need this */ + handle->data.precomputed_normals = (float *)poly_normals; /* don't strictly need this */ handle->data.w = ibuf->x; handle->data.h = ibuf->y; handle->data.lores_dm = dm; diff --git a/source/blender/render/intern/pipeline.c b/source/blender/render/intern/pipeline.c index ed4326bce69..2a93fb2c46b 100644 --- a/source/blender/render/intern/pipeline.c +++ b/source/blender/render/intern/pipeline.c @@ -1106,6 +1106,8 @@ static void do_render_compositor_scenes(Render *re) return; } + bool changed_scene = false; + /* now foreach render-result node we do a full render */ /* results are stored in a way compositor will find it */ GSet *scenes_rendered = BLI_gset_ptr_new(__func__); @@ -1118,11 +1120,20 @@ static void do_render_compositor_scenes(Render *re) do_render_compositor_scene(re, scene, cfra); BLI_gset_add(scenes_rendered, scene); node->typeinfo->updatefunc(restore_scene->nodetree, node); + + if (scene != re->scene) { + changed_scene = true; + } } } } } BLI_gset_free(scenes_rendered, NULL); + + if (changed_scene) { + /* If rendered another scene, switch back to the current scene with compositing nodes. */ + re->current_scene_update(re->suh, re->scene); + } } /* bad call... need to think over proper method still */ diff --git a/source/blender/sequencer/intern/effects.c b/source/blender/sequencer/intern/effects.c index 3196ca08155..aa433eeed09 100644 --- a/source/blender/sequencer/intern/effects.c +++ b/source/blender/sequencer/intern/effects.c @@ -1918,7 +1918,7 @@ static void RVBlurBitmap2_float(float *map, int width, int height, float blur, i * I changed the math around to implement an actual Gaussian distribution. * * Watch out though, it tends to misbehave with large blur values on - * a small bitmap. Avoid avoid! */ + * a small bitmap. Avoid! */ float *temp = NULL, *swap; float *filter = NULL; diff --git a/source/blender/sequencer/intern/proxy.c b/source/blender/sequencer/intern/proxy.c index d87da1557e0..91b69bfe01f 100644 --- a/source/blender/sequencer/intern/proxy.c +++ b/source/blender/sequencer/intern/proxy.c @@ -549,18 +549,6 @@ void SEQ_proxy_rebuild(SeqIndexBuildContext *context, } } -static bool seq_orig_free_anims(Sequence *seq_iter, void *data) -{ - SessionUUID orig_seq_uuid = ((SeqIndexBuildContext *)data)->orig_seq_uuid; - - if (BLI_session_uuid_is_equal(&seq_iter->runtime.session_uuid, &orig_seq_uuid)) { - for (StripAnim *sanim = seq_iter->anims.first; sanim; sanim = sanim->next) { - IMB_close_anim_proxies(sanim->anim); - } - } - return true; -} - void SEQ_proxy_rebuild_finish(SeqIndexBuildContext *context, bool stop) { if (context->index_context) { @@ -570,9 +558,6 @@ void SEQ_proxy_rebuild_finish(SeqIndexBuildContext *context, bool stop) IMB_close_anim_proxies(sanim->anim); } - /* `context->seq_orig` may have been removed during building. */ - SEQ_for_each_callback(&context->scene->ed->seqbase, seq_orig_free_anims, context); - IMB_anim_index_rebuild_finish(context->index_context, stop); } diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index c1281632cc1..21d76ce93bd 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -127,11 +127,7 @@ void WM_reinit_gizmomap_all(struct Main *bmain); */ void WM_script_tag_reload(void); -wmWindow *WM_window_find_under_cursor(const wmWindowManager *wm, - const wmWindow *win_ignore, - const wmWindow *win, - const int mval[2], - int r_mval[2]); +wmWindow *WM_window_find_under_cursor(wmWindow *win, const int mval[2], int r_mval[2]); void WM_window_pixel_sample_read(const wmWindowManager *wm, const wmWindow *win, const int pos[2], @@ -1247,6 +1243,8 @@ enum { WM_JOB_TYPE_COMPOSITE, WM_JOB_TYPE_RENDER, WM_JOB_TYPE_RENDER_PREVIEW, /* UI preview */ + /** Job for the UI to load previews from the file system (uses OS thumbnail cache). */ + WM_JOB_TYPE_LOAD_PREVIEW, /* UI preview */ WM_JOB_TYPE_OBJECT_SIM_OCEAN, WM_JOB_TYPE_OBJECT_SIM_FLUID, WM_JOB_TYPE_OBJECT_BAKE_TEXTURE, @@ -1425,15 +1423,17 @@ bool WM_window_modal_keymap_status_draw(struct bContext *C, */ void WM_event_print(const struct wmEvent *event); -int WM_event_modifier_flag(const struct wmEvent *event); - /** - * For modal callbacks, check configuration for how to interpret exit with tweaks. + * For modal callbacks, check configuration for how to interpret exit when dragging. */ -bool WM_event_is_modal_tweak_exit(const struct wmEvent *event, int tweak_event); +bool WM_event_is_modal_drag_exit(const struct wmEvent *event, + short init_event_type, + short init_event_val); bool WM_event_is_last_mousemove(const struct wmEvent *event); bool WM_event_is_mouse_drag(const struct wmEvent *event); bool WM_event_is_mouse_drag_or_press(const wmEvent *event); +int WM_event_drag_direction(const wmEvent *event); + /** * Detect motion between selection (callers should only use this for selection picking), * typically mouse press/click events. @@ -1583,8 +1583,7 @@ bool WM_xr_action_create(wmXrData *xr, const char *action_set_name, const char *action_name, eXrActionType type, - unsigned int count_subaction_paths, - const char **subaction_paths, + const ListBase *user_paths, struct wmOperatorType *ot, struct IDProperty *op_properties, const char *haptic_name, @@ -1599,9 +1598,8 @@ bool WM_xr_action_binding_create(wmXrData *xr, const char *action_set_name, const char *action_name, const char *profile_path, - unsigned int count_subaction_paths, - const char **subaction_paths, - const char **component_paths, + const ListBase *user_paths, + const ListBase *component_paths, const float *float_thresholds, const eXrAxisFlag *axis_flags, const struct wmXrPose *poses); diff --git a/source/blender/windowmanager/WM_keymap.h b/source/blender/windowmanager/WM_keymap.h index 32315d72ba7..068dbb32be2 100644 --- a/source/blender/windowmanager/WM_keymap.h +++ b/source/blender/windowmanager/WM_keymap.h @@ -42,8 +42,13 @@ void WM_keymap_clear(struct wmKeyMap *keymap); /** * Always add item. */ -wmKeyMapItem *WM_keymap_add_item( - struct wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier); +wmKeyMapItem *WM_keymap_add_item(struct wmKeyMap *keymap, + const char *idname, + int type, + int val, + int modifier, + int keymodifier, + int direction); wmKeyMapItem *WM_keymap_add_item_copy(struct wmKeyMap *keymap, wmKeyMapItem *kmi_src); bool WM_keymap_remove_item(struct wmKeyMap *keymap, struct wmKeyMapItem *kmi); @@ -80,32 +85,43 @@ bool WM_keymap_item_compare(const struct wmKeyMapItem *k1, const struct wmKeyMap /** * Menu wrapper for #WM_keymap_add_item. */ -wmKeyMapItem *WM_keymap_add_menu( - struct wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier); +wmKeyMapItem *WM_keymap_add_menu(struct wmKeyMap *keymap, + const char *idname, + int type, + int val, + int modifier, + int keymodifier, + int direction); /** * Pie-menu wrapper for #WM_keymap_add_item. */ -wmKeyMapItem *WM_keymap_add_menu_pie( - struct wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier); +wmKeyMapItem *WM_keymap_add_menu_pie(struct wmKeyMap *keymap, + const char *idname, + int type, + int val, + int modifier, + int keymodifier, + int direction); /** * Panel (popover) wrapper for #WM_keymap_add_item. */ -wmKeyMapItem *WM_keymap_add_panel( - struct wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier); +wmKeyMapItem *WM_keymap_add_panel(struct wmKeyMap *keymap, + const char *idname, + int type, + int val, + int modifier, + int keymodifier, + int direction); /** * Tool wrapper for #WM_keymap_add_item. */ -wmKeyMapItem *WM_keymap_add_tool( - struct wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier); - -/** Useful for mapping numbers to an enum. */ -void WM_keymap_add_context_enum_set_items(wmKeyMap *keymap, - const struct EnumPropertyItem *items, - const char *data_path, - int type_start, - int val, - int modifier, - int keymodifier); +wmKeyMapItem *WM_keymap_add_tool(struct wmKeyMap *keymap, + const char *idname, + int type, + int val, + int modifier, + int keymodifier, + int direction); wmKeyMap *WM_keymap_guess_from_context(const struct bContext *C); @@ -137,10 +153,20 @@ wmKeyMap *WM_modalkeymap_ensure(struct wmKeyConfig *keyconf, const char *idname, const struct EnumPropertyItem *items); wmKeyMap *WM_modalkeymap_find(struct wmKeyConfig *keyconf, const char *idname); -wmKeyMapItem *WM_modalkeymap_add_item( - struct wmKeyMap *km, int type, int val, int modifier, int keymodifier, int value); -wmKeyMapItem *WM_modalkeymap_add_item_str( - struct wmKeyMap *km, int type, int val, int modifier, int keymodifier, const char *value); +wmKeyMapItem *WM_modalkeymap_add_item(struct wmKeyMap *km, + int type, + int val, + int modifier, + int keymodifier, + int direction, + int value); +wmKeyMapItem *WM_modalkeymap_add_item_str(struct wmKeyMap *km, + int type, + int val, + int modifier, + int keymodifier, + int direction, + const char *value); const wmKeyMapItem *WM_modalkeymap_find_propvalue(const wmKeyMap *km, int propvalue); void WM_modalkeymap_assign(struct wmKeyMap *km, const char *opname); diff --git a/source/blender/windowmanager/WM_toolsystem.h b/source/blender/windowmanager/WM_toolsystem.h index 4886e39943f..a9e1495d9bf 100644 --- a/source/blender/windowmanager/WM_toolsystem.h +++ b/source/blender/windowmanager/WM_toolsystem.h @@ -103,6 +103,7 @@ void WM_toolsystem_do_msg_notify_tag_refresh(struct bContext *C, struct wmMsgSubscribeKey *msg_key, struct wmMsgSubscribeValue *msg_val); +struct IDProperty *WM_toolsystem_ref_properties_get_idprops(struct bToolRef *tref); struct IDProperty *WM_toolsystem_ref_properties_ensure_idprops(struct bToolRef *tref); void WM_toolsystem_ref_properties_ensure_ex(struct bToolRef *tref, const char *idname, diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 4799483157c..c4858a8a1fa 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -225,35 +225,68 @@ typedef enum eOperatorPropTags { } eOperatorPropTags; #define OP_PROP_TAG_ADVANCED ((eOperatorPropTags)OP_PROP_TAG_ADVANCED) -/* ************** wmKeyMap ************************ */ - -/* modifier */ -#define KM_SHIFT 1 -#define KM_CTRL 2 -#define KM_ALT 4 -#define KM_OSKEY 8 - -/* Used for key-map item creation function arguments (never stored in DNA). */ -#define KM_SHIFT_ANY 16 -#define KM_CTRL_ANY 32 -#define KM_ALT_ANY 64 -#define KM_OSKEY_ANY 128 - -/* KM_MOD_ flags for `wmKeyMapItem` and `wmEvent.alt/shift/oskey/ctrl`. */ -/* note that KM_ANY and KM_NOTHING are used with these defines too */ +/* -------------------------------------------------------------------- */ +/** \name #wmKeyMapItem + * \{ */ + +/** + * Modifier keys, not actually used for #wmKeyMapItem (never stored in DNA), used for: + * - #wmEvent.modifier without the `KM_*_ANY` flags. + * - #WM_keymap_add_item & #WM_modalkeymap_add_item + */ +enum { + KM_SHIFT = (1 << 0), + KM_CTRL = (1 << 1), + KM_ALT = (1 << 2), + KM_OSKEY = (1 << 3), + + /* Used for key-map item creation function arguments. */ + KM_SHIFT_ANY = (1 << 4), + KM_CTRL_ANY = (1 << 5), + KM_ALT_ANY = (1 << 6), + KM_OSKEY_ANY = (1 << 7), +}; + +/* `KM_MOD_*` flags for #wmKeyMapItem and `wmEvent.alt/shift/oskey/ctrl`. */ +/* Note that #KM_ANY and #KM_NOTHING are used with these defines too. */ #define KM_MOD_HELD 1 -/* type: defined in wm_event_types.c */ -#define KM_TEXTINPUT -2 +/** + * #wmKeyMapItem.type + * NOTE: most types are defined in `wm_event_types.h`. + */ +enum { + KM_TEXTINPUT = -2, +}; -/* val */ -#define KM_ANY -1 -#define KM_NOTHING 0 -#define KM_PRESS 1 -#define KM_RELEASE 2 -#define KM_CLICK 3 -#define KM_DBL_CLICK 4 -#define KM_CLICK_DRAG 5 +/** #wmKeyMapItem.val */ +enum { + KM_ANY = -1, + KM_NOTHING = 0, + KM_PRESS = 1, + KM_RELEASE = 2, + KM_CLICK = 3, + KM_DBL_CLICK = 4, + KM_CLICK_DRAG = 5, +}; + +/** + * #wmKeyMapItem.direction + * + * Value of tweaks and line gestures. #KM_ANY (-1) works for this case too. + */ +enum { + KM_DIRECTION_N = 1, + KM_DIRECTION_NE = 2, + KM_DIRECTION_E = 3, + KM_DIRECTION_SE = 4, + KM_DIRECTION_S = 5, + KM_DIRECTION_SW = 6, + KM_DIRECTION_W = 7, + KM_DIRECTION_NW = 8, +}; + +/** \} */ /* ************** UI Handler ***************** */ @@ -470,6 +503,7 @@ typedef struct wmNotifier { #define NS_EDITMODE_ARMATURE (8 << 8) #define NS_MODE_POSE (9 << 8) #define NS_MODE_PARTICLE (10 << 8) +#define NS_EDITMODE_CURVES (11 << 8) /* subtype 3d view editing */ #define NS_VIEW3D_GPU (16 << 8) @@ -493,7 +527,6 @@ typedef struct wmNotifier { /* ************** Gesture Manager data ************** */ /* wmGesture->type */ -#define WM_GESTURE_TWEAK 0 #define WM_GESTURE_LINES 1 #define WM_GESTURE_RECT 2 #define WM_GESTURE_CROSS_RECT 3 @@ -503,12 +536,15 @@ typedef struct wmNotifier { /** * wmGesture is registered to #wmWindow.gesture, handled by operator callbacks. - * Tweak gesture is builtin feature. */ typedef struct wmGesture { struct wmGesture *next, *prev; /** #wmEvent.type */ int event_type; + /** #wmEvent.modifier */ + uint8_t event_modifier; + /** #wmEvent.keymodifier */ + short event_keymodifier; /** Gesture type define. */ int type; /** bounds of region to draw gesture within. */ @@ -555,6 +591,22 @@ typedef struct wmGesture { /* ************** wmEvent ************************ */ +typedef enum eWM_EventFlag { + /** + * True if the operating system inverted the delta x/y values and resulting + * `prev_xy` values, for natural scroll direction. + * For absolute scroll direction, the delta must be negated again. + */ + WM_EVENT_SCROLL_INVERT = (1 << 0), + /** + * Generated by auto-repeat, note that this must only ever be set for keyboard events + * where `ISKEYBOARD(event->type) == true`. + * + * See #KMI_REPEAT_IGNORE for details on how key-map handling uses this. + */ + WM_EVENT_IS_REPEAT = (1 << 1), +} eWM_EventFlag; + typedef struct wmTabletData { /** 0=EVT_TABLET_NONE, 1=EVT_TABLET_STYLUS, 2=EVT_TABLET_ERASER. */ int active; @@ -611,22 +663,10 @@ typedef struct wmEvent { /** From ghost, fallback if utf8 isn't set. */ char ascii; - /** - * Generated by auto-repeat, note that this must only ever be set for keyboard events - * where `ISKEYBOARD(event->type) == true`. - * - * See #KMI_REPEAT_IGNORE for details on how key-map handling uses this. - */ - char is_repeat; - /** The previous value of `type`. */ short prev_type; /** The previous value of `val`. */ short prev_val; - /** The time when the key is pressed, see #PIL_check_seconds_timer. */ - double prev_click_time; - /** The location when the key is pressed (used to enforce drag thresholds). */ - int prev_click_xy[2]; /** * The previous value of #wmEvent.xy, * Unlike other previous state variables, this is set on any mouse motion. @@ -634,29 +674,40 @@ typedef struct wmEvent { */ int prev_xy[2]; - /** Modifier states. */ - /** 'oskey' is apple or windows-key, value denotes order of pressed. */ - short shift, ctrl, alt, oskey; + /** The `type` at the point of the click action. */ + short prev_click_type; + /** The time when the key is pressed, see #PIL_check_seconds_timer. */ + double prev_click_time; + /** The location when the key is pressed (used to enforce drag thresholds). */ + int prev_click_xy[2]; + /** The `modifier` at the point of the click action. */ + uint8_t prev_click_modifier; + /** The `keymodifier` at the point of the click action. */ + short prev_click_keymodifier; + + /** + * Modifier states. + * #KM_SHIFT, #KM_CTRL, #KM_ALT & #KM_OSKEY is apple or windows-key. + */ + uint8_t modifier; + + /** The direction (for #KM_CLICK_DRAG events only). */ + int8_t direction; + /** Raw-key modifier (allow using any key as a modifier). */ short keymodifier; /** Tablet info, available for mouse move and button events. */ wmTabletData tablet; + eWM_EventFlag flag; + /* Custom data. */ /** Custom data type, stylus, 6dof, see wm_event_types.h */ short custom; short customdata_free; - int pad2; /** Ascii, unicode, mouse-coords, angles, vectors, NDOF data, drag-drop info. */ void *customdata; - - /** - * True if the operating system inverted the delta x/y values and resulting - * `prev_xy` values, for natural scroll direction. - * For absolute scroll direction, the delta must be negated again. - */ - char is_direction_inverted; } wmEvent; /** diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c index c46a2b6afe5..2971cdf40c3 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c @@ -640,24 +640,29 @@ wmKeyMap *wm_gizmogroup_tweak_modal_keymap(wmKeyConfig *keyconf) keymap = WM_modalkeymap_ensure(keyconf, name, modal_items); /* items for modal map */ - WM_modalkeymap_add_item(keymap, EVT_ESCKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CANCEL); - WM_modalkeymap_add_item(keymap, RIGHTMOUSE, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CANCEL); + WM_modalkeymap_add_item(keymap, EVT_ESCKEY, KM_PRESS, KM_ANY, 0, KM_ANY, TWEAK_MODAL_CANCEL); + WM_modalkeymap_add_item(keymap, RIGHTMOUSE, KM_PRESS, KM_ANY, 0, KM_ANY, TWEAK_MODAL_CANCEL); - WM_modalkeymap_add_item(keymap, EVT_RETKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CONFIRM); - WM_modalkeymap_add_item(keymap, EVT_PADENTER, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_CONFIRM); + WM_modalkeymap_add_item(keymap, EVT_RETKEY, KM_PRESS, KM_ANY, 0, KM_ANY, TWEAK_MODAL_CONFIRM); + WM_modalkeymap_add_item(keymap, EVT_PADENTER, KM_PRESS, KM_ANY, 0, KM_ANY, TWEAK_MODAL_CONFIRM); WM_modalkeymap_add_item( - keymap, EVT_RIGHTSHIFTKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_PRECISION_ON); + keymap, EVT_RIGHTSHIFTKEY, KM_PRESS, KM_ANY, 0, KM_ANY, TWEAK_MODAL_PRECISION_ON); WM_modalkeymap_add_item( - keymap, EVT_RIGHTSHIFTKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_PRECISION_OFF); - WM_modalkeymap_add_item(keymap, EVT_LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_PRECISION_ON); + keymap, EVT_RIGHTSHIFTKEY, KM_RELEASE, KM_ANY, 0, KM_ANY, TWEAK_MODAL_PRECISION_OFF); WM_modalkeymap_add_item( - keymap, EVT_LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_PRECISION_OFF); + keymap, EVT_LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, KM_ANY, TWEAK_MODAL_PRECISION_ON); + WM_modalkeymap_add_item( + keymap, EVT_LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, KM_ANY, TWEAK_MODAL_PRECISION_OFF); - WM_modalkeymap_add_item(keymap, EVT_RIGHTCTRLKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_SNAP_ON); - WM_modalkeymap_add_item(keymap, EVT_RIGHTCTRLKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_SNAP_OFF); - WM_modalkeymap_add_item(keymap, EVT_LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, TWEAK_MODAL_SNAP_ON); - WM_modalkeymap_add_item(keymap, EVT_LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, TWEAK_MODAL_SNAP_OFF); + WM_modalkeymap_add_item( + keymap, EVT_RIGHTCTRLKEY, KM_PRESS, KM_ANY, 0, KM_ANY, TWEAK_MODAL_SNAP_ON); + WM_modalkeymap_add_item( + keymap, EVT_RIGHTCTRLKEY, KM_RELEASE, KM_ANY, 0, KM_ANY, TWEAK_MODAL_SNAP_OFF); + WM_modalkeymap_add_item( + keymap, EVT_LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, KM_ANY, TWEAK_MODAL_SNAP_ON); + WM_modalkeymap_add_item( + keymap, EVT_LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, KM_ANY, TWEAK_MODAL_SNAP_OFF); WM_modalkeymap_assign(keymap, "GIZMOGROUP_OT_gizmo_tweak"); @@ -709,24 +714,26 @@ static wmKeyMap *WM_gizmogroup_keymap_template_select_ex( const int select_tweak = (U.flag & USER_LMOUSESELECT) ? EVT_TWEAK_L : EVT_TWEAK_R; const int action_mouse = (U.flag & USER_LMOUSESELECT) ? RIGHTMOUSE : LEFTMOUSE; #else - const int select_mouse = RIGHTMOUSE; - const int select_tweak = EVT_TWEAK_R; - const int action_mouse = LEFTMOUSE; + const int select_mouse = RIGHTMOUSE, select_mouse_val = KM_PRESS; + const int select_tweak = RIGHTMOUSE, select_tweak_val = KM_CLICK_DRAG; + const int action_mouse = LEFTMOUSE, action_mouse_val = KM_PRESS; #endif if (do_init) { - WM_keymap_add_item(km, "GIZMOGROUP_OT_gizmo_tweak", action_mouse, KM_PRESS, KM_ANY, 0); - WM_keymap_add_item(km, "GIZMOGROUP_OT_gizmo_tweak", select_tweak, KM_ANY, 0, 0); + WM_keymap_add_item( + km, "GIZMOGROUP_OT_gizmo_tweak", action_mouse, action_mouse_val, KM_ANY, 0, KM_ANY); + WM_keymap_add_item( + km, "GIZMOGROUP_OT_gizmo_tweak", select_tweak, select_tweak_val, 0, 0, KM_ANY); } if (do_init) { wmKeyMapItem *kmi = WM_keymap_add_item( - km, "GIZMOGROUP_OT_gizmo_select", select_mouse, KM_PRESS, 0, 0); + km, "GIZMOGROUP_OT_gizmo_select", select_mouse, select_mouse_val, 0, 0, KM_ANY); RNA_boolean_set(kmi->ptr, "extend", false); RNA_boolean_set(kmi->ptr, "deselect", false); RNA_boolean_set(kmi->ptr, "toggle", false); kmi = WM_keymap_add_item( - km, "GIZMOGROUP_OT_gizmo_select", select_mouse, KM_PRESS, KM_SHIFT, 0); + km, "GIZMOGROUP_OT_gizmo_select", select_mouse, select_mouse_val, KM_SHIFT, 0, KM_ANY); RNA_boolean_set(kmi->ptr, "extend", false); RNA_boolean_set(kmi->ptr, "deselect", false); RNA_boolean_set(kmi->ptr, "toggle", true); @@ -1129,7 +1136,8 @@ void WM_gizmo_group_refresh(const bContext *C, wmGizmoGroup *gzgroup) ARegion *region = CTX_wm_region(C); BLI_assert(region->gizmo_map == gzmap); /* Check if the tweak event originated from this region. */ - if ((win->tweak != NULL) && BLI_rcti_compare(®ion->winrct, &win->tweak->winrct)) { + if ((win->eventstate != NULL) && (win->event_queue_check_drag) && + BLI_rcti_isect_pt_v(®ion->winrct, win->eventstate->prev_click_xy)) { /* We need to run refresh again. */ gzgroup->init_flag &= ~WM_GIZMOGROUP_INIT_REFRESH; WM_gizmomap_tag_refresh_drawstep(gzmap, WM_gizmomap_drawstep_from_gizmo_group(gzgroup)); diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index 4aba287aefb..f1ac19f4651 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -726,7 +726,7 @@ static wmGizmo *gizmo_find_intersected_3d(bContext *C, * - First, don't use the depth buffer at all, use occlusion queries to detect any gizmos. * If there are no gizmos or only one - early exit, otherwise. * - * - Bind the depth buffer and and use selection picking logic. + * - Bind the depth buffer and use selection picking logic. * This is much slower than occlusion queries (since it's reading depths while drawing). * When there is a single gizmo under the cursor (quite common), early exit, otherwise. * @@ -819,8 +819,6 @@ wmGizmo *wm_gizmomap_highlight_find(wmGizmoMap *gzmap, do_step[i] = WM_gizmo_context_check_drawstep(C, i); } - const int event_modifier = WM_event_modifier_flag(event); - LISTBASE_FOREACH (wmGizmoGroup *, gzgroup, &gzmap->groups) { /* If it were important we could initialize here, @@ -839,11 +837,11 @@ wmGizmo *wm_gizmomap_highlight_find(wmGizmoMap *gzmap, } if (step == WM_GIZMOMAP_DRAWSTEP_3D) { wm_gizmogroup_intersectable_gizmos_to_list( - wm, gzgroup, event_modifier, &visible_3d_gizmos); + wm, gzgroup, event->modifier, &visible_3d_gizmos); } else if (step == WM_GIZMOMAP_DRAWSTEP_2D) { if ((gz = wm_gizmogroup_find_intersected_gizmo( - wm, gzgroup, C, event_modifier, event->mval, r_part))) { + wm, gzgroup, C, event->modifier, event->mval, r_part))) { break; } } diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index 13bf902f02c..c333d8149ed 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -156,7 +156,6 @@ static void window_manager_blend_read_data(BlendDataReader *reader, ID *id) win->gpuctx = NULL; win->eventstate = NULL; win->cursor_keymap_status = NULL; - win->tweak = NULL; #if defined(WIN32) || defined(__APPLE__) win->ime_data = NULL; #endif diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index f69a612df19..4ffb6b90e11 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -318,6 +318,10 @@ static wmDropBox *dropbox_active(bContext *C, wmEventHandler_Dropbox *handler = (wmEventHandler_Dropbox *)handler_base; if (handler->dropboxes) { LISTBASE_FOREACH (wmDropBox *, drop, handler->dropboxes) { + if (drag->drop_state.ui_context) { + CTX_store_set(C, drag->drop_state.ui_context); + } + if (!drop->poll(C, drag, event)) { /* If the drop's poll fails, don't set the disabled-info. This would be too aggressive. * Instead show it only if the drop box could be used in principle, but the operator @@ -326,10 +330,6 @@ static wmDropBox *dropbox_active(bContext *C, } const wmOperatorCallContext opcontext = wm_drop_operator_context_get(drop); - if (drag->drop_state.ui_context) { - CTX_store_set(C, drag->drop_state.ui_context); - } - if (WM_operator_poll_context(C, drop->ot, opcontext)) { return drop; } @@ -651,8 +651,12 @@ void WM_drag_free_imported_drag_ID(struct Main *bmain, wmDrag *drag, wmDropBox * } ID *id = BKE_libblock_find_name(bmain, asset_drag->id_type, name); - if (id) { - BKE_id_delete(bmain, id); + if (id != NULL) { + /* Do not delete the dragged ID if it has any user, otherwise if it is a 're-used' ID it will + * cause T95636. Note that we need first to add the user that we want to remove in + * #BKE_id_free_us. */ + id_us_plus(id); + BKE_id_free_us(bmain, id); } } diff --git a/source/blender/windowmanager/intern/wm_event_query.c b/source/blender/windowmanager/intern/wm_event_query.c index 304d7f73eb1..ee13e1832ed 100644 --- a/source/blender/windowmanager/intern/wm_event_query.c +++ b/source/blender/windowmanager/intern/wm_event_query.c @@ -47,12 +47,7 @@ static void event_ids_from_type_and_value(const short type, RNA_enum_identifier(rna_enum_event_type_items, type, r_type_id); /* Value. */ - if (ISTWEAK(type)) { - RNA_enum_identifier(rna_enum_event_value_tweak_items, val, r_val_id); - } - else { - RNA_enum_identifier(rna_enum_event_value_all_items, val, r_val_id); - } + RNA_enum_identifier(rna_enum_event_value_items, val, r_val_id); } void WM_event_print(const wmEvent *event) @@ -80,12 +75,12 @@ void WM_event_print(const wmEvent *event) prev_type_id, event->prev_val, prev_val_id, - event->shift, - event->ctrl, - event->alt, - event->oskey, + (event->modifier & KM_SHIFT) != 0, + (event->modifier & KM_CTRL) != 0, + (event->modifier & KM_ALT) != 0, + (event->modifier & KM_OSKEY) != 0, event->keymodifier, - event->is_repeat, + (event->flag & WM_EVENT_IS_REPEAT) != 0, event->xy[0], event->xy[1], event->ascii, @@ -129,24 +124,6 @@ void WM_event_print(const wmEvent *event) /** \name Event Modifier/Type Queries * \{ */ -int WM_event_modifier_flag(const wmEvent *event) -{ - int flag = 0; - if (event->ctrl) { - flag |= KM_CTRL; - } - if (event->alt) { - flag |= KM_ALT; - } - if (event->shift) { - flag |= KM_SHIFT; - } - if (event->oskey) { - flag |= KM_OSKEY; - } - return flag; -} - bool WM_event_type_mask_test(const int event_type, const enum eEventType_Mask mask) { /* Keyboard. */ @@ -178,13 +155,6 @@ bool WM_event_type_mask_test(const int event_type, const enum eEventType_Mask ma } } - /* Tweak. */ - if (mask & EVT_TYPE_MASK_TWEAK) { - if (ISTWEAK(event_type)) { - return true; - } - } - /* Action Zone. */ if (mask & EVT_TYPE_MASK_ACTIONZONE) { if (IS_EVENT_ACTIONZONE(event_type)) { @@ -201,34 +171,30 @@ bool WM_event_type_mask_test(const int event_type, const enum eEventType_Mask ma /** \name Event Motion Queries * \{ */ -bool WM_event_is_modal_tweak_exit(const wmEvent *event, int tweak_event) +bool WM_event_is_modal_drag_exit(const wmEvent *event, + const short init_event_type, + const short init_event_val) { - /* if the release-confirm userpref setting is enabled, - * tweak events can be canceled when mouse is released - */ + /* If the release-confirm preference setting is enabled, + * drag events can be canceled when mouse is released. */ if (U.flag & USER_RELEASECONFIRM) { /* option on, so can exit with km-release */ if (event->val == KM_RELEASE) { - switch (tweak_event) { - case EVT_TWEAK_L: - case EVT_TWEAK_M: - case EVT_TWEAK_R: - return 1; + if ((init_event_val == KM_CLICK_DRAG) && (event->type == init_event_type)) { + return 1; } } else { - /* if the initial event wasn't a tweak event then - * ignore USER_RELEASECONFIRM setting: see T26756. */ - if (ELEM(tweak_event, EVT_TWEAK_L, EVT_TWEAK_M, EVT_TWEAK_R) == 0) { + /* If the initial event wasn't a drag event then + * ignore #USER_RELEASECONFIRM setting: see T26756. */ + if (init_event_val != KM_CLICK_DRAG) { return 1; } } } else { - /* this is fine as long as not doing km-release, otherwise - * some items (i.e. markers) being tweaked may end up getting - * dropped all over - */ + /* This is fine as long as not doing km-release, otherwise some items (i.e. markers) + * being tweaked may end up getting dropped all over. */ if (event->val != KM_RELEASE) { return 1; } @@ -249,7 +215,7 @@ bool WM_event_is_last_mousemove(const wmEvent *event) bool WM_event_is_mouse_drag(const wmEvent *event) { - return ISTWEAK(event->type) || (ISMOUSE_BUTTON(event->type) && (event->val == KM_CLICK_DRAG)); + return (ISMOUSE_BUTTON(event->type) && (event->val == KM_CLICK_DRAG)); } bool WM_event_is_mouse_drag_or_press(const wmEvent *event) @@ -258,6 +224,68 @@ bool WM_event_is_mouse_drag_or_press(const wmEvent *event) (ISMOUSE_BUTTON(event->type) && (event->val == KM_PRESS)); } +int WM_event_drag_direction(const wmEvent *event) +{ + const int delta[2] = { + event->xy[0] - event->prev_click_xy[0], + event->xy[1] - event->prev_click_xy[1], + }; + + int theta = round_fl_to_int(4.0f * atan2f((float)delta[1], (float)delta[0]) / (float)M_PI); + int val = KM_DIRECTION_W; + + if (theta == 0) { + val = KM_DIRECTION_E; + } + else if (theta == 1) { + val = KM_DIRECTION_NE; + } + else if (theta == 2) { + val = KM_DIRECTION_N; + } + else if (theta == 3) { + val = KM_DIRECTION_NW; + } + else if (theta == -1) { + val = KM_DIRECTION_SE; + } + else if (theta == -2) { + val = KM_DIRECTION_S; + } + else if (theta == -3) { + val = KM_DIRECTION_SW; + } + +#if 0 + /* debug */ + if (val == 1) { + printf("tweak north\n"); + } + if (val == 2) { + printf("tweak north-east\n"); + } + if (val == 3) { + printf("tweak east\n"); + } + if (val == 4) { + printf("tweak south-east\n"); + } + if (val == 5) { + printf("tweak south\n"); + } + if (val == 6) { + printf("tweak south-west\n"); + } + if (val == 7) { + printf("tweak west\n"); + } + if (val == 8) { + printf("tweak north-west\n"); + } +#endif + return val; +} + bool WM_cursor_test_motion_and_update(const int mval[2]) { static int mval_prev[2] = {-1, -1}; @@ -278,8 +306,8 @@ bool WM_cursor_test_motion_and_update(const int mval[2]) int WM_event_drag_threshold(const struct wmEvent *event) { int drag_threshold; - if (ISMOUSE(event->prev_type)) { - BLI_assert(event->prev_type != MOUSEMOVE); + if (ISMOUSE(event->prev_click_type)) { + BLI_assert(event->prev_click_type != MOUSEMOVE); /* Using the previous type is important is we want to check the last pressed/released button, * The `event->type` would include #MOUSEMOVE which is always the case when dragging * and does not help us know which threshold to use. */ @@ -331,12 +359,6 @@ int WM_userdef_event_map(int kmitype) int WM_userdef_event_type_from_keymap_type(int kmitype) { switch (kmitype) { - case EVT_TWEAK_L: - return LEFTMOUSE; - case EVT_TWEAK_M: - return MIDDLEMOUSE; - case EVT_TWEAK_R: - return RIGHTMOUSE; case WHEELOUTMOUSE: return (U.uiflag & USER_WHEELZOOMDIR) ? WHEELUPMOUSE : WHEELDOWNMOUSE; case WHEELINMOUSE: @@ -458,7 +480,7 @@ int WM_event_absolute_delta_x(const struct wmEvent *event) { int dx = event->xy[0] - event->prev_xy[0]; - if (!event->is_direction_inverted) { + if ((event->flag & WM_EVENT_SCROLL_INVERT) == 0) { dx = -dx; } @@ -469,7 +491,7 @@ int WM_event_absolute_delta_y(const struct wmEvent *event) { int dy = event->xy[1] - event->prev_xy[1]; - if (!event->is_direction_inverted) { + if ((event->flag & WM_EVENT_SCROLL_INVERT) == 0) { dy = -dy; } @@ -491,8 +513,8 @@ int WM_event_absolute_delta_y(const struct wmEvent *event) */ bool WM_event_is_ime_switch(const struct wmEvent *event) { - return event->val == KM_PRESS && event->type == EVT_SPACEKEY && - (event->ctrl || event->oskey || event->alt); + return (event->val == KM_PRESS) && (event->type == EVT_SPACEKEY) && + (event->modifier & (KM_CTRL | KM_OSKEY | KM_ALT)); } #endif diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 8a19c99f59b..375cc4e785e 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -153,7 +153,7 @@ wmEvent *WM_event_add_simulate(wmWindow *win, const wmEvent *event_to_add) win->eventstate->type = event->type; if (event->val == KM_PRESS) { - if (event->is_repeat == false) { + if ((event->flag & WM_EVENT_IS_REPEAT) == 0) { copy_v2_v2_int(win->eventstate->prev_click_xy, event->xy); } } @@ -166,7 +166,7 @@ void wm_event_free(wmEvent *event) #ifndef NDEBUG /* Don't use assert here because it's fairly harmless in most cases, * more an issue of correctness, something we should avoid in general. */ - if (event->is_repeat && !ISKEYBOARD(event->type)) { + if ((event->flag & WM_EVENT_IS_REPEAT) && !ISKEYBOARD(event->type)) { printf("%s: 'is_repeat=true' for non-keyboard event, this should not happen.\n", __func__); WM_event_print(event); } @@ -739,7 +739,7 @@ void wm_event_handler_ui_cancel_ex(bContext *C, wm_event_init_from_window(win, &event); event.type = EVT_BUT_CANCEL; event.val = reactivate_button ? 0 : 1; - event.is_repeat = false; + event.flag = 0; handler->handle_fn(C, &event, handler->user_data); } } @@ -1982,7 +1982,7 @@ static bool wm_eventmatch(const wmEvent *winevent, const wmKeyMapItem *kmi) return false; } - if (winevent->is_repeat) { + if (winevent->flag & WM_EVENT_IS_REPEAT) { if (kmi->flag & KMI_REPEAT_IGNORE) { return false; } @@ -2029,29 +2029,41 @@ static bool wm_eventmatch(const wmEvent *winevent, const wmKeyMapItem *kmi) } } + if (kmi->val == KM_CLICK_DRAG) { + if (kmi->direction != KM_ANY) { + if (kmi->direction != winevent->direction) { + return false; + } + } + } + + const bool shift = (winevent->modifier & KM_SHIFT) != 0; + const bool ctrl = (winevent->modifier & KM_CTRL) != 0; + const bool alt = (winevent->modifier & KM_ALT) != 0; + const bool oskey = (winevent->modifier & KM_OSKEY) != 0; + /* Modifiers also check bits, so it allows modifier order. * Account for rare case of when these keys are used as the 'type' not as modifiers. */ if (kmi->shift != KM_ANY) { - if ((winevent->shift != kmi->shift) && !(winevent->shift & kmi->shift) && + if ((shift != kmi->shift) && !(shift & kmi->shift) && !ELEM(winevent->type, EVT_LEFTSHIFTKEY, EVT_RIGHTSHIFTKEY)) { return false; } } if (kmi->ctrl != KM_ANY) { - if (winevent->ctrl != kmi->ctrl && !(winevent->ctrl & kmi->ctrl) && + if (ctrl != kmi->ctrl && !(ctrl & kmi->ctrl) && !ELEM(winevent->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY)) { return false; } } if (kmi->alt != KM_ANY) { - if (winevent->alt != kmi->alt && !(winevent->alt & kmi->alt) && + if (alt != kmi->alt && !(alt & kmi->alt) && !ELEM(winevent->type, EVT_LEFTALTKEY, EVT_RIGHTALTKEY)) { return false; } } if (kmi->oskey != KM_ANY) { - if (winevent->oskey != kmi->oskey && !(winevent->oskey & kmi->oskey) && - (winevent->type != EVT_OSKEY)) { + if (oskey != kmi->oskey && !(oskey & kmi->oskey) && (winevent->type != EVT_OSKEY)) { return false; } } @@ -2771,7 +2783,7 @@ static int wm_handlers_do_gizmo_handler(bContext *C, { /* Drag events use the previous click location to highlight the gizmos, * Get the highlight again in case the user dragged off the gizmo. */ - const bool is_event_drag = ISTWEAK(event->type) || (event->val == KM_CLICK_DRAG); + const bool is_event_drag = (event->val == KM_CLICK_DRAG); const bool is_event_modifier = ISKEYMODIFIER(event->type); /* Only keep the highlight if the gizmo becomes modal as result of event handling. * Without this check, even un-handled drag events will set the highlight if the drag @@ -2882,15 +2894,10 @@ static int wm_handlers_do_gizmo_handler(bContext *C, wmEvent event_test_click_drag = *event; event_test_click_drag.val = KM_CLICK_DRAG; - wmEvent event_test_tweak = *event; - event_test_tweak.type = EVT_TWEAK_L + (event->type - LEFTMOUSE); - event_test_tweak.val = KM_ANY; - LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) { if ((kmi->flag & KMI_INACTIVE) == 0) { if (wm_eventmatch(&event_test_click, kmi) || - wm_eventmatch(&event_test_click_drag, kmi) || - wm_eventmatch(&event_test_tweak, kmi)) { + wm_eventmatch(&event_test_click_drag, kmi)) { wmOperatorType *ot = WM_operatortype_find(kmi->idname, 0); if (WM_operator_poll_context(C, ot, WM_OP_INVOKE_DEFAULT)) { is_event_handle_all = true; @@ -3151,39 +3158,45 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) } if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { - /* Test for CLICK_DRAG events. */ - if (wm_action_not_handled(action)) { - if (win->event_queue_check_drag) { - if (WM_event_drag_test(event, event->prev_click_xy)) { - win->event_queue_check_drag_handled = true; - - int xy[2] = {UNPACK2(event->xy)}; - short val = event->val; - short type = event->type; - - copy_v2_v2_int(event->xy, event->prev_click_xy); - event->val = KM_CLICK_DRAG; - event->type = event->prev_type; - - CLOG_INFO(WM_LOG_HANDLERS, 1, "handling PRESS_DRAG"); + /* Test for #WM_CLICK_DRAG events. */ + + /* NOTE(@campbellbarton): Ignore `action` so drag can be used for editors that use both click + * selection and passing through the drag action to box select. See #WM_generic_select_modal. + * In the case of marker select-drag the combinations of (pass-through / finished / modal) + * can accumulate to have flags set that they can't be properly interpreted here. + * Instead `win->event_queue_check_drag` is cleared in `wm_event_do_handlers`. */ + if (win->event_queue_check_drag) { + if (WM_event_drag_test(event, event->prev_click_xy)) { + win->event_queue_check_drag_handled = true; + const int direction = WM_event_drag_direction(event); + + const int prev_xy[2] = {UNPACK2(event->xy)}; + const short prev_val = event->val; + const short prev_type = event->type; + const uint8_t prev_modifier = event->modifier; + const short prev_keymodifier = event->keymodifier; + + copy_v2_v2_int(event->xy, event->prev_click_xy); + event->val = KM_CLICK_DRAG; + event->type = event->prev_click_type; + event->modifier = event->prev_click_modifier; + event->keymodifier = event->prev_click_keymodifier; + event->direction = direction; + + CLOG_INFO(WM_LOG_HANDLERS, 1, "handling PRESS_DRAG"); + + action |= wm_handlers_do_intern(C, win, event, handlers); + + event->direction = 0; + event->keymodifier = prev_keymodifier; + event->modifier = prev_modifier; + event->val = prev_val; + event->type = prev_type; + copy_v2_v2_int(event->xy, prev_xy); - action |= wm_handlers_do_intern(C, win, event, handlers); - - event->val = val; - event->type = type; - copy_v2_v2_int(event->xy, xy); - - win->event_queue_check_click = false; - if (!wm_action_not_handled(action)) { - /* Only disable when handled as other handlers may use this drag event. */ - win->event_queue_check_drag = false; - } - } + win->event_queue_check_click = false; } } - else { - win->event_queue_check_drag = false; - } } else if (ISMOUSE_BUTTON(event->type) || ISKEYBOARD(event->type)) { /* All events that don't set wmEvent.prev_type must be ignored. */ @@ -3194,17 +3207,26 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) * wasn't handled, the KM_RELEASE will become a KM_CLICK */ if (event->val == KM_PRESS) { - if (event->is_repeat == false) { + if ((event->flag & WM_EVENT_IS_REPEAT) == 0) { win->event_queue_check_click = true; win->event_queue_check_drag = true; win->event_queue_check_drag_handled = false; } } else if (event->val == KM_RELEASE) { - win->event_queue_check_drag = false; + if (win->event_queue_check_drag) { + if ((event->prev_click_type != event->type) && + (ISKEYMODIFIER(event->type) || (event->type == event->prev_click_keymodifier))) { + /* Support releasing modifier keys without canceling the drag event, see T89989. + * NOTE: this logic is replicated for tweak gestures. */ + } + else { + win->event_queue_check_drag = false; + } + } } - if (event->prev_type == event->type) { + if (event->prev_click_type == event->type) { if (event->val == KM_RELEASE) { if (event->prev_val == KM_PRESS) { @@ -3245,7 +3267,6 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) } else { win->event_queue_check_click = false; - win->event_queue_check_drag = false; } } else if (ISMOUSE_WHEEL(event->type) || ISMOUSE_GESTURE(event->type)) { @@ -3692,8 +3713,10 @@ void wm_event_do_handlers(bContext *C) /* Check dragging, creates new event or frees, adds draw tag. */ wm_event_drag_and_drop_test(wm, win, event); - /* Builtin tweak, if action is break it removes tweak. */ - wm_tweakevent_test(C, event, action); + /* Builtin drag: #KM_CLICK_DRAG. */ + if (action & WM_HANDLER_BREAK) { + win->event_queue_check_drag = false; + } if ((action & WM_HANDLER_BREAK) == 0) { /* NOTE: setting subwin active should be done here, after modal handlers have been done. */ @@ -3795,7 +3818,7 @@ void wm_event_do_handlers(bContext *C) tevent.type = MOUSEMOVE; tevent.prev_xy[0] = tevent.xy[0]; tevent.prev_xy[1] = tevent.xy[1]; - tevent.is_repeat = false; + tevent.flag = 0; wm_event_add(win, &tevent); win->addmousemove = 0; } @@ -4487,19 +4510,18 @@ static void wm_eventemulation(wmEvent *event, bool test_only) if (U.flag & USER_TWOBUTTONMOUSE) { if (event->type == LEFTMOUSE) { - short *mod = ( + const uint8_t mod_test = ( #if !defined(WIN32) - (U.mouse_emulate_3_button_modifier == USER_EMU_MMB_MOD_OSKEY) ? &event->oskey : - &event->alt + (U.mouse_emulate_3_button_modifier == USER_EMU_MMB_MOD_OSKEY) ? KM_OSKEY : KM_ALT #else /* Disable for WIN32 for now because it accesses the start menu. */ - &event->alt + KM_ALT #endif ); if (event->val == KM_PRESS) { - if (*mod) { - *mod = 0; + if (event->modifier & mod_test) { + event->modifier &= ~mod_test; event->type = MIDDLEMOUSE; if (!test_only) { @@ -4511,7 +4533,7 @@ static void wm_eventemulation(wmEvent *event, bool test_only) /* Only send middle-mouse release if emulated. */ if (emulating_event == MIDDLEMOUSE) { event->type = MIDDLEMOUSE; - *mod = 0; + event->modifier &= ~mod_test; } if (!test_only) { @@ -4649,8 +4671,8 @@ static wmWindow *wm_event_cursor_other_windows(wmWindowManager *wm, wmWindow *wi } } - wmWindow *win_other = WM_window_find_under_cursor(wm, win, win, mval, mval); - if (win_other) { + wmWindow *win_other = WM_window_find_under_cursor(win, mval, mval); + if (win_other && win_other != win) { copy_v2_v2_int(event->xy, mval); return win_other; } @@ -4687,6 +4709,9 @@ static void wm_event_prev_values_set(wmEvent *event, wmEvent *event_state) static void wm_event_prev_click_set(wmEvent *event, wmEvent *event_state) { event->prev_click_time = event_state->prev_click_time = PIL_check_seconds_timer(); + event->prev_click_type = event_state->prev_click_type = event_state->type; + event->prev_click_modifier = event_state->prev_click_modifier = event_state->modifier; + event->prev_click_keymodifier = event_state->prev_click_keymodifier = event_state->keymodifier; event->prev_click_xy[0] = event_state->prev_click_xy[0] = event_state->xy[0]; event->prev_click_xy[1] = event_state->prev_click_xy[1] = event_state->xy[1]; } @@ -4700,7 +4725,7 @@ static wmEvent *wm_event_add_mousemove(wmWindow *win, const wmEvent *event) * them for better performance. */ if (event_last && event_last->type == MOUSEMOVE) { event_last->type = INBETWEEN_MOUSEMOVE; - event_last->is_repeat = false; + event_last->flag = 0; } wmEvent *event_new = wm_event_add(win, event); @@ -4752,7 +4777,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void /* Initialize and copy state (only mouse x y and modifiers). */ event = *event_state; - event.is_repeat = false; + event.flag = 0; /** * Always support accessing the last key press/release. This is set from `win->eventstate`, @@ -4850,7 +4875,9 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void event.val = KM_NOTHING; /* The direction is inverted from the device due to system preferences. */ - event.is_direction_inverted = pd->isDirectionInverted; + if (pd->isDirectionInverted) { + event.flag |= WM_EVENT_SCROLL_INVERT; + } wm_event_add_trackpad(win, &event, pd->deltaX, -pd->deltaY); break; @@ -4931,12 +4958,16 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void case GHOST_kEventKeyDown: case GHOST_kEventKeyUp: { GHOST_TEventKeyData *kd = customdata; - short keymodifier = KM_NOTHING; + /* Only copy these flags into the `event_state`. */ + const eWM_EventFlag event_state_flag_mask = WM_EVENT_IS_REPEAT; + bool keymodifier = 0; event.type = convert_key(kd->key); event.ascii = kd->ascii; /* Might be not NULL terminated. */ memcpy(event.utf8_buf, kd->utf8_buf, sizeof(event.utf8_buf)); - event.is_repeat = kd->is_repeat; + if (kd->is_repeat) { + event.flag |= WM_EVENT_IS_REPEAT; + } event.val = (type == GHOST_kEventKeyDown) ? KM_PRESS : KM_RELEASE; wm_eventemulation(&event, false); @@ -4945,7 +4976,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void /* Copy to event state. */ event_state->val = event.val; event_state->type = event.type; - event_state->is_repeat = event.is_repeat; + event_state->flag = (event.flag & event_state_flag_mask); /* Exclude arrow keys, esc, etc from text input. */ if (type == GHOST_kEventKeyUp) { @@ -4981,29 +5012,57 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void case EVT_LEFTSHIFTKEY: case EVT_RIGHTSHIFTKEY: if (event.val == KM_PRESS) { - keymodifier = KM_MOD_HELD; + keymodifier = true; + } + if (keymodifier) { + event.modifier |= KM_SHIFT; + event_state->modifier |= KM_SHIFT; + } + else { + event.modifier &= ~KM_SHIFT; + event_state->modifier &= ~KM_SHIFT; } - event.shift = event_state->shift = keymodifier; break; case EVT_LEFTCTRLKEY: case EVT_RIGHTCTRLKEY: if (event.val == KM_PRESS) { - keymodifier = KM_MOD_HELD; + keymodifier = true; + } + if (keymodifier) { + event.modifier |= KM_CTRL; + event_state->modifier |= KM_CTRL; + } + else { + event.modifier &= ~KM_CTRL; + event_state->modifier &= ~KM_CTRL; } - event.ctrl = event_state->ctrl = keymodifier; break; case EVT_LEFTALTKEY: case EVT_RIGHTALTKEY: if (event.val == KM_PRESS) { - keymodifier = KM_MOD_HELD; + keymodifier = true; + } + if (keymodifier) { + event.modifier |= KM_ALT; + event_state->modifier |= KM_ALT; + } + else { + event.modifier &= ~KM_ALT; + event_state->modifier &= ~KM_ALT; } - event.alt = event_state->alt = keymodifier; break; case EVT_OSKEY: if (event.val == KM_PRESS) { - keymodifier = KM_MOD_HELD; + keymodifier = true; + } + if (keymodifier) { + event.modifier |= KM_OSKEY; + event_state->modifier |= KM_OSKEY; + } + else { + event.modifier &= ~KM_OSKEY; + event_state->modifier &= ~KM_OSKEY; } - event.oskey = event_state->oskey = keymodifier; break; default: if (event.val == KM_PRESS && event.keymodifier == 0) { @@ -5041,14 +5100,14 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void * XXX Keep global for now? */ if ((event.type == EVT_ESCKEY && event.val == KM_PRESS) && /* Check other modifiers because ms-windows uses these to bring up the task manager. */ - (event.shift == 0 && event.ctrl == 0 && event.alt == 0)) { + ((event.modifier & (KM_SHIFT | KM_CTRL | KM_ALT)) == 0)) { G.is_break = true; } /* Double click test - only for press. */ if (event.val == KM_PRESS) { /* Don't reset timer & location when holding the key generates repeat events. */ - if (event.is_repeat == false) { + if ((event.flag & WM_EVENT_IS_REPEAT) == 0) { wm_event_prev_click_set(&event, event_state); } } @@ -5169,7 +5228,7 @@ void wm_event_add_xrevent(wmWindow *win, wmXrActionData *actiondata, short val) wmEvent event = { .type = EVT_XR_ACTION, .val = val, - .is_repeat = false, + .flag = 0, .custom = EVT_DATA_XR, .customdata = actiondata, .customdata_free = true, @@ -5301,9 +5360,7 @@ wmKeyMapItem *WM_event_match_keymap_item_from_handlers( /** State storage to detect changes between calls to refresh the information. */ struct CursorKeymapInfo_State { - struct { - short shift, ctrl, alt, oskey; - } modifiers; + uint8_t modifier; short space_type; short region_type; /* Never use, just compare memory for changes. */ @@ -5326,10 +5383,7 @@ static void wm_event_cursor_store(struct CursorKeymapInfo_State *state, short region_type, const bToolRef *tref) { - state->modifiers.shift = event->shift; - state->modifiers.ctrl = event->ctrl; - state->modifiers.alt = event->alt; - state->modifiers.oskey = event->oskey; + state->modifier = event->modifier; state->space_type = space_type; state->region_type = region_type; state->tref = tref ? *tref : (bToolRef){0}; @@ -5460,15 +5514,15 @@ void WM_window_cursor_keymap_status_refresh(bContext *C, wmWindow *win) } event_data[] = { {0, 0, LEFTMOUSE, KM_PRESS}, {0, 0, LEFTMOUSE, KM_CLICK}, - {0, 1, EVT_TWEAK_L, KM_ANY}, + {0, 0, LEFTMOUSE, KM_CLICK_DRAG}, {1, 0, MIDDLEMOUSE, KM_PRESS}, {1, 0, MIDDLEMOUSE, KM_CLICK}, - {1, 1, EVT_TWEAK_M, KM_ANY}, + {1, 0, MIDDLEMOUSE, KM_CLICK_DRAG}, {2, 0, RIGHTMOUSE, KM_PRESS}, {2, 0, RIGHTMOUSE, KM_CLICK}, - {2, 1, EVT_TWEAK_R, KM_ANY}, + {2, 0, RIGHTMOUSE, KM_CLICK_DRAG}, }; for (int button_index = 0; button_index < 3; button_index++) { diff --git a/source/blender/windowmanager/intern/wm_gesture.c b/source/blender/windowmanager/intern/wm_gesture.c index 581c5f8a198..a6fbad8b171 100644 --- a/source/blender/windowmanager/intern/wm_gesture.c +++ b/source/blender/windowmanager/intern/wm_gesture.c @@ -42,6 +42,8 @@ wmGesture *WM_gesture_new(wmWindow *window, const ARegion *region, const wmEvent gesture->type = type; gesture->event_type = event->type; + gesture->event_modifier = event->modifier; + gesture->event_keymodifier = event->keymodifier; gesture->winrct = region->winrct; gesture->user_data.use_free = true; /* Free if userdata is set. */ gesture->modal_state = GESTURE_MODAL_NOP; @@ -50,7 +52,6 @@ wmGesture *WM_gesture_new(wmWindow *window, const ARegion *region, const wmEvent if (ELEM(type, WM_GESTURE_RECT, WM_GESTURE_CROSS_RECT, - WM_GESTURE_TWEAK, WM_GESTURE_CIRCLE, WM_GESTURE_STRAIGHTLINE)) { rcti *rect = MEM_callocN(sizeof(rcti), "gesture rect new"); @@ -81,9 +82,6 @@ wmGesture *WM_gesture_new(wmWindow *window, const ARegion *region, const wmEvent void WM_gesture_end(wmWindow *win, wmGesture *gesture) { - if (win->tweak == gesture) { - win->tweak = NULL; - } BLI_remlink(&win->gesture, gesture); MEM_freeN(gesture->customdata); WM_generic_user_data_free(&gesture->user_data); @@ -112,74 +110,6 @@ bool WM_gesture_is_modal_first(const wmGesture *gesture) return (gesture->is_active_prev == false); } -int wm_gesture_evaluate(wmGesture *gesture, const wmEvent *event) -{ - if (gesture->type == WM_GESTURE_TWEAK) { - rcti *rect = gesture->customdata; - const int delta[2] = { - BLI_rcti_size_x(rect), - BLI_rcti_size_y(rect), - }; - - if (WM_event_drag_test_with_delta(event, delta)) { - int theta = round_fl_to_int(4.0f * atan2f((float)delta[1], (float)delta[0]) / (float)M_PI); - int val = EVT_GESTURE_W; - - if (theta == 0) { - val = EVT_GESTURE_E; - } - else if (theta == 1) { - val = EVT_GESTURE_NE; - } - else if (theta == 2) { - val = EVT_GESTURE_N; - } - else if (theta == 3) { - val = EVT_GESTURE_NW; - } - else if (theta == -1) { - val = EVT_GESTURE_SE; - } - else if (theta == -2) { - val = EVT_GESTURE_S; - } - else if (theta == -3) { - val = EVT_GESTURE_SW; - } - -#if 0 - /* debug */ - if (val == 1) { - printf("tweak north\n"); - } - if (val == 2) { - printf("tweak north-east\n"); - } - if (val == 3) { - printf("tweak east\n"); - } - if (val == 4) { - printf("tweak south-east\n"); - } - if (val == 5) { - printf("tweak south\n"); - } - if (val == 6) { - printf("tweak south-west\n"); - } - if (val == 7) { - printf("tweak west\n"); - } - if (val == 8) { - printf("tweak north-west\n"); - } -#endif - return val; - } - } - return 0; -} - /* ******************* gesture draw ******************* */ static void wm_gesture_draw_line_active_side(rcti *rect, const bool flip) @@ -509,11 +439,6 @@ void wm_gesture_draw(wmWindow *win) if (gt->type == WM_GESTURE_RECT) { wm_gesture_draw_rect(gt); } -#if 0 - else if (gt->type == WM_GESTURE_TWEAK) { - wm_gesture_draw_line(gt); - } -#endif else if (gt->type == WM_GESTURE_CIRCLE) { wm_gesture_draw_circle(gt); } diff --git a/source/blender/windowmanager/intern/wm_gesture_ops.c b/source/blender/windowmanager/intern/wm_gesture_ops.c index 2a27a8df411..1fdc8bbe2c8 100644 --- a/source/blender/windowmanager/intern/wm_gesture_ops.c +++ b/source/blender/windowmanager/intern/wm_gesture_ops.c @@ -471,118 +471,6 @@ void WM_OT_circle_gesture(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Tweak Gesture - * \{ */ - -static void gesture_tweak_modal(bContext *C, const wmEvent *event) -{ - wmWindow *window = CTX_wm_window(C); - wmGesture *gesture = window->tweak; - rcti *rect = gesture->customdata; - bool gesture_end = false; - - switch (event->type) { - case MOUSEMOVE: - case INBETWEEN_MOUSEMOVE: { - - rect->xmax = event->xy[0] - gesture->winrct.xmin; - rect->ymax = event->xy[1] - gesture->winrct.ymin; - - const int val = wm_gesture_evaluate(gesture, event); - if (val != 0) { - wmEvent tevent; - - wm_event_init_from_window(window, &tevent); - /* We want to get coord from start of drag, - * not from point where it becomes a tweak event, see T40549. */ - tevent.xy[0] = rect->xmin + gesture->winrct.xmin; - tevent.xy[1] = rect->ymin + gesture->winrct.ymin; - if (gesture->event_type == LEFTMOUSE) { - tevent.type = EVT_TWEAK_L; - } - else if (gesture->event_type == RIGHTMOUSE) { - tevent.type = EVT_TWEAK_R; - } - else { - tevent.type = EVT_TWEAK_M; - } - tevent.val = val; - tevent.is_repeat = false; - /* mouse coords! */ - - /* important we add immediately after this event, so future mouse releases - * (which may be in the queue already), are handled in order, see T44740 */ - wm_event_add_ex(window, &tevent, event); - - gesture_end = true; - } - - break; - } - - case LEFTMOUSE: - case RIGHTMOUSE: - case MIDDLEMOUSE: - if (gesture->event_type == event->type) { - gesture_end = true; - - /* when tweak fails we should give the other keymap entries a chance */ - - /* XXX, assigning to readonly, BAD JUJU! */ - ((wmEvent *)event)->val = KM_RELEASE; - } - break; - default: - if (!ISTIMER(event->type) && event->type != EVENT_NONE) { - gesture_end = true; - } - break; - } - - if (gesture_end) { - /* Frees gesture itself, and unregisters from window. */ - WM_gesture_end(window, gesture); - - /* This isn't very nice but needed to redraw gizmos which are hidden while tweaking, - * See #WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK for details. */ - ARegion *region = CTX_wm_region(C); - if ((region != NULL) && (region->gizmo_map != NULL)) { - if (WM_gizmomap_tag_delay_refresh_for_tweak_check(region->gizmo_map)) { - ED_region_tag_redraw(region); - } - } - } -} - -void wm_tweakevent_test(bContext *C, const wmEvent *event, int action) -{ - wmWindow *win = CTX_wm_window(C); - - if (win->tweak == NULL) { - const ARegion *region = CTX_wm_region(C); - - if (region) { - if (event->val == KM_PRESS) { - if (ELEM(event->type, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE)) { - win->tweak = WM_gesture_new(win, region, event, WM_GESTURE_TWEAK); - } - } - } - } - else { - /* no tweaks if event was handled */ - if (action & WM_HANDLER_BREAK) { - WM_gesture_end(win, win->tweak); - } - else { - gesture_tweak_modal(C, event); - } - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Lasso Gesture * \{ */ diff --git a/source/blender/windowmanager/intern/wm_keymap.c b/source/blender/windowmanager/intern/wm_keymap.c index f7bc138f163..ffac585cde7 100644 --- a/source/blender/windowmanager/intern/wm_keymap.c +++ b/source/blender/windowmanager/intern/wm_keymap.c @@ -169,6 +169,7 @@ static bool wm_keymap_item_equals(wmKeyMapItem *a, wmKeyMapItem *b) return (wm_keymap_item_equals_result(a, b) && a->type == b->type && a->val == b->val && a->shift == b->shift && a->ctrl == b->ctrl && a->alt == b->alt && a->oskey == b->oskey && a->keymodifier == b->keymodifier && a->maptype == b->maptype && + ((a->val != KM_CLICK_DRAG) || (a->direction == b->direction)) && ((ISKEYBOARD(a->type) == 0) || (a->flag & KMI_REPEAT_IGNORE) == (b->flag & KMI_REPEAT_IGNORE))); } @@ -195,9 +196,6 @@ int WM_keymap_item_map_type_get(const wmKeyMapItem *kmi) if (ISKEYBOARD(kmi->type)) { return KMI_TYPE_KEYBOARD; } - if (ISTWEAK(kmi->type)) { - return KMI_TYPE_TWEAK; - } if (ISMOUSE(kmi->type)) { return KMI_TYPE_MOUSE; } @@ -459,11 +457,12 @@ bool WM_keymap_poll(bContext *C, wmKeyMap *keymap) } static void keymap_event_set( - wmKeyMapItem *kmi, short type, short val, int modifier, short keymodifier) + wmKeyMapItem *kmi, short type, short val, int modifier, short keymodifier, int direction) { kmi->type = type; kmi->val = val; kmi->keymodifier = keymodifier; + kmi->direction = direction; if (modifier == KM_ANY) { kmi->shift = kmi->ctrl = kmi->alt = kmi->oskey = KM_ANY; @@ -497,15 +496,20 @@ static void keymap_item_set_id(wmKeyMap *keymap, wmKeyMapItem *kmi) } } -wmKeyMapItem *WM_keymap_add_item( - wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier) +wmKeyMapItem *WM_keymap_add_item(wmKeyMap *keymap, + const char *idname, + int type, + int val, + int modifier, + int keymodifier, + int direction) { wmKeyMapItem *kmi = MEM_callocN(sizeof(wmKeyMapItem), "keymap entry"); BLI_addtail(&keymap->items, kmi); BLI_strncpy(kmi->idname, idname, OP_MAX_TYPENAME); - keymap_event_set(kmi, type, val, modifier, keymodifier); + keymap_event_set(kmi, type, val, modifier, keymodifier, direction); wm_keymap_item_properties_set(kmi); keymap_item_set_id(keymap, kmi); @@ -919,14 +923,14 @@ wmKeyMap *WM_modalkeymap_find(wmKeyConfig *keyconf, const char *idname) } wmKeyMapItem *WM_modalkeymap_add_item( - wmKeyMap *km, int type, int val, int modifier, int keymodifier, int value) + wmKeyMap *km, int type, int val, int modifier, int keymodifier, int direction, int value) { wmKeyMapItem *kmi = MEM_callocN(sizeof(wmKeyMapItem), "keymap entry"); BLI_addtail(&km->items, kmi); kmi->propvalue = value; - keymap_event_set(kmi, type, val, modifier, keymodifier); + keymap_event_set(kmi, type, val, modifier, keymodifier, direction); keymap_item_set_id(km, kmi); @@ -935,15 +939,20 @@ wmKeyMapItem *WM_modalkeymap_add_item( return kmi; } -wmKeyMapItem *WM_modalkeymap_add_item_str( - wmKeyMap *km, int type, int val, int modifier, int keymodifier, const char *value) +wmKeyMapItem *WM_modalkeymap_add_item_str(wmKeyMap *km, + int type, + int val, + int modifier, + int keymodifier, + int direction, + const char *value) { wmKeyMapItem *kmi = MEM_callocN(sizeof(wmKeyMapItem), "keymap entry"); BLI_addtail(&km->items, kmi); BLI_strncpy(kmi->propvalue_str, value, sizeof(kmi->propvalue_str)); - keymap_event_set(kmi, type, val, modifier, keymodifier); + keymap_event_set(kmi, type, val, modifier, keymodifier, direction); keymap_item_set_id(km, kmi); @@ -1730,6 +1739,9 @@ bool WM_keymap_item_compare(const wmKeyMapItem *k1, const wmKeyMapItem *k2) if (k1->val != k2->val) { return 0; } + if (k1->val == KM_CLICK_DRAG && (k1->direction != k2->direction)) { + return 0; + } } if (k1->shift != KM_ANY && k2->shift != KM_ANY && k1->shift != k2->shift) { diff --git a/source/blender/windowmanager/intern/wm_keymap_utils.c b/source/blender/windowmanager/intern/wm_keymap_utils.c index 162246798de..24c221221d1 100644 --- a/source/blender/windowmanager/intern/wm_keymap_utils.c +++ b/source/blender/windowmanager/intern/wm_keymap_utils.c @@ -29,64 +29,64 @@ /** \name Wrappers for #WM_keymap_add_item * \{ */ -wmKeyMapItem *WM_keymap_add_menu( - wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier) +wmKeyMapItem *WM_keymap_add_menu(wmKeyMap *keymap, + const char *idname, + int type, + int val, + int modifier, + int keymodifier, + int direction) { wmKeyMapItem *kmi = WM_keymap_add_item( - keymap, "WM_OT_call_menu", type, val, modifier, keymodifier); + keymap, "WM_OT_call_menu", type, val, modifier, keymodifier, direction); RNA_string_set(kmi->ptr, "name", idname); return kmi; } -wmKeyMapItem *WM_keymap_add_menu_pie( - wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier) +wmKeyMapItem *WM_keymap_add_menu_pie(wmKeyMap *keymap, + const char *idname, + int type, + int val, + int modifier, + int keymodifier, + int direction) { wmKeyMapItem *kmi = WM_keymap_add_item( - keymap, "WM_OT_call_menu_pie", type, val, modifier, keymodifier); + keymap, "WM_OT_call_menu_pie", type, val, modifier, keymodifier, direction); RNA_string_set(kmi->ptr, "name", idname); return kmi; } -wmKeyMapItem *WM_keymap_add_panel( - wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier) +wmKeyMapItem *WM_keymap_add_panel(wmKeyMap *keymap, + const char *idname, + int type, + int val, + int modifier, + int keymodifier, + int direction) { wmKeyMapItem *kmi = WM_keymap_add_item( - keymap, "WM_OT_call_panel", type, val, modifier, keymodifier); + keymap, "WM_OT_call_panel", type, val, modifier, keymodifier, direction); RNA_string_set(kmi->ptr, "name", idname); /* TODO: we might want to disable this. */ RNA_boolean_set(kmi->ptr, "keep_open", false); return kmi; } -wmKeyMapItem *WM_keymap_add_tool( - wmKeyMap *keymap, const char *idname, int type, int val, int modifier, int keymodifier) +wmKeyMapItem *WM_keymap_add_tool(wmKeyMap *keymap, + const char *idname, + int type, + int val, + int modifier, + int keymodifier, + int direction) { wmKeyMapItem *kmi = WM_keymap_add_item( - keymap, "WM_OT_tool_set_by_id", type, val, modifier, keymodifier); + keymap, "WM_OT_tool_set_by_id", type, val, modifier, keymodifier, direction); RNA_string_set(kmi->ptr, "name", idname); return kmi; } -void WM_keymap_add_context_enum_set_items(wmKeyMap *keymap, - const EnumPropertyItem *items, - const char *data_path, - int type_start, - int val, - int modifier, - int keymodifier) -{ - for (int i = 0, type_offset = 0; items[i].identifier; i++) { - if (items[i].identifier[0] == '\0') { - continue; - } - wmKeyMapItem *kmi = WM_keymap_add_item( - keymap, "WM_OT_context_set_enum", type_start + type_offset, val, modifier, keymodifier); - RNA_string_set(kmi->ptr, "data_path", data_path); - RNA_string_set(kmi->ptr, "value", items[i].identifier); - type_offset += 1; - } -} - /** \} */ /* -------------------------------------------------------------------- */ @@ -106,6 +106,9 @@ wmKeyMap *WM_keymap_guess_from_context(const bContext *C) case CTX_MODE_EDIT_CURVE: km_id = "Curve"; break; + case CTX_MODE_EDIT_CURVES: + km_id = "Curves"; + break; case CTX_MODE_EDIT_SURFACE: km_id = "Curve"; break; @@ -158,7 +161,7 @@ wmKeyMap *WM_keymap_guess_from_context(const bContext *C) km_id = "Grease Pencil Stroke Vertex Mode"; break; case CTX_MODE_SCULPT_CURVES: - km_id = "Curves Sculpt Mode"; + km_id = "Curves Sculpt"; break; } } diff --git a/source/blender/windowmanager/intern/wm_operator_utils.c b/source/blender/windowmanager/intern/wm_operator_utils.c index 6f3f42bee53..5a817075cd5 100644 --- a/source/blender/windowmanager/intern/wm_operator_utils.c +++ b/source/blender/windowmanager/intern/wm_operator_utils.c @@ -115,11 +115,11 @@ static bool interactive_value_update(ValueInteraction *inter, (((float)(mval_curr - mval_init) / inter->context_vars.region->winx) * value_range)) * value_scale; - if (event->ctrl) { + if (event->modifier & KM_CTRL) { const double snap = 0.1; value_delta = (float)roundf((double)value_delta / snap) * snap; } - if (event->shift) { + if (event->modifier & KM_SHIFT) { value_delta *= 0.1f; } const float value_final = inter->init.prop_value + value_delta; @@ -133,8 +133,8 @@ static bool interactive_value_update(ValueInteraction *inter, } inter->prev.prop_value = value_final; - inter->prev.is_snap = event->ctrl; - inter->prev.is_precise = event->shift; + inter->prev.is_snap = (event->modifier & KM_CTRL) != 0; + inter->prev.is_precise = (event->modifier & KM_SHIFT) != 0; *r_value_final = value_final; return changed; diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index a476fb4fa13..7e680af4537 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -911,7 +911,6 @@ int WM_generic_select_modal(bContext *C, wmOperator *op, const wmEvent *event) ret_value = op->type->exec(C, op); OPERATOR_RETVAL_CHECK(ret_value); - op->customdata = POINTER_FROM_INT((int)event->type); if (ret_value & OPERATOR_RUNNING_MODAL) { WM_event_add_modal_handler(C, op); @@ -2822,7 +2821,7 @@ static int radial_control_modal(bContext *C, wmOperator *op, const wmEvent *even float numValue; /* TODO: fix hardcoded events */ - bool snap = event->ctrl != 0; + bool snap = (event->modifier & KM_CTRL) != 0; /* Modal numinput active, try to handle numeric inputs first... */ if (event->val == KM_PRESS && has_numInput && handleNumInput(C, &rc->num_input, event)) { diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c index 2ee608c0755..95879829d42 100644 --- a/source/blender/windowmanager/intern/wm_playanim.c +++ b/source/blender/windowmanager/intern/wm_playanim.c @@ -483,7 +483,6 @@ static void draw_display_buffer(PlayState *ps, ImBuf *ibuf) if (!glsl_used) { immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_COLOR); immUniformColor3f(1.0f, 1.0f, 1.0f); - immUniform1i("image", 0); } immBegin(GPU_PRIM_TRI_FAN, 4); diff --git a/source/blender/windowmanager/intern/wm_stereo.c b/source/blender/windowmanager/intern/wm_stereo.c index 6bdd11df776..182308cbe5e 100644 --- a/source/blender/windowmanager/intern/wm_stereo.c +++ b/source/blender/windowmanager/intern/wm_stereo.c @@ -67,7 +67,7 @@ void wm_stereo3d_draw_sidebyside(wmWindow *win, int view) const float halfx = GLA_PIXEL_OFS / sizex; const float halfy = GLA_PIXEL_OFS / sizex; - immUniform1i("image", 0); /* texture is already bound to GL_TEXTURE0 unit */ + /* Texture is already bound to GL_TEXTURE0 unit. */ immBegin(GPU_PRIM_TRI_FAN, 4); @@ -111,7 +111,7 @@ void wm_stereo3d_draw_topbottom(wmWindow *win, int view) const float halfx = GLA_PIXEL_OFS / sizex; const float halfy = GLA_PIXEL_OFS / sizex; - immUniform1i("image", 0); /* texture is already bound to GL_TEXTURE0 unit */ + /* Texture is already bound to GL_TEXTURE0 unit. */ immBegin(GPU_PRIM_TRI_FAN, 4); diff --git a/source/blender/windowmanager/intern/wm_toolsystem.c b/source/blender/windowmanager/intern/wm_toolsystem.c index 4ae935b14f2..51e4bc9faa8 100644 --- a/source/blender/windowmanager/intern/wm_toolsystem.c +++ b/source/blender/windowmanager/intern/wm_toolsystem.c @@ -663,6 +663,8 @@ static const char *toolsystem_default_tool(const bToolKey *tkey) return "builtin_brush.Weight"; case CTX_MODE_VERTEX_GPENCIL: return "builtin_brush.Draw"; + case CTX_MODE_SCULPT_CURVES: + return "builtin_brush.Test 1"; /* end temporary hack. */ case CTX_MODE_PARTICLE: @@ -804,13 +806,34 @@ void WM_toolsystem_do_msg_notify_tag_refresh(bContext *C, WM_toolsystem_refresh_screen_area(workspace, view_layer, area); } +static IDProperty *idprops_ensure_named_group(IDProperty *group, const char *idname) +{ + IDProperty *prop = IDP_GetPropertyFromGroup(group, idname); + if ((prop == NULL) || (prop->type != IDP_GROUP)) { + IDPropertyTemplate val = {0}; + prop = IDP_New(IDP_GROUP, &val, __func__); + STRNCPY(prop->name, idname); + IDP_ReplaceInGroup_ex(group, prop, NULL); + } + return prop; +} + +IDProperty *WM_toolsystem_ref_properties_get_idprops(bToolRef *tref) +{ + IDProperty *group = tref->properties; + if (group == NULL) { + return NULL; + } + return IDP_GetPropertyFromGroup(group, tref->idname); +} + IDProperty *WM_toolsystem_ref_properties_ensure_idprops(bToolRef *tref) { if (tref->properties == NULL) { IDPropertyTemplate val = {0}; - tref->properties = IDP_New(IDP_GROUP, &val, "wmOperatorProperties"); + tref->properties = IDP_New(IDP_GROUP, &val, __func__); } - return tref->properties; + return idprops_ensure_named_group(tref->properties, tref->idname); } bool WM_toolsystem_ref_properties_get_ex(bToolRef *tref, @@ -818,7 +841,7 @@ bool WM_toolsystem_ref_properties_get_ex(bToolRef *tref, StructRNA *type, PointerRNA *r_ptr) { - IDProperty *group = tref->properties; + IDProperty *group = WM_toolsystem_ref_properties_get_idprops(tref); IDProperty *prop = group ? IDP_GetPropertyFromGroup(group, idname) : NULL; RNA_pointer_create(NULL, type, prop, r_ptr); return (prop != NULL); @@ -830,17 +853,7 @@ void WM_toolsystem_ref_properties_ensure_ex(bToolRef *tref, PointerRNA *r_ptr) { IDProperty *group = WM_toolsystem_ref_properties_ensure_idprops(tref); - IDProperty *prop = IDP_GetPropertyFromGroup(group, idname); - if (prop == NULL) { - IDPropertyTemplate val = {0}; - prop = IDP_New(IDP_GROUP, &val, "wmGenericProperties"); - STRNCPY(prop->name, idname); - IDP_ReplaceInGroup_ex(group, prop, NULL); - } - else { - BLI_assert(prop->type == IDP_GROUP); - } - + IDProperty *prop = idprops_ensure_named_group(group, idname); RNA_pointer_create(NULL, type, prop, r_ptr); } @@ -857,8 +870,9 @@ void WM_toolsystem_ref_properties_init_for_keymap(bToolRef *tref, IDPropertyTemplate val = {0}; dst_ptr->data = IDP_New(IDP_GROUP, &val, "wmOpItemProp"); } - if (tref->properties != NULL) { - IDProperty *prop = IDP_GetPropertyFromGroup(tref->properties, ot->idname); + IDProperty *group = WM_toolsystem_ref_properties_get_idprops(tref); + if (group != NULL) { + IDProperty *prop = IDP_GetPropertyFromGroup(group, ot->idname); if (prop) { /* Important key-map items properties don't get overwritten by the tools. * - When a key-map item doesn't set a property, the tool-systems is used. diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index a983150b504..e93ffe48aba 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -1104,10 +1104,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr win->active = 0; /* XXX */ /* clear modifiers for inactive windows */ - win->eventstate->alt = 0; - win->eventstate->ctrl = 0; - win->eventstate->shift = 0; - win->eventstate->oskey = 0; + win->eventstate->modifier = 0; win->eventstate->keymodifier = 0; break; @@ -1138,7 +1135,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr kdata.ascii = '\0'; kdata.utf8_buf[0] = '\0'; - if (win->eventstate->shift) { + if (win->eventstate->modifier & KM_SHIFT) { if ((keymodifier & KM_SHIFT) == 0) { kdata.key = GHOST_kKeyLeftShift; wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, &kdata); @@ -1147,11 +1144,11 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr #ifdef USE_WIN_ACTIVATE else { if (keymodifier & KM_SHIFT) { - win->eventstate->shift = KM_MOD_HELD; + win->eventstate->modifier |= KM_SHIFT; } } #endif - if (win->eventstate->ctrl) { + if (win->eventstate->modifier & KM_CTRL) { if ((keymodifier & KM_CTRL) == 0) { kdata.key = GHOST_kKeyLeftControl; wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, &kdata); @@ -1160,11 +1157,11 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr #ifdef USE_WIN_ACTIVATE else { if (keymodifier & KM_CTRL) { - win->eventstate->ctrl = KM_MOD_HELD; + win->eventstate->modifier |= KM_CTRL; } } #endif - if (win->eventstate->alt) { + if (win->eventstate->modifier & KM_ALT) { if ((keymodifier & KM_ALT) == 0) { kdata.key = GHOST_kKeyLeftAlt; wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, &kdata); @@ -1173,11 +1170,11 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr #ifdef USE_WIN_ACTIVATE else { if (keymodifier & KM_ALT) { - win->eventstate->alt = KM_MOD_HELD; + win->eventstate->modifier |= KM_ALT; } } #endif - if (win->eventstate->oskey) { + if (win->eventstate->modifier & KM_OSKEY) { if ((keymodifier & KM_OSKEY) == 0) { kdata.key = GHOST_kKeyOS; wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, &kdata); @@ -1186,7 +1183,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr #ifdef USE_WIN_ACTIVATE else { if (keymodifier & KM_OSKEY) { - win->eventstate->oskey = KM_MOD_HELD; + win->eventstate->modifier |= KM_OSKEY; } } #endif @@ -1216,7 +1213,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr wm_event_init_from_window(win, &event); event.type = MOUSEMOVE; copy_v2_v2_int(event.prev_xy, event.xy); - event.is_repeat = false; + event.flag = 0; wm_event_add(win, &event); @@ -1347,7 +1344,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr /* activate region */ event.type = MOUSEMOVE; copy_v2_v2_int(event.prev_xy, event.xy); - event.is_repeat = false; + event.flag = 0; /* No context change! C->wm->windrawable is drawable, or for area queues. */ wm->winactive = win; @@ -1488,7 +1485,7 @@ static bool wm_window_timer(const bContext *C) event.type = wt->event_type; event.val = KM_NOTHING; event.keymodifier = 0; - event.is_repeat = false; + event.flag = 0; event.custom = EVT_DATA_TIMER; event.customdata = wt; wm_event_add(win, &event); @@ -1844,56 +1841,23 @@ bool wm_window_get_swap_interval(wmWindow *win, int *intervalOut) /** \name Find Window Utility * \{ */ -static void wm_window_desktop_pos_get(const wmWindow *win, - const int screen_pos[2], - int r_desk_pos[2]) +wmWindow *WM_window_find_under_cursor(wmWindow *win, const int mval[2], int r_mval[2]) { - /* To desktop space. */ - r_desk_pos[0] = screen_pos[0] + (int)(U.pixelsize * win->posx); - r_desk_pos[1] = screen_pos[1] + (int)(U.pixelsize * win->posy); -} - -static void wm_window_screen_pos_get(const wmWindow *win, - const int desktop_pos[2], - int r_scr_pos[2]) -{ - /* To window space. */ - r_scr_pos[0] = desktop_pos[0] - (int)(U.pixelsize * win->posx); - r_scr_pos[1] = desktop_pos[1] - (int)(U.pixelsize * win->posy); -} - -wmWindow *WM_window_find_under_cursor(const wmWindowManager *wm, - const wmWindow *win_ignore, - const wmWindow *win, - const int mval[2], - int r_mval[2]) -{ - int desk_pos[2]; - wm_window_desktop_pos_get(win, mval, desk_pos); - - /* TODO: This should follow the order of the activated windows. - * The current solution is imperfect but usable in most cases. */ - LISTBASE_FOREACH (wmWindow *, win_iter, &wm->windows) { - if (win_iter == win_ignore) { - continue; - } - - if (win_iter->windowstate == GHOST_kWindowStateMinimized) { - continue; - } - - int scr_pos[2]; - wm_window_screen_pos_get(win_iter, desk_pos, scr_pos); + int tmp[2]; + copy_v2_v2_int(tmp, mval); + wm_cursor_position_to_ghost(win, &tmp[0], &tmp[1]); - if (scr_pos[0] >= 0 && scr_pos[1] >= 0 && scr_pos[0] <= WM_window_pixels_x(win_iter) && - scr_pos[1] <= WM_window_pixels_y(win_iter)) { + GHOST_WindowHandle ghostwin = GHOST_GetWindowUnderCursor(g_system, tmp[0], tmp[1]); - copy_v2_v2_int(r_mval, scr_pos); - return win_iter; - } + if (!ghostwin) { + return NULL; } - return NULL; + wmWindow *r_win = GHOST_GetWindowUserData(ghostwin); + wm_cursor_position_from_ghost(r_win, &tmp[0], &tmp[1]); + copy_v2_v2_int(r_mval, tmp); + + return r_win; } void WM_window_pixel_sample_read(const wmWindowManager *wm, diff --git a/source/blender/windowmanager/wm.h b/source/blender/windowmanager/wm.h index 68b16d46746..172a879e118 100644 --- a/source/blender/windowmanager/wm.h +++ b/source/blender/windowmanager/wm.h @@ -74,16 +74,8 @@ void wm_gesture_draw(struct wmWindow *win); /** * Tweak and line gestures. */ -int wm_gesture_evaluate(wmGesture *gesture, const struct wmEvent *event); void wm_gesture_tag_redraw(struct wmWindow *win); -/* wm_gesture_ops.c */ - -/** - * Standard tweak, called after window handlers passed on event. - */ -void wm_tweakevent_test(bContext *C, const wmEvent *event, int action); - /* wm_jobs.c */ /** diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h index 0ff181db9b1..d5c8c5022cc 100644 --- a/source/blender/windowmanager/wm_event_types.h +++ b/source/blender/windowmanager/wm_event_types.h @@ -325,16 +325,6 @@ enum { /* NOTE: these values are saved in key-map files, do not change them but just add new ones. */ - /* Tweak events: - * Sent as additional event with the mouse coordinates - * from where the initial click was placed. */ - - /* Tweak events for L M R mouse-buttons. */ - EVT_TWEAK_L = 0x5002, /* 20482 */ - EVT_TWEAK_M = 0x5003, /* 20483 */ - EVT_TWEAK_R = 0x5004, /* 20484 */ - /* 0x5010 (and lower) should be left to add other tweak types in the future. */ - /* 0x5011 is taken, see EVT_ACTIONZONE_FULLSCREEN */ /* Misc Blender internals: 0x502x */ @@ -394,9 +384,6 @@ enum { BUTTON6MOUSE, \ BUTTON7MOUSE)) -/** Test whether the event is tweak event. */ -#define ISTWEAK(event_type) ((event_type) >= EVT_TWEAK_L && (event_type) <= EVT_TWEAK_R) - /** Test whether the event is a NDOF event. */ #define ISNDOF(event_type) ((event_type) >= _NDOF_MIN && (event_type) <= _NDOF_MAX) @@ -408,15 +395,6 @@ enum { ((ISKEYBOARD(event_type) || ISMOUSE(event_type) || ISNDOF(event_type)) && \ (ISKEYMODIFIER(event_type) == false)) -/* Internal helpers. */ -#define _VA_IS_EVENT_MOD2(v, a) (CHECK_TYPE_INLINE(v, wmEvent *), ((v)->a)) -#define _VA_IS_EVENT_MOD3(v, a, b) (_VA_IS_EVENT_MOD2(v, a) || ((v)->b)) -#define _VA_IS_EVENT_MOD4(v, a, b, c) (_VA_IS_EVENT_MOD3(v, a, b) || ((v)->c)) -#define _VA_IS_EVENT_MOD5(v, a, b, c, d) (_VA_IS_EVENT_MOD4(v, a, b, c) || ((v)->d)) - -/** Reusable `IS_EVENT_MOD(event, shift, ctrl, alt, oskey)` macro. */ -#define IS_EVENT_MOD(...) VA_NARGS_CALL_OVERLOAD(_VA_IS_EVENT_MOD, __VA_ARGS__) - enum eEventType_Mask { /** #ISKEYMODIFIER */ EVT_TYPE_MASK_KEYBOARD_MODIFIER = (1 << 0), @@ -432,14 +410,11 @@ enum eEventType_Mask { EVT_TYPE_MASK_MOUSE = (1 << 5), /** #ISNDOF */ EVT_TYPE_MASK_NDOF = (1 << 6), - /** #ISTWEAK */ - EVT_TYPE_MASK_TWEAK = (1 << 7), /** #IS_EVENT_ACTIONZONE */ - EVT_TYPE_MASK_ACTIONZONE = (1 << 8), + EVT_TYPE_MASK_ACTIONZONE = (1 << 7), }; #define EVT_TYPE_MASK_ALL \ - (EVT_TYPE_MASK_KEYBOARD | EVT_TYPE_MASK_MOUSE | EVT_TYPE_MASK_NDOF | EVT_TYPE_MASK_TWEAK | \ - EVT_TYPE_MASK_ACTIONZONE) + (EVT_TYPE_MASK_KEYBOARD | EVT_TYPE_MASK_MOUSE | EVT_TYPE_MASK_NDOF | EVT_TYPE_MASK_ACTIONZONE) #define EVT_TYPE_MASK_HOTKEY_INCLUDE \ (EVT_TYPE_MASK_KEYBOARD | EVT_TYPE_MASK_MOUSE | EVT_TYPE_MASK_NDOF) @@ -454,18 +429,6 @@ bool WM_event_type_mask_test(int event_type, enum eEventType_Mask mask); * \{ */ /* Gestures */ -/* NOTE: these values are saved in keymap files, do not change them but just add new ones */ -enum { - /* Value of tweaks and line gestures. #KM_ANY (-1) works for this case too. */ - EVT_GESTURE_N = 1, - EVT_GESTURE_NE = 2, - EVT_GESTURE_E = 3, - EVT_GESTURE_SE = 4, - EVT_GESTURE_S = 5, - EVT_GESTURE_SW = 6, - EVT_GESTURE_W = 7, - EVT_GESTURE_NW = 8, -}; /* File select */ enum { diff --git a/source/blender/windowmanager/xr/intern/wm_xr_action.c b/source/blender/windowmanager/xr/intern/wm_xr_action.c index f6003428700..6750e7a7d77 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_action.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_action.c @@ -56,8 +56,7 @@ static wmXrActionSet *action_set_find(wmXrData *xr, const char *action_set_name) static wmXrAction *action_create(const char *action_name, eXrActionType type, - unsigned int count_subaction_paths, - const char **subaction_paths, + const ListBase *user_paths, wmOperatorType *ot, IDProperty *op_properties, const char *haptic_name, @@ -73,15 +72,16 @@ static wmXrAction *action_create(const char *action_name, strcpy(action->name, action_name); action->type = type; - const unsigned int count = count_subaction_paths; + const unsigned int count = (unsigned int)BLI_listbase_count(user_paths); + unsigned int subaction_idx = 0; action->count_subaction_paths = count; action->subaction_paths = MEM_mallocN(sizeof(*action->subaction_paths) * count, "XrAction_SubactionPaths"); - for (unsigned int i = 0; i < count; ++i) { - action->subaction_paths[i] = MEM_mallocN(strlen(subaction_paths[i]) + 1, - "XrAction_SubactionPath"); - strcpy(action->subaction_paths[i], subaction_paths[i]); + LISTBASE_FOREACH_INDEX (XrUserPath *, user_path, user_paths, subaction_idx) { + action->subaction_paths[subaction_idx] = MEM_mallocN(strlen(user_path->path) + 1, + "XrAction_SubactionPath"); + strcpy(action->subaction_paths[subaction_idx], user_path->path); } size_t size; @@ -140,10 +140,9 @@ static void action_destroy(void *val) MEM_SAFE_FREE(action->name); - const unsigned int count = action->count_subaction_paths; char **subaction_paths = action->subaction_paths; if (subaction_paths) { - for (unsigned int i = 0; i < count; ++i) { + for (unsigned int i = 0; i < action->count_subaction_paths; ++i) { MEM_SAFE_FREE(subaction_paths[i]); } MEM_freeN(subaction_paths); @@ -214,8 +213,7 @@ bool WM_xr_action_create(wmXrData *xr, const char *action_set_name, const char *action_name, eXrActionType type, - unsigned int count_subaction_paths, - const char **subaction_paths, + const ListBase *user_paths, wmOperatorType *ot, IDProperty *op_properties, const char *haptic_name, @@ -232,8 +230,7 @@ bool WM_xr_action_create(wmXrData *xr, wmXrAction *action = action_create(action_name, type, - count_subaction_paths, - subaction_paths, + user_paths, ot, op_properties, haptic_name, @@ -244,10 +241,20 @@ bool WM_xr_action_create(wmXrData *xr, action_flag, haptic_flag); + const unsigned int count = (unsigned int)BLI_listbase_count(user_paths); + unsigned int subaction_idx = 0; + + char **subaction_paths = MEM_calloc_arrayN( + count, sizeof(*subaction_paths), "XrAction_SubactionPathPointers"); + + LISTBASE_FOREACH_INDEX (XrUserPath *, user_path, user_paths, subaction_idx) { + subaction_paths[subaction_idx] = (char *)user_path->path; + } + GHOST_XrActionInfo info = { .name = action_name, - .count_subaction_paths = count_subaction_paths, - .subaction_paths = subaction_paths, + .count_subaction_paths = count, + .subaction_paths = (const char **)subaction_paths, .states = action->states, .float_thresholds = action->float_thresholds, .axis_flags = (int16_t *)action->axis_flags, @@ -273,11 +280,11 @@ bool WM_xr_action_create(wmXrData *xr, break; } - if (!GHOST_XrCreateActions(xr->runtime->context, action_set_name, 1, &info)) { - return false; - } + const bool success = GHOST_XrCreateActions(xr->runtime->context, action_set_name, 1, &info); - return true; + MEM_freeN(subaction_paths); + + return success; } void WM_xr_action_destroy(wmXrData *xr, const char *action_set_name, const char *action_name) @@ -323,19 +330,29 @@ bool WM_xr_action_binding_create(wmXrData *xr, const char *action_set_name, const char *action_name, const char *profile_path, - unsigned int count_subaction_paths, - const char **subaction_paths, - const char **component_paths, + const ListBase *user_paths, + const ListBase *component_paths, const float *float_thresholds, const eXrAxisFlag *axis_flags, const struct wmXrPose *poses) { + const unsigned int count = (unsigned int)BLI_listbase_count(user_paths); + BLI_assert(count == (unsigned int)BLI_listbase_count(component_paths)); + GHOST_XrActionBindingInfo *binding_infos = MEM_calloc_arrayN( - count_subaction_paths, sizeof(*binding_infos), __func__); + count, sizeof(*binding_infos), "XrActionBinding_Infos"); - for (unsigned int i = 0; i < count_subaction_paths; ++i) { + char **subaction_paths = MEM_calloc_arrayN( + count, sizeof(*subaction_paths), "XrActionBinding_SubactionPathPointers"); + + for (unsigned int i = 0; i < count; ++i) { GHOST_XrActionBindingInfo *binding_info = &binding_infos[i]; - binding_info->component_path = component_paths[i]; + const XrUserPath *user_path = BLI_findlink(user_paths, i); + const XrComponentPath *component_path = BLI_findlink(component_paths, i); + + subaction_paths[i] = (char *)user_path->path; + + binding_info->component_path = component_path->path; if (float_thresholds) { binding_info->float_threshold = float_thresholds[i]; } @@ -351,15 +368,18 @@ bool WM_xr_action_binding_create(wmXrData *xr, GHOST_XrActionProfileInfo profile_info = { .action_name = action_name, .profile_path = profile_path, - .count_subaction_paths = count_subaction_paths, - .subaction_paths = subaction_paths, + .count_subaction_paths = count, + .subaction_paths = (const char **)subaction_paths, .bindings = binding_infos, }; - bool ret = GHOST_XrCreateActionBindings(xr->runtime->context, action_set_name, 1, &profile_info); + const bool success = GHOST_XrCreateActionBindings( + xr->runtime->context, action_set_name, 1, &profile_info); + MEM_freeN(subaction_paths); MEM_freeN(binding_infos); - return ret; + + return success; } void WM_xr_action_binding_destroy(wmXrData *xr, diff --git a/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c b/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c index 0c356ab2b2e..8a1982fa8b5 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c @@ -103,6 +103,12 @@ static XrActionMapBinding *wm_xr_actionmap_binding_copy(XrActionMapBinding *amb_ XrActionMapBinding *amb_dst = MEM_dupallocN(amb_src); amb_dst->prev = amb_dst->next = NULL; + BLI_listbase_clear(&amb_dst->component_paths); + LISTBASE_FOREACH (XrComponentPath *, path, &amb_src->component_paths) { + XrComponentPath *path_new = MEM_dupallocN(path); + BLI_addtail(&amb_dst->component_paths, path_new); + } + return amb_dst; } @@ -118,11 +124,17 @@ XrActionMapBinding *WM_xr_actionmap_binding_add_copy(XrActionMapItem *ami, return amb_dst; } +static void wm_xr_actionmap_binding_clear(XrActionMapBinding *amb) +{ + BLI_freelistN(&amb->component_paths); +} + bool WM_xr_actionmap_binding_remove(XrActionMapItem *ami, XrActionMapBinding *amb) { int idx = BLI_findindex(&ami->bindings, amb); if (idx != -1) { + wm_xr_actionmap_binding_clear(amb); BLI_freelinkN(&ami->bindings, amb); if (idx <= ami->selbinding) { @@ -155,12 +167,6 @@ XrActionMapBinding *WM_xr_actionmap_binding_find(XrActionMapItem *ami, const cha * Item in an XR action map, that maps an XR event to an operator, pose, or haptic output. * \{ */ -static void wm_xr_actionmap_item_bindings_clear(XrActionMapItem *ami) -{ - BLI_freelistN(&ami->bindings); - ami->selbinding = 0; -} - static void wm_xr_actionmap_item_properties_set(XrActionMapItem *ami) { WM_operator_properties_alloc(&(ami->op_properties_ptr), &(ami->op_properties), ami->op); @@ -180,6 +186,19 @@ static void wm_xr_actionmap_item_properties_free(XrActionMapItem *ami) } } +static void wm_xr_actionmap_item_clear(XrActionMapItem *ami) +{ + LISTBASE_FOREACH (XrActionMapBinding *, amb, &ami->bindings) { + wm_xr_actionmap_binding_clear(amb); + } + BLI_freelistN(&ami->bindings); + ami->selbinding = 0; + + wm_xr_actionmap_item_properties_free(ami); + + BLI_freelistN(&ami->user_paths); +} + void WM_xr_actionmap_item_properties_update_ot(XrActionMapItem *ami) { switch (ami->type) { @@ -305,6 +324,12 @@ static XrActionMapItem *wm_xr_actionmap_item_copy(XrActionMapItem *ami_src) ami_dst->op_properties_ptr = NULL; } + BLI_listbase_clear(&ami_dst->user_paths); + LISTBASE_FOREACH (XrUserPath *, path, &ami_src->user_paths) { + XrUserPath *path_new = MEM_dupallocN(path); + BLI_addtail(&ami_dst->user_paths, path_new); + } + return ami_dst; } @@ -324,8 +349,7 @@ bool WM_xr_actionmap_item_remove(XrActionMap *actionmap, XrActionMapItem *ami) int idx = BLI_findindex(&actionmap->items, ami); if (idx != -1) { - wm_xr_actionmap_item_bindings_clear(ami); - wm_xr_actionmap_item_properties_free(ami); + wm_xr_actionmap_item_clear(ami); BLI_freelinkN(&actionmap->items, ami); if (idx <= actionmap->selitem) { @@ -480,12 +504,9 @@ XrActionMap *WM_xr_actionmap_find(wmXrRuntimeData *runtime, const char *name) void WM_xr_actionmap_clear(XrActionMap *actionmap) { LISTBASE_FOREACH (XrActionMapItem *, ami, &actionmap->items) { - wm_xr_actionmap_item_bindings_clear(ami); - wm_xr_actionmap_item_properties_free(ami); + wm_xr_actionmap_item_clear(ami); } - BLI_freelistN(&actionmap->items); - actionmap->selitem = 0; } @@ -494,9 +515,7 @@ 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 = 0; } diff --git a/source/blender/windowmanager/xr/intern/wm_xr_intern.h b/source/blender/windowmanager/xr/intern/wm_xr_intern.h index 0e7c4d18753..9480104150a 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_intern.h +++ b/source/blender/windowmanager/xr/intern/wm_xr_intern.h @@ -114,12 +114,8 @@ typedef struct 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]; + /** OpenXR user path identifier. */ + char subaction_path[64]; /* XR_MAX_USER_PATH_LENGTH */ /** Pose (in world space) that represents the user's hand when holding the controller. */ GHOST_XrPose grip_pose; diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c index 59b4eb00363..dfeaeae196c 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_session.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c @@ -1188,8 +1188,9 @@ void wm_xr_session_actions_update(wmWindowManager *wm) &state->viewer_pose, settings->base_scale * state->nav_scale, state->viewer_viewmat); } - int ret = GHOST_XrSyncActions(xr_context, active_action_set ? active_action_set->name : NULL); - if (!ret) { + const bool synced = GHOST_XrSyncActions(xr_context, + active_action_set ? active_action_set->name : NULL); + if (!synced) { return; } |